00001 #include "epoc-btprox.hpp" 00002 00003 #if __BTPROX_ENABLED__ 00004 00005 #include "cf_query.h" 00006 #include "er_errors.h" 00007 #include "kr_controller_private.h" 00008 #include "ld_logging.h" 00009 #include "utils_cl2.h" 00010 00011 #include "common/epoc-time.h" 00012 00013 #include <stdlib.h> 00014 #include <string.h> 00015 00016 // might be related -- http://code.google.com/p/bt-proximity/ 00017 00018 #define PRINT_ELEMENTS (__DO_LOGGING__ && 0) 00019 00020 // ------------------------------------------------------------------- 00021 00022 #if PRINT_ELEMENTS 00023 static void PrintElement(gpointer data, gpointer user_data) 00024 { 00025 btprox_item* item = (btprox_item*)data; 00026 logg("element '%s' '%s'", item->address, item->name); 00027 } 00028 00029 static void PrintElements(GPtrArray* array) 00030 { 00031 g_ptr_array_foreach(array, &PrintElement, NULL); 00032 } 00033 #endif 00034 00035 static void FreeElement(gpointer data, gpointer user_data) 00036 { 00037 btprox_item* item = (btprox_item*)data; 00038 g_free(item->address); 00039 g_free(item->name); 00040 g_free(item); 00041 } 00042 00043 // Frees all elements, but does not modify the pointer array. 00044 static void FreeElements(GPtrArray* array) 00045 { 00046 g_ptr_array_foreach(array, &FreeElement, NULL); 00047 } 00048 00049 static void FreeResult(GPtrArray* array) 00050 { 00051 if (array) { 00052 FreeElements(array); 00053 g_ptr_array_free(array, TRUE); 00054 } 00055 } 00056 00057 static void ClearResult(GPtrArray* array) 00058 { 00059 FreeElements(array); 00060 g_ptr_array_set_size(array, 0); 00061 } 00062 00063 static gint CmpString(const char* a, const char* b) 00064 { 00065 if (!a && !b) return 0; 00066 if (a && !b) return 1; 00067 if (!a && b) return -1; 00068 return strcmp(a, b); 00069 } 00070 00071 static gint CmpElement(gconstpointer a, gconstpointer b) 00072 { 00073 btprox_item* ai = (btprox_item*)a; 00074 btprox_item* bi = (btprox_item*)b; 00075 return (CmpString(ai->address, bi->address) || 00076 CmpString(ai->name, bi->name)); 00077 } 00078 00079 static gint cmpInt(int a, int b) 00080 { 00081 if (a == b) 00082 return 0; 00083 if (a < b) 00084 return -1; 00085 return 1; 00086 } 00087 00088 // A good generic utility candidate. Assumes sorted arrays. 00089 static gint g_ptr_array_cmp(GPtrArray* array0, GPtrArray* array1, GCompareFunc compareFunc) 00090 { 00091 int i; 00092 int len = MIN(array0->len, array1->len); 00093 for (i=0; i<len; i++) { 00094 int res = (*compareFunc)(array0->pdata + i, array1->pdata + i); 00095 if (!res) return res; 00096 } 00097 return cmpInt(array0->len, array1->len); 00098 } 00099 00100 static void SortResult(GPtrArray* array) 00101 { 00102 g_ptr_array_sort(array, &CmpElement); 00103 } 00104 00105 static gint CmpResults(GPtrArray* array0, GPtrArray* array1) 00106 { 00107 return g_ptr_array_cmp(array0, array1, &CmpElement); 00108 } 00109 00110 // ------------------------------------------------------------------- 00111 00112 CSensor_btprox* CSensor_btprox::NewL(LogDb* aLogDb) 00113 { 00114 CSensor_btprox* obj = new (ELeave) CSensor_btprox(aLogDb); 00115 CleanupStack::PushL(obj); 00116 obj->ConstructL(); 00117 CleanupStack::Pop(); 00118 return obj; 00119 } 00120 00121 CSensor_btprox::~CSensor_btprox() 00122 { 00123 Cancel(); 00124 00125 BtClose(); 00126 SESSION_CLOSE_IF_OPEN(iSocketServ); 00127 SESSION_CLOSE_IF_OPEN(iTimer); 00128 00129 FreeResult(iResult); 00130 FreeResult(iOldResult); 00131 } 00132 00133 void CSensor_btprox::BtClose() 00134 { 00135 SESSION_CLOSE_IF_OPEN(iHostResolver); 00136 } 00137 00138 gboolean CSensor_btprox::StartL(GError** error) 00139 { 00140 iNumScanFailures = 0; 00141 if (iState == EInactive) { 00142 TryStartScanning(); 00143 log_db_log_status(iLogDb, NULL, "btprox sensor started"); 00144 } 00145 return TRUE; 00146 } 00147 00148 void CSensor_btprox::TryStartScanning() 00149 { 00150 if (!EnsureBtInit()) { 00151 // It might be more optimal to observe BT on/off events, and 00152 // adjust scanning attempts accordingly; not sure if the plugin 00153 // API supports this. 00154 iNumScanFailures++; 00155 iState = ERetryWaiting; 00156 } else { 00157 iState = EScanWaiting; 00158 } 00159 SetTimer(); 00160 } 00161 00162 void CSensor_btprox::Stop() 00163 { 00164 if (iState != EInactive) { 00165 Cancel(); 00166 log_db_log_status(iLogDb, NULL, "btprox sensor stopped"); 00167 } 00168 } 00169 00170 CSensor_btprox::CSensor_btprox(LogDb* aLogDb) : 00171 CActiveRunG(EPriorityStandard) 00172 { 00173 iLogDb = aLogDb; 00174 CActiveScheduler::Add(this); 00175 } 00176 00177 void CSensor_btprox::ConstructL() 00178 { 00179 RefreshBaseScanIntervalSecs(); 00180 00181 SET_TRAP_OOM(User::LeaveNoMemory()); 00182 iResult = g_ptr_array_sized_new(15); 00183 iOldResult = g_ptr_array_sized_new(15); 00184 UNSET_TRAP_OOM(); 00185 00186 LEAVE_IF_ERROR_OR_SET_SESSION_OPEN(iTimer, iTimer.CreateLocal()); 00187 00188 LEAVE_IF_ERROR_OR_SET_SESSION_OPEN(iSocketServ, iSocketServ.Connect()); 00189 } 00190 00191 /* Returns true iff BT resolver is ready for use. 00192 Produces an error otherwise. */ 00193 TBool CSensor_btprox::EnsureBtInit() 00194 { 00195 if (IS_SESSION_OPEN(iHostResolver)) { 00196 return TRUE; 00197 } 00198 00199 TRAPD(errCode, TryBtInitL()); 00200 if (errCode == KErrNone) { 00201 return TRUE; 00202 } 00203 00204 dblogg("BT init failed in btprox scanner: %s (%d)", plat_error_strerror(errCode), errCode); 00205 return FALSE; 00206 } 00207 00208 /* This can be expected to fail whenever Bluetooth is turned off. */ 00209 void CSensor_btprox::TryBtInitL() 00210 { 00211 assert(!IS_SESSION_OPEN(iHostResolver) && "BT session already open"); 00212 00213 TProtocolName protocolName; 00214 _LIT(KBtLinkManager, "BTLinkManager"); 00215 protocolName.Copy(KBtLinkManager); 00216 TProtocolDesc protocolDesc; 00217 User::LeaveIfError(iSocketServ.FindProtocol(protocolName, protocolDesc)); 00218 00219 LEAVE_IF_ERROR_OR_SET_SESSION_OPEN(iHostResolver, iHostResolver.Open(iSocketServ, protocolDesc.iAddrFamily, protocolDesc.iProtocol)); 00220 } 00221 00222 void CSensor_btprox::Reconfigure(const gchar* name, const gchar* value) 00223 { 00224 if (strcmp(name, "sensor.btprox.interval") == 0) 00225 RefreshBaseScanIntervalSecs(); 00226 } 00227 00228 #define SCAN_INTERVAL_SECS (10 * 60) // 10 minutes 00229 00230 // This only takes effect the next time the timer is set. 00231 void CSensor_btprox::RefreshBaseScanIntervalSecs() 00232 { 00233 iBaseScanIntervalSecs = SCAN_INTERVAL_SECS; 00234 try_get_ConfigDb_int("sensor.btprox.interval", 00235 &iBaseScanIntervalSecs, 00236 NULL, NULL); 00237 dblogg("btprox scan interval set to %d secs", iBaseScanIntervalSecs); 00238 } 00239 00240 void CSensor_btprox::SetTimer() 00241 { 00242 assert(iState == EScanWaiting || iState == ERetryWaiting); 00243 int secs = iBaseScanIntervalSecs * (1 + iNumScanFailures) + (rand() % 10); 00244 TTimeIntervalMicroSeconds32 interval = SecsToUsecs(secs); 00245 //logg("btprox timer set to %d secs / %d usecs", secs, interval.Int()); 00246 iTimer.After(iStatus, interval); 00247 SetActive(); 00248 } 00249 00250 #define SWAP(t,x,y) { t _tmp = x; x = y; y = _tmp; } 00251 00252 void CSensor_btprox::BtDiscover() 00253 { 00254 guilogf("btprox: scanning..."); 00255 00256 SWAP(GPtrArray*, iResult, iOldResult); 00257 ClearResult(iResult); 00258 00259 iInquirySockAddr.SetIAC(KGIAC); 00260 iInquirySockAddr.SetAction(KHostResInquiry|KHostResName); 00261 // We should also specify KHostResIgnoreCache if we did not 00262 // want cache BT friendly names, but in most cases we do not 00263 // require genuine name discovery, and device discovery is 00264 // slow enough as it is. 00265 iHostResolver.GetByAddress(iInquirySockAddr, iNameEntry, iStatus); 00266 SetActive(); 00267 iState = EDiscovering; 00268 } 00269 00270 void CSensor_btprox::BtNext() 00271 { 00272 iHostResolver.Next(iNameEntry, iStatus); 00273 SetActive(); 00274 iState = EDiscovering; 00275 } 00276 00277 static void BtDevAddrToString(TDes8& aString, const TBTDevAddr& addr) 00278 { 00279 // GetReadable() does not produce a "standard" result, 00280 // so have to construct a string manually. 00281 aString.Zero(); 00282 _LIT8(KColon, ":"); 00283 for (TInt i=0; i<6; i++) { 00284 const TUint8& val = addr[i]; 00285 aString.AppendNumFixedWidthUC(val, EHex, 2); 00286 if (i < 5) 00287 aString.Append(KColon); 00288 } 00289 } 00290 00291 gboolean CSensor_btprox::HandleScanEventL(TInt errCode, GError** error) 00292 { 00293 if (errCode == KErrEof) // no more devices 00294 { 00295 guilogf("btprox: scan complete"); 00296 iNumScanFailures = 0; 00297 assert(iResult); 00298 SortResult(iResult); 00299 assert(iOldResult); 00300 if (CmpResults(iResult, iOldResult)) 00301 { // Log result. 00302 #if PRINT_ELEMENTS 00303 PrintElements(iResult); 00304 #endif 00305 if (!log_db_log_btprox(iLogDb, iResult, error)) 00306 { 00307 return FALSE; 00308 } 00309 } 00310 else 00311 { 00312 // There was some discussion as to whether something should be 00313 // logged even when there was no change, but is that really 00314 // necessary here; if there was no change, then the previous 00315 // result still stands. Is it necessary to know the time 00316 // of the attempt to scan a different set? 00317 00318 guilogf("btprox: device set unchanged"); 00319 } 00320 iState = EScanWaiting; 00321 SetTimer(); // wait before scanning for more 00322 } 00323 else if (errCode) // some error 00324 { 00325 guilogf("btprox: scan failed"); 00326 iNumScanFailures++; 00327 dblogg("%dth consecutive failure in btprox: %s (%d)", 00328 iNumScanFailures, plat_error_strerror(errCode), errCode); 00329 iState = ERetryWaiting; 00330 SetTimer(); 00331 } 00332 else // no error 00333 { 00334 { // Add to result. 00335 TSockAddr& sockAddr = iNameEntry().iAddr; 00336 TBTDevAddr btDevAddr = static_cast<TBTSockAddr>(sockAddr).BTAddr(); 00337 /* 00338 TBuf<32> addrBuf; 00339 btDevAddr.GetReadable(addrBuf); // has no colons 00340 */ 00341 TBuf8<6*2+5+1> addrBuf8; 00342 BtDevAddrToString(addrBuf8, btDevAddr); 00343 00344 THostName& hostName = iNameEntry().iName; 00345 00346 btprox_item* item = g_try_new0(btprox_item, 1); 00347 User::LeaveIfNull(item); 00348 #define free_item_action FreeElement(item, NULL); User::Leave(KErrNoMemory); 00349 item->name = ConvToUtf8CString(hostName); 00350 if (!item->name) { free_item_action; } 00351 SET_TRAP_OOM(free_item_action); 00352 item->address = g_strdup((gchar*)(addrBuf8.PtrZ())); 00353 g_ptr_array_add(iResult, item); 00354 UNSET_TRAP_OOM(); 00355 guilogf("btprox: device '%s' '%s'", item->address, item->name); 00356 } 00357 BtNext(); 00358 } 00359 00360 return TRUE; 00361 } 00362 00363 gboolean CSensor_btprox::RunGL(GError** error) 00364 { 00365 assert_error_unset(error); 00366 00367 TInt errCode = iStatus.Int(); 00368 TState oldState = iState; 00369 iState = EInactive; 00370 00371 switch (oldState) 00372 { 00373 case EScanWaiting: 00374 case ERetryWaiting: 00375 { 00376 if (errCode) { 00377 // The timer expired with an error. This is rather strange 00378 // with interval timers. 00379 if (error) { 00380 // If gx_error_new fails, *error will be set to NULL, which 00381 // we interpret as an out-of-memory error. This is can 00382 // only happen with the Symbian port, as normally there 00383 // will be an automatic abort(). 00384 *error = gx_error_new(domain_cl2app, code_timer, 00385 "timer failure in btprox sensor: %s (%d)", 00386 plat_error_strerror(errCode), errCode); 00387 } 00388 return FALSE; 00389 } 00390 00391 if (oldState == EScanWaiting) { 00392 BtDiscover(); 00393 } else { 00394 TryStartScanning(); 00395 } 00396 00397 break; 00398 } 00399 case EDiscovering: 00400 { 00401 if (!HandleScanEventL(errCode, error)) 00402 return FALSE; 00403 break; 00404 } 00405 default: 00406 { 00407 assert(0 && "unexpected state in btprox"); 00408 break; 00409 } 00410 } 00411 00412 return TRUE; 00413 } 00414 00415 void CSensor_btprox::DoCancel() 00416 { 00417 switch (iState) 00418 { 00419 case EScanWaiting: 00420 case ERetryWaiting: 00421 { 00422 iTimer.Cancel(); 00423 break; 00424 } 00425 case EDiscovering: 00426 { 00427 iHostResolver.Cancel(); 00428 break; 00429 } 00430 default: 00431 { 00432 assert(0 && "unexpected state in btprox"); 00433 } 00434 } 00435 00436 // Note that the state must never become anything else without 00437 // invoking SetActive at the same time. 00438 iState = EInactive; 00439 } 00440 00441 const char* CSensor_btprox::Description() 00442 { 00443 return "btprox"; 00444 } 00445 00446 #endif // __BTPROX_ENABLED__ 00447 00448 /** 00449 00450 epoc-btprox.cpp 00451 00452 Copyright 2009 Helsinki Institute for Information Technology (HIIT) 00453 and the authors. All rights reserved. 00454 00455 Authors: Tero Hasu <tero.hasu@hut.fi> 00456 00457 Permission is hereby granted, free of charge, to any person 00458 obtaining a copy of this software and associated documentation files 00459 (the "Software"), to deal in the Software without restriction, 00460 including without limitation the rights to use, copy, modify, merge, 00461 publish, distribute, sublicense, and/or sell copies of the Software, 00462 and to permit persons to whom the Software is furnished to do so, 00463 subject to the following conditions: 00464 00465 The above copyright notice and this permission notice shall be 00466 included in all copies or substantial portions of the Software. 00467 00468 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 00469 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00470 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 00471 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 00472 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 00473 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 00474 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 00475 SOFTWARE. 00476 00477 **/
ContextLogger2—ContextLogger2 Logger Daemon Internals—Generated on Mon May 2 13:49:52 2011 by Doxygen 1.6.1