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