epoc-callstatus.cpp

Go to the documentation of this file.
00001 /*
00002  !concept {:name => "Observing call status events",
00003    :desc => "Detecting phone call status changes and querying for additional information about any call."}
00004 */
00005 
00006 #include "epoc-callstatus.hpp"
00007 
00008 #if __CALLSTATUS_ENABLED__
00009 
00010 #include "er_errors.h"
00011 #include "ld_logging.h"
00012 #include "sa_sensor_list_log_db.h"
00013 #include "ut_telno_epoc.hpp"
00014 #include "utils_cl2.h"
00015 
00016 #include "common/assertions.h"
00017 #include "common/epoc-time.h"
00018 #include "common/error_list.h"
00019 #include "common/logging.h"
00020 #include "common/platform_error.h"
00021 #include "common/utilities.h"
00022 
00023 #include <stdlib.h> // rand
00024 
00025 #include <glib/gprintf.h>
00026 
00027 // http://library.forum.nokia.com/topic/S60_3rd_Edition_Cpp_Developers_Library/GUID-35228542-8C95-4849-A73F-2B4F082F0C44/html/SDL_93/doc_source/guide/Telephony-subsystem-guide/ThirdPartyTelephony/info_calls.html
00028 
00029 // xxx We might also want to log the remote party phone number when
00030 // the remote party information changes. This would involve a separate
00031 // active object for requesting notification of remote party info
00032 // change (with a separate ``NotifyChange`` request).
00033 
00034 // TCallInfoV1::iExitCode is not documented in API docs, but see http://wiki.forum.nokia.com/index.php/TSS001320_-_Interpreting_TCallInfoV1::iExitCode_value -- supposedly "gives the reason for the termination of a finished call"
00035 
00036 // -------------------------------------------------------------------
00037 // utilities...
00038 
00039 static gboolean TFlightModeV1ToBoolean(const CTelephony::TFlightModeV1& data)
00040 {
00041   if (data.iFlightModeStatus == CTelephony::EFlightModeOn)
00042     return ETrue;
00043   else if (data.iFlightModeStatus == CTelephony::EFlightModeOff)
00044     return EFalse;
00045   else
00046     assert(0 && "unexpected TFlightModeStatus value");
00047   return EFalse;
00048 }
00049 
00050 // ``GetCallInfo`` should be used after a notification of an incoming
00051 // call (``EStatusRinging``?), notification of line status change to
00052 // dialling (``EStatusDialling``), or notification of remote party
00053 // info change (with a separate ``NotifyChange`` request).
00054 
00055 static TInt DoGetCallInfo(CTelephony& aTelephony,
00056         CTelephony::TCallInfoV1& callInfo,
00057         CTelephony::TRemotePartyInfoV1& remoteInfo)
00058 {
00059   CTelephony::TCallSelectionV1 callSelect;
00060   CTelephony::TCallSelectionV1Pckg callSelectPckg(callSelect);
00061   callSelect.iLine = CTelephony::EVoiceLine;
00062   // Call during setup/disconnecting process, not active.
00063   callSelect.iSelect = CTelephony::EInProgressCall;
00064 
00065   CTelephony::TCallInfoV1Pckg callInfoPckg(callInfo);
00066   CTelephony::TRemotePartyInfoV1Pckg remoteInfoPckg(remoteInfo);
00067 
00068   // "If a call with the appropriate status is not available, then
00069   // KErrNotFound is returned."
00070   TInt errCode = aTelephony.GetCallInfo(callSelectPckg, callInfoPckg, remoteInfoPckg);
00071   return errCode;
00072 }
00073 
00074 // -------------------------------------------------------------------
00075 // the sensor object implementation...
00076 
00077 CTOR_IMPL_CSensor_callstatus;
00078 
00079 void CSensor_callstatus::ConstructL()
00080 {
00081   iTelephony = CTelephony::NewL();
00082   iRetryAo = CRetryAo::NewL(*this, 100, 10);
00083   iFlightModeGetter = new (ELeave) CGetterAo_FlightMode(*iTelephony, *this);
00084   iFlightModeNotifier = new (ELeave) CNotifyAo_FlightMode(*iTelephony, *this);
00085   iCallStatusNotifier = new (ELeave) CNotifyAo_CallStatus(*iTelephony, *this);
00086 }
00087 
00088 CSensor_callstatus::~CSensor_callstatus()
00089 {
00090   delete iRetryAo;
00091   delete iFlightModeGetter;
00092   delete iFlightModeNotifier;
00093   delete iCallStatusNotifier;
00094   delete iTelephony;
00095 }
00096 
00097 void CSensor_callstatus::Cancel()
00098 {
00099   iFlightModeGetter->Cancel();
00100   iFlightModeNotifier->Cancel();
00101   iCallStatusNotifier->Cancel();
00102   iRetryAo->Cancel();
00103   iState = EInactive;
00104 }
00105 
00106 gboolean CSensor_callstatus::StartL(GError** error)
00107 {
00108   if (!IsActive()) {
00109     iRetryAo->ResetFailures();
00110     iFlightModeGetter->MakeRequest();
00111     iState = EQueryingFlightMode;
00112     //log_db_log_status(GetLogDb(), NULL, "callstatus sensor started");
00113   }
00114   return TRUE;
00115 }
00116 
00117 void CSensor_callstatus::Stop()
00118 {
00119   if ((IsActive())) {
00120     Cancel();
00121     //log_db_log_status(GetLogDb(), NULL, "callstatus sensor stopped");
00122   }
00123 }
00124 
00125 // We expect success here.
00126 void CSensor_callstatus::GotData_FlightMode(TInt errCode)
00127 {
00128   if (errCode) {
00129     log_db_log_status(GetLogDb(), NULL,
00130           "INACTIVATE: callstatus: failure getting flight mode: %s (%d)", 
00131           plat_error_strerror(errCode), errCode);
00132     Cancel();
00133   } else {
00134     gboolean value = TFlightModeV1ToBoolean(iFlightModeGetter->Data());
00135     logg("current flight mode setting: %d", value);
00136     if (value) {
00137       iState = EInFlightMode;
00138     } else {
00139       iCallStatusNotifier->MakeRequest();
00140       iState = EQueryingCallStatus;
00141     }
00142     iFlightModeNotifier->MakeRequest();
00143   }
00144 }
00145 
00146 // We expect success here.
00147 void CSensor_callstatus::ChangedData_FlightMode(TInt errCode)
00148 {
00149   if (errCode) {
00150     log_db_log_status(GetLogDb(), NULL,
00151           "INACTIVATE: callstatus: flight mode notification error: %s (%d)", 
00152           plat_error_strerror(errCode), errCode);
00153     Cancel();
00154   } else {
00155     gboolean value = TFlightModeV1ToBoolean(iFlightModeNotifier->Data());
00156 
00157     if (value) {
00158       Cancel();
00159       iState = EInFlightMode;
00160     } else {
00161       iRetryAo->Cancel(); // to make sure
00162       iCallStatusNotifier->MakeRequest(); // harmless if active
00163       iState = EQueryingCallStatus;
00164     }
00165     iFlightModeNotifier->MakeRequest();
00166   }
00167 }
00168 
00169 void CSensor_callstatus::ChangedData_CallStatus(TInt errCode)
00170 {
00171   if (errCode) {
00172     log_db_log_status(GetLogDb(), NULL,
00173           "NOTICE: call status notification error in callstatus sensor: %s (%d)", 
00174           plat_error_strerror(errCode), errCode);
00175     if (iRetryAo->Retry()) {
00176       iState = ERetryWaiting;
00177     } else {
00178       log_db_log_status(GetLogDb(), NULL,
00179       "INACTIVATE: callstatus: max num of retries");
00180       Cancel();
00181     }
00182   } else {
00183     iRetryAo->ResetFailures();
00184 
00185     // Here iStatus is of the enum type TCallStatus which has at
00186     // least 10 or so different possible values.
00187     CTelephony::TCallStatus callStatus = iCallStatusNotifier->Data().iStatus;
00188 
00189     // Set to remote party phone number if possible. Will be left as
00190     // NULL otherwise.
00191     const char* number = NULL;
00192 
00193     // Any contact name as a string. May be left as NULL. Must be
00194     // freed if non-NULL.
00195     gchar* contactName = NULL;
00196 
00197     // Call start time. May be left as zero, which means no start time.
00198     time_t startTime = 0;
00199 
00200     // Call termination reason codes. May be left as 1, which means no
00201     // reason.
00202     TInt etelCode = 1;
00203     TInt netCode = 1;
00204 
00205     CTelephony::TCallInfoV1 callInfo;
00206     CTelephony::TRemotePartyInfoV1 remoteInfo;
00207     errCode = DoGetCallInfo(*iTelephony, callInfo, remoteInfo);
00208 
00209     if (errCode) {
00210       // Certainly this is true for idle line (1), and probably for
00211       // unknown status (0) as well.
00212       logg("failed to get call info for status %d: %s (%d)", 
00213      callStatus, plat_error_strerror(errCode), errCode);
00214     } else {
00215       logg("got call info for status %d", callStatus);
00216     }
00217     TBool gotCallInfo = (errCode == 0);
00218 
00219     if (gotCallInfo) {
00220       // We would like to use call start time as the "call ID", as it
00221       // ought to be unique enough, but it seems to always be 0.
00222       {
00223   const TDateTime& dt = callInfo.iStartTime;
00224   TTime epocStartTime(dt);
00225   if (epocStartTime.Int64() != 0) {
00226     logg("call start datetime is %d.%d.%d %d:%d:%d.%d",
00227          dt.Year(), dt.Month(), dt.Day(),
00228          dt.Hour(), dt.Minute(), dt.Second(), dt.MicroSecond());
00229     startTime = LocalEpocTimeToUnixTime(epocStartTime);
00230     logg("call start time is %d", startTime);
00231     logg("time now is %d", time(NULL));
00232   }
00233       }
00234 
00235 #define SET_NUMBER_AND_CONTACT { \
00236   if (numberW.Length() > 0) { \
00237     numberBuf.Copy(numberW); \
00238     number = (const char*)(numberBuf.PtrZ()); \
00239     logg("call remote party number is '%s'", number); \
00240     contactName = GetContactNameByPhoneNo(numberW); \
00241     if (contactName) { \
00242       logg("call remote party name is '%s'", contactName); \
00243     } else { \
00244       logt("could not get call remote party name"); \
00245     } \
00246   } else { \
00247     logt("could not get call remote party phone number"); \
00248   } \
00249       }
00250 
00251       // For some cases we can get some additional useful information.
00252       switch (callStatus)
00253   {
00254     // May be able to get remote party phone number in these cases.
00255   case CTelephony::EStatusRinging: // use TRemotePartyInfoV1
00256   case CTelephony::EStatusDialling: // use TCallInfoV1
00257     {
00258       TBuf8<CTelephony::KMaxTelNumberSize + 1> numberBuf;
00259 
00260       if (callStatus == CTelephony::EStatusRinging) {
00261         switch (remoteInfo.iRemoteIdStatus)
00262     {
00263     case CTelephony::ERemoteIdentityUnknown:
00264       {
00265         number = "(unknown)";
00266         break;
00267       }
00268     case CTelephony::ERemoteIdentitySuppressed:
00269       {
00270         number = "(suppressed)";
00271         break;
00272       }
00273     case CTelephony::ERemoteIdentityAvailable:
00274       {
00275         TDesC& numberW = remoteInfo.iRemoteNumber.iTelNumber; // TBuf<KMaxTelNumberSize>
00276         SET_NUMBER_AND_CONTACT;
00277         break;
00278       }
00279     default:
00280       {
00281         logt("unknown remoteInfo.iRemoteIdStatus");
00282         break;
00283       }
00284     }
00285       } else { // callStatus == CTelephony::EStatusDialling
00286         TDesC& numberW = callInfo.iDialledParty.iTelNumber; // TBuf<KMaxTelNumberSize>
00287         SET_NUMBER_AND_CONTACT;
00288       }
00289       break;
00290     }
00291     
00292     // May be able to get disconnect reason in this case. The
00293     // information is supplied by the operator, though, which
00294     // means we depend on what they choose to tell us.
00295   case CTelephony::EStatusDisconnecting:
00296     {
00297       // KErrNone if the call ended normally.
00298       TInt& exitCode = callInfo.iExitCode;
00299 #if 1
00300       {
00301               // We want to interpret the two words as negative
00302               // values, as that is how error values generally are in
00303               // Symbian. Note that a right shift is well defined only
00304               // for unsigned values.
00305         TInt16 lower = ((TUint32)exitCode) & 0xffffu;
00306         TInt16 upper = ((TUint32)exitCode) >> 16;
00307         etelCode = lower;
00308         netCode = upper;
00309       }
00310 #else
00311             // xxx Only works right for negative values, for zero this
00312             // gives -65536.
00313       etelCode = (exitCode | 0xFFFF0000);
00314             // xxx Dangerous. Compiler-specific behavior.
00315       netCode = (exitCode >> 16);
00316 #endif
00317       logg("disconnect reason: code=%d, etel=%d, net=%d", exitCode, etelCode, netCode);
00318       /*
00319       char extraBuf[50];
00320       g_sprintf(extraBuf, "os=%d/net=%d", etelCode, netCode);
00321       */
00322       break;
00323     }
00324     
00325   default:
00326     {
00327       // No additional information to record in this state.
00328     }
00329   } // end switch
00330     }
00331       
00332     // Log.
00333     {
00334       GError* localError = NULL;
00335       if (!log_db_log_callstatus(GetLogDb(), callStatus, number, contactName, startTime, etelCode, netCode, &localError)) {
00336   gx_txtlog_fatal_error_free(localError);
00337   goto cleanup;
00338       }
00339       guilogf("callstatus: %d num='%s' name='%s' os=%d net=%d",
00340         callStatus,
00341         number ? number : "(N/A)",
00342         contactName ? contactName : "(N/A)",
00343         etelCode, netCode);
00344     }
00345 
00346     iCallStatusNotifier->MakeRequest();
00347     iState = EQueryingCallStatus;
00348 
00349   cleanup:
00350     g_free(contactName);
00351   }
00352 }
00353 
00354 // We expect success here.
00355 void CSensor_callstatus::RetryTimerExpired(CRetryAo* src, TInt errCode)
00356 {
00357   (void)src;
00358   if (errCode) {
00359     log_db_log_status(GetLogDb(), NULL,
00360           "INACTIVATE: callstatus: timer error: %s (%d)", 
00361           plat_error_strerror(errCode), errCode);
00362     Cancel();
00363   } else {
00364     iCallStatusNotifier->MakeRequest();
00365     iState = EQueryingCallStatus;
00366   }
00367 }
00368 
00369 #endif // __CALLSTATUS_ENABLED__
00370 
00371 /**
00372 
00373 epoc-callstatus.cpp
00374 
00375 Copyright 2009 Helsinki Institute for Information Technology (HIIT)
00376 and the authors. All rights reserved.
00377 
00378 Authors: Tero Hasu <tero.hasu@hut.fi>
00379 
00380 Permission is hereby granted, free of charge, to any person
00381 obtaining a copy of this software and associated documentation files
00382 (the "Software"), to deal in the Software without restriction,
00383 including without limitation the rights to use, copy, modify, merge,
00384 publish, distribute, sublicense, and/or sell copies of the Software,
00385 and to permit persons to whom the Software is furnished to do so,
00386 subject to the following conditions:
00387 
00388 The above copyright notice and this permission notice shall be
00389 included in all copies or substantial portions of the Software.
00390 
00391 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00392 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00393 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00394 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
00395 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
00396 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00397 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00398 SOFTWARE.
00399 
00400  **/

ContextLogger2—ContextLogger2 Logger Daemon Internals—Generated on Mon May 2 13:49:52 2011 by Doxygen 1.6.1