Gears Android WIFI/基站定位源代码分析

来源:互联网 发布:高仪和科勒哪个好 知乎 编辑:程序博客网 时间:2024/05/16 17:09
 
文章出处:http://www.limodev.cn/blog
作者联系方式:李先静 <xianjimli at hotmail dot com>

Broncho A1还不支持基站和WIFI定位,Android的老版本里是有NetworkLocationProvider的,它实现了基站和WIFI定位,但从 android 1.5之后就被移除了。本来想在broncho A1里自己实现NetworkLocationProvider的,但一直没有时间去研究。我知道 gears(http://code.google.com/p/gears/)是有提供类似的功能,昨天研究了一下Gears的代码,看能不能移植到 android中来。
1.下载源代码
[url]svn checkout http://gears.googlecode.com/svn/trunk/ gears-read-only[/url]

定位相关的源代码在gears/geolocation目录中。

2.关注android平台中的基站位置变化。

JAVA类AndroidRadioDataProvider是 PhoneStateListener的子类,用来监听Android电话的状态变化。当服务状态、信号强度和基站变化时,就会用下面代码获取小区信息:
Java代码 复制代码 收藏代码
  1. RadioData radioData = new RadioData();   
  2.       GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation;   
  3.     
  4.       // Extract the cell id, LAC, and signal strength.  
  5.       radioData.cellId = gsmCellLocation.getCid();   
  6.       radioData.locationAreaCode = gsmCellLocation.getLac();   
  7.       radioData.signalStrength = signalStrength;   
  8.     
  9.       // Extract the home MCC and home MNC.  
  10.       String operator = telephonyManager.getSimOperator();   
  11.       radioData.setMobileCodes(operator, true);   
  12.     
  13.       if (serviceState != null) {   
  14.         // Extract the carrier name.   
  15.         radioData.carrierName = serviceState.getOperatorAlphaLong();   
  16.     
  17.         // Extract the MCC and MNC.   
  18.         operator = serviceState.getOperatorNumeric();   
  19.         radioData.setMobileCodes(operator, false);   
  20.       }   
  21.     
  22.       // Finally get the radio type.   
  23.       int type = telephonyManager.getNetworkType();   
  24.       if (type == TelephonyManager.NETWORK_TYPE_UMTS) {   
  25.         radioData.radioType = RADIO_TYPE_WCDMA;   
  26.       } else if (type == TelephonyManager.NETWORK_TYPE_GPRS   
  27.                  || type == TelephonyManager.NETWORK_TYPE_EDGE) {   
  28.         radioData.radioType = RADIO_TYPE_GSM;   
  29.       }  


然后调用用C代码实现的onUpdateAvailable函数。

2.Native函数onUpdateAvailable是在 radio_data_provider_android.cc里实现的。

声明Native函数
Java代码 复制代码 收藏代码
  1. JNINativeMethod AndroidRadioDataProvider::native_methods_[] = {   
  2.   {"onUpdateAvailable",   
  3.    "(L" GEARS_JAVA_PACKAGE "/AndroidRadioDataProvider$RadioData;J)V",   
  4.    reinterpret_cast<void*>(AndroidRadioDataProvider::OnUpdateAvailable)   
  5.   },   
  6. };  


JNI调用好像只能调用静态成员函数,把对象本身用一个参数传进来,然后再调用对象的成员函数。
Java代码 复制代码 收藏代码
  1. void AndroidRadioDataProvider::OnUpdateAvailable(JNIEnv* env,   
  2.                                                  jclass cls,   
  3.                                                  jobject radio_data,   
  4.                                                  jlong self) {   
  5.   assert(radio_data);   
  6.   assert(self);   
  7.   AndroidRadioDataProvider *self_ptr =   
  8.       reinterpret_cast<AndroidRadioDataProvider*>(self);   
  9.   RadioData new_radio_data;   
  10.   if (InitFromJavaRadioData(env, radio_data, &new_radio_data)) {   
  11.     self_ptr->NewRadioDataAvailable(&new_radio_data);   
  12.   }   
  13. }  


先判断基站信息有没有变化,如果有变化则通知相关的监听者。
Java代码 复制代码 收藏代码
  1. void AndroidRadioDataProvider::NewRadioDataAvailable(   
  2.     RadioData* new_radio_data) {   
  3.   bool is_update_available = false;   
  4.   data_mutex_.Lock();   
  5.   if (new_radio_data && !radio_data_.Matches(*new_radio_data)) {   
  6.     radio_data_ = *new_radio_data;   
  7.     is_update_available = true;   
  8.   }   
  9.   // Avoid holding the mutex locked while notifying observers.  
  10.   data_mutex_.Unlock();   
  11.     
  12.   if (is_update_available) {   
  13.     NotifyListeners();   
  14.   }   
  15. }  


接下来的过程,基站定位和WIFI定位是一样的,后面我们再来介绍。下面我们先看 WIFI定位。

3.关注android平台中的WIFI变化。

JAVA类AndroidWifiDataProvider扩展了 BroadcastReceiver类,它关注WIFI扫描结果:
Java代码 复制代码 收藏代码
  1. IntentFilter filter = new IntentFilter();   
  2.     filter.addAction(mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION);   
  3.     mContext.registerReceiver(this, filter, null, handler);  


当收到WIFI扫描结果后,调用Native函数 onUpdateAvailable,并把WIFI的扫描结果传递过去。
Java代码 复制代码 收藏代码
  1. public void onReceive(Context context, Intent intent) {   
  2.    if (intent.getAction().equals(   
  3.            mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {   
  4.      if (Config.LOGV) {   
  5.        Log.v(TAG, "Wifi scan resulst available");   
  6.      }   
  7.      onUpdateAvailable(mWifiManager.getScanResults(), mNativeObject);   
  8.    }   
  9.  }  


4.Native函数onUpdateAvailable是在 wifi_data_provider_android.cc里实现的。
Java代码 复制代码 收藏代码
  1. JNINativeMethod AndroidWifiDataProvider::native_methods_[] = {   
  2.   {"onUpdateAvailable",   
  3.    "(Ljava/util/List;J)V",   
  4.    reinterpret_cast<void*>(AndroidWifiDataProvider::OnUpdateAvailable)   
  5.   },   
  6. };   
  7.     
  8. void AndroidWifiDataProvider::OnUpdateAvailable(JNIEnv*  /* env */,   
  9.                                                 jclass  /* cls */,   
  10.                                                 jobject wifi_data,   
  11.                                                 jlong self) {   
  12.   assert(self);   
  13.   AndroidWifiDataProvider *self_ptr =   
  14.       reinterpret_cast<AndroidWifiDataProvider*>(self);   
  15.   WifiData new_wifi_data;   
  16.   if (wifi_data) {   
  17.     InitFromJava(wifi_data, &new_wifi_data);   
  18.   }   
  19.   // We notify regardless of whether new_wifi_data is empty  
  20.   // or not. The arbitrator will decide what to do with an empty  
  21.   // WifiData object.   
  22.   self_ptr->NewWifiDataAvailable(&new_wifi_data);   
  23. }   
  24.     
  25. void AndroidWifiDataProvider::NewWifiDataAvailable(WifiData* new_wifi_data) {   
  26.   assert(supported_);   
  27.   assert(new_wifi_data);   
  28.   bool is_update_available = false;   
  29.   data_mutex_.Lock();   
  30.   is_update_available = wifi_data_.DiffersSignificantly(*new_wifi_data);   
  31.   wifi_data_ = *new_wifi_data;   
  32.   // Avoid holding the mutex locked while notifying observers.  
  33.   data_mutex_.Unlock();   
  34.     
  35.   if (is_update_available) {   
  36.     is_first_scan_complete_ = true;   
  37.     NotifyListeners();   
  38.   }   
  39.     
  40. #if USING_CCTESTS   
  41.   // This is needed for running the WiFi test on the emulator.  
  42.   // See wifi_data_provider_android.h for details.  
  43.   if (!first_callback_made_ && wifi_data_.access_point_data.empty()) {   
  44.     first_callback_made_ = true;   
  45.     NotifyListeners();   
  46.   }   
  47. #endif   
  48. }  


从以上代码可以看出,WIFI定位和基站定位的逻辑差不多,只是前者获取的WIFI的扫描结果,而后者获取的基站信息。后面代码的基本上就统一起来了,接下来我们继续看。

5.把变化(WIFI/基站)通知给相应的监听者。
Java代码 复制代码 收藏代码
  1. AndroidWifiDataProvider和AndroidRadioDataProvider都是继承了DeviceDataProviderImplBase,DeviceDataProviderImplBase的主要功能就是管理所有Listeners。   
  2.     
  3.   static DeviceDataProvider *Register(ListenerInterface *listener) {   
  4.     MutexLock mutex(&instance_mutex_);   
  5.     if (!instance_) {   
  6.       instance_ = new DeviceDataProvider();   
  7.     }   
  8.     assert(instance_);   
  9.     instance_->Ref();   
  10.     instance_->AddListener(listener);   
  11.     return instance_;   
  12.   }   
  13.     
  14.   static bool Unregister(ListenerInterface *listener) {   
  15.     MutexLock mutex(&instance_mutex_);   
  16.     if (!instance_->RemoveListener(listener)) {   
  17.       return false;   
  18.     }   
  19.     if (instance_->Unref()) {   
  20.       delete instance_;   
  21.       instance_ = NULL;   
  22.     }   
  23.     return true;   
  24.   }  


6.谁在监听变化(WIFI/基站)

NetworkLocationProvider在监听变化(WIFI/基站):
Java代码 复制代码 收藏代码
  1. radio_data_provider_ = RadioDataProvider::Register(this);   
  2.   wifi_data_provider_ = WifiDataProvider::Register(this);  


当有变化时,会调用函数DeviceDataUpdateAvailable:
Java代码 复制代码 收藏代码
  1. // DeviceDataProviderInterface::ListenerInterface implementation.  
  2. void NetworkLocationProvider::DeviceDataUpdateAvailable(   
  3.     RadioDataProvider *provider) {   
  4.   MutexLock lock(&data_mutex_);   
  5.   assert(provider == radio_data_provider_);   
  6.   is_radio_data_complete_ = radio_data_provider_->GetData(&radio_data_);   
  7.     
  8.   DeviceDataUpdateAvailableImpl();   
  9. }   
  10.     
  11. void NetworkLocationProvider::DeviceDataUpdateAvailable(   
  12.     WifiDataProvider *provider) {   
  13.   assert(provider == wifi_data_provider_);   
  14.   MutexLock lock(&data_mutex_);   
  15.   is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_);   
  16.     
  17.   DeviceDataUpdateAvailableImpl();   
  18. }  


无论是WIFI还是基站变化,最后都会调用 DeviceDataUpdateAvailableImpl:
Java代码 复制代码 收藏代码
  1. void NetworkLocationProvider::DeviceDataUpdateAvailableImpl() {   
  2.   timestamp_ = GetCurrentTimeMillis();   
  3.     
  4.   // Signal to the worker thread that new data is available.  
  5.   is_new_data_available_ = true;   
  6.   thread_notification_event_.Signal();   
  7. }  


这里面只是发了一个signal,通知另外一个线程去处理。

7.谁在等待thread_notification_event_

线程函数NetworkLocationProvider::Run在一个循环中等待 thread_notification_event,当有变化(WIFI/基站)时,就准备请求服务器查询位置。

先等待:
Java代码 复制代码 收藏代码
  1. if (remaining_time > 0) {   
  2.       thread_notification_event_.WaitWithTimeout(   
  3.           static_cast<int>(remaining_time));   
  4.     } else {   
  5.       thread_notification_event_.Wait();   
  6.     }  


准备请求:
Java代码 复制代码 收藏代码
  1. if (make_request) {   
  2.   MakeRequest();   
  3.   remaining_time = 1;   
  4. }  


再来看MakeRequest的实现:

先从cache中查找位置:
Java代码 复制代码 收藏代码
  1. const Position *cached_position =   
  2.      position_cache_->FindPosition(radio_data_, wifi_data_);   
  3.  data_mutex_.Unlock();   
  4.  if (cached_position) {   
  5.    assert(cached_position->IsGoodFix());   
  6.    // Record the position and update its timestamp.  
  7.    position_mutex_.Lock();   
  8.    position_ = *cached_position;   
  9.    position_.timestamp = timestamp_;   
  10.    position_mutex_.Unlock();   
  11.   
  12.    // Let listeners know that we now have a position available.  
  13.    UpdateListeners();   
  14.    return true;   
  15.  }  


如果找不到,再做实际的请求
Java代码 复制代码 收藏代码
  1. return request_->MakeRequest(access_token,   
  2.                              radio_data_,   
  3.                              wifi_data_,   
  4.                              request_address_,   
  5.                              address_language_,   
  6.                              kBadLatLng,  // We don't have a position to pass  
  7.                              kBadLatLng,  // to the server.  
  8.                              timestamp_);  


7.客户端协议包装

前面的request_是NetworkLocationRequest实例,先看 MakeRequest的实现:

先对参数进行打包:
Java代码 复制代码 收藏代码
  1. if (!FormRequestBody(host_name_, access_token, radio_data, wifi_data,   
  2.                      request_address, address_language, latitude, longitude,   
  3.                      is_reverse_geocode_, &post_body_)) {   
  4.   return false;   
  5. }  


通知负责收发的线程
Java代码 复制代码 收藏代码
  1. thread_event_.Signal();  


8.负责收发的线程
Java代码 复制代码 收藏代码
  1. void NetworkLocationRequest::Run() {   
  2.   while (true) {   
  3.     thread_event_.Wait();   
  4.     if (is_shutting_down_) {   
  5.       break;   
  6.     }   
  7.     MakeRequestImpl();   
  8.   }   
  9. }   
  10.     
  11. void NetworkLocationRequest::MakeRequestImpl() {   
  12.   WebCacheDB::PayloadInfo payload;  


把打包好的数据通过HTTP请求,发送给服务器
Java代码 复制代码 收藏代码
  1. scoped_refptr<BlobInterface> payload_data;   
  2.  bool result = HttpPost(url_.c_str(),   
  3.                         false,            // Not capturing, so follow redirects  
  4.                         NULL,             // reason_header_value  
  5.                         HttpConstants::kMimeApplicationJson,  // Content-Type  
  6.                         NULL,             // mod_since_date  
  7.                         NULL,             // required_cookie  
  8.                         true,             // disable_browser_cookies  
  9.                         post_body_.get(),   
  10.                         &payload,   
  11.                         &payload_data,   
  12.                         NULL,             // was_redirected  
  13.                         NULL,             // full_redirect_url  
  14.                         NULL);            // error_message  
  15.   
  16.  MutexLock lock(&is_processing_response_mutex_);   
  17.  // is_aborted_ may be true even if HttpPost succeeded.  
  18.  if (is_aborted_) {   
  19.    LOG(("NetworkLocationRequest::Run() : HttpPost request was cancelled.\n"));   
  20.    return;   
  21.  }   
  22.  if (listener_) {   
  23.    Position position;   
  24.    std::string response_body;   
  25.    if (result) {   
  26.      // If HttpPost succeeded, payload_data is guaranteed to be non-NULL.  
  27.      assert(payload_data.get());   
  28.      if (!payload_data->Length() ||   
  29.          !BlobToString(payload_data.get(), &response_body)) {   
  30.        LOG(("NetworkLocationRequest::Run() : Failed to get response body.\n"));   
  31.      }   
  32.    }  


解析出位置信息
Java代码 复制代码 收藏代码
  1. std::string16 access_token;   
  2.     GetLocationFromResponse(result, payload.status_code, response_body,   
  3.                             timestamp_, url_, is_reverse_geocode_,   
  4.                             &position, &access_token);  


通知位置信息的监听者。
Java代码 复制代码 收藏代码
  1.  bool server_error =   
  2.         !result || (payload.status_code >= 500 && payload.status_code < 600);   
  3.     listener_->LocationResponseAvailable(position, server_error, access_token);   
  4.   }   
  5. }  


有人会问,请求是发哪个服务器的?当然是google了,缺省的URL是:
Java代码 复制代码 收藏代码
  1. static const char16 *kDefaultLocationProviderUrl =   
  2.     STRING16(L"https://www.google.com/loc/json");  


回过头来,我们再总结一下:

1.WIFI和基站定位过程如下:


2.NetworkLocationProvider和 NetworkLocationRequest各有一个线程来异步处理请求。

3.这里的NetworkLocationProvider与android中的 NetworkLocationProvider并不是同一个东西,这里是给gears用的,要在android的google map中使用,还得包装成android中的NetworkLocationProvider的接口。

4.WIFI和基站定位与平台无关,只要你能拿到WIFI扫描结果或基站信息,而且能访问google的定位服务器,不管你是Android平台,Windows Mobile平台还是传统的feature phone,你都可以实现WIFI和基站定位。



附: WIFI和基站定位原理

无论是WIFI的接入点,还是移动网络的基站设备,它们的位置基本上都是固定的。设备端(如手机)可以找到它们的ID,现在的问题就是如何通过这些ID找到对应的位置。网上的流行的说法是开车把所有每个位置都跑一遍,把这些设备的位置与 GPS测试的位置关联起来。


参考资料:
Gears:
[url] http://gears.googlecode.com/[/url]
Google 地图 API:[url] http://code.google.com/intl/zh-CN/apis/maps/documentation/reference.html[/url]
wifi定位技术:[url] http://blog.csdn.net/NewMap/archive/2009/03/17/3999337.aspx
[/url]
 
zhua
原创粉丝点击