epoc-btprox.cpp

Go to the documentation of this file.
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