Android系列之Wifi定位

Android系列之Wifi定位html

Broncho A1還不支持基站和WIFI定位,Android的老版本里是有NetworkLocationProvider的,它實現了基站和WIFI定位,但從 android 1.5以後就被移除了。原本想在broncho A1裏本身實現NetworkLocationProvider的,但一直沒有時間去研究。我知道 gears(http://code.google.com/p/gears/)是有提供相似的功能,昨天研究了一下Gears的代碼,看能不能移植到 android中來java

1.下載源代碼
[url]svn checkout http://gears.googlecode.com/svn/trunk/ gears-read-only[/url] 
定位相關的源代碼在gears/geolocation目錄中。android

2.關注android平臺中的基站位置變化git

JAVA類AndroidRadioDataProvider是 PhoneStateListener的子類,用來監聽Android電話的狀態變化。當服務狀態、信號強度和基站變化時,json

就會用下面代碼獲取小區信息:服務器

 

複製代碼
 1RadioData 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(operatortrue);   12 13if (serviceState !=null) {   14// Extract the carrier name.    15        radioData.carrierName = serviceState.getOperatorAlphaLong();   16 17// Extract the MCC and MNC.    18operator= serviceState.getOperatorNumeric();   19        radioData.setMobileCodes(operatorfalse);   20      }   21 22// Finally get the radio type.    23int type = telephonyManager.getNetworkType();   24if (type == TelephonyManager.NETWORK_TYPE_UMTS) {   25        radioData.radioType = RADIO_TYPE_WCDMA;   26      } elseif (type == TelephonyManager.NETWORK_TYPE_GPRS   27|| type == TelephonyManager.NETWORK_TYPE_EDGE) {   28        radioData.radioType = RADIO_TYPE_GSM;   29      }  30
複製代碼



而後再調用用C代碼實現的onUpdateAvailable函數。 
2.Native函數onUpdateAvailable是在 radio_data_provider_android.cc裏實現的。
聲明Native函數 cookie

 

複製代碼
1JNINativeMethod AndroidRadioDataProvider::native_methods_[] = {   2  {"onUpdateAvailable",   3"(L" GEARS_JAVA_PACKAGE "/AndroidRadioDataProvider$RadioData;J)V",   4   reinterpret_cast<void*>(AndroidRadioDataProvider::OnUpdateAvailable)   5  },   6};  7
複製代碼

 

 

JNI調用好像只能調用靜態成員函數,把對象自己用一個參數傳進來,而後再調用對象的成員函數。網絡

 

代碼
void AndroidRadioDataProvider::OnUpdateAvailable(JNIEnv* env,   
                                                 jclass cls,   
                                                 jobject radio_data,   
                                                 jlong self) {   
  assert(radio_data);   
  assert(self);   
  AndroidRadioDataProvider *self_ptr =
      reinterpret_cast<AndroidRadioDataProvider*>(self);   
  RadioData new_radio_data;   if (InitFromJavaRadioData(env, radio_data, &new_radio_data)) {   
    self_ptr->NewRadioDataAvailable(&new_radio_data);   
  }   
}  

先判斷基站信息有沒有變化,若是有變化則通知相關的監聽者。less

 

複製代碼
 1void AndroidRadioDataProvider::NewRadioDataAvailable(    2    RadioData* new_radio_data) {    3bool is_update_available =false;    4  data_mutex_.Lock();    5if (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 12if (is_update_available) {   13    NotifyListeners();   14  }   15}  
複製代碼

 

 

接下來的過程,在基站定位和WIFI定位是同樣的,後面咱們再來介紹。下面咱們先看 WIFI定位異步

3.關注android平臺中的WIFI變化。

JAVA類AndroidWifiDataProvider擴展了 BroadcastReceiver類,它關注WIFI掃描結果:

 

1IntentFilter filter =new IntentFilter();   2    filter.addAction(mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION);   3    mContext.registerReceiver(this, filter, null, handler);  

當收到WIFI掃描結果後,調用Native函數 onUpdateAvailable,並把WIFI的掃描結果傳遞過去。

 

複製代碼
1publicvoid onReceive(Context context, Intent intent) {   2if (intent.getAction().equals(   3           mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {   4if (Config.LOGV) {   5       Log.v(TAG, "Wifi scan resulst available");   6     }   7     onUpdateAvailable(mWifiManager.getScanResults(), mNativeObject);   8   }   9 }  
複製代碼

Native函數onUpdateAvailable是在 wifi_data_provider_android.cc裏實現的。 

 

複製代碼
1JNINativeMethod AndroidWifiDataProvider::native_methods_[] = { 2{"onUpdateAvailable"3"(Ljava/util/List;J)V"4reinterpret_cast<void*>(AndroidWifiDataProvider::OnUpdateAvailable) 5}, 6}; 7 8void AndroidWifiDataProvider::OnUpdateAvailable(JNIEnv*/* env */9jclass /* cls */10jobject wifi_data, 11jlong self) { 12assert(self); 13AndroidWifiDataProvider *self_ptr =14reinterpret_cast<AndroidWifiDataProvider*>(self); 15WifiData new_wifi_data; 16if (wifi_data) { 17InitFromJava(wifi_data, &new_wifi_data); 1819// 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.  22self_ptr->NewWifiDataAvailable(&new_wifi_data); 2324 25void AndroidWifiDataProvider::NewWifiDataAvailable(WifiData* new_wifi_data) { 26assert(supported_); 27assert(new_wifi_data); 28bool is_update_available =false29data_mutex_.Lock(); 30is_update_available = wifi_data_.DiffersSignificantly(*new_wifi_data); 31wifi_data_ =*new_wifi_data; 32// Avoid holding the mutex locked while notifying observers.  33data_mutex_.Unlock(); 34 35if (is_update_available) { 36is_first_scan_complete_ =true37NotifyListeners(); 3839 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.  43if (!first_callback_made_ && wifi_data_.access_point_data.empty()) { 44first_callback_made_ =true45NotifyListeners(); 4647#endif 4849 50JNINativeMethod AndroidWifiDataProvider::native_methods_[] = {51{"onUpdateAvailable",52"(Ljava/util/List;J)V",53reinterpret_cast<void*>(AndroidWifiDataProvider::OnUpdateAvailable)54},55};56 57void AndroidWifiDataProvider::OnUpdateAvailable(JNIEnv*/* env */,58jclass /* cls */,59jobject wifi_data,60jlong self) {61assert(self);62AndroidWifiDataProvider *self_ptr =63reinterpret_cast<AndroidWifiDataProvider*>(self);64WifiData new_wifi_data;65if (wifi_data) {66InitFromJava(wifi_data, &new_wifi_data);67}68// We notify regardless of whether new_wifi_data is empty69// or not. The arbitrator will decide what to do with an empty70// WifiData object. 71self_ptr->NewWifiDataAvailable(&new_wifi_data);72}73 74void AndroidWifiDataProvider::NewWifiDataAvailable(WifiData* new_wifi_data) {75assert(supported_);76assert(new_wifi_data);77bool is_update_available =false;78data_mutex_.Lock();79is_update_available = wifi_data_.DiffersSignificantly(*new_wifi_data);80wifi_data_ =*new_wifi_data;81// Avoid holding the mutex locked while notifying observers. 82data_mutex_.Unlock();83 84if (is_update_available) {85is_first_scan_complete_ =true;86NotifyListeners();87}88 89#if USING_CCTESTS90// This is needed for running the WiFi test on the emulator.91// See wifi_data_provider_android.h for details. 92if (!first_callback_made_ && wifi_data_.access_point_data.empty()) {93first_callback_made_ =true;94NotifyListeners();95}96#endif 97}98
複製代碼

 從以上代碼能夠看出,WIFI定位和基站定位的邏輯差很少,只是前者獲取的WIFI的掃描結果,然後者獲取的基站信息。

後面代碼的基本上就統一塊兒來了,接下來咱們繼續看。 

5.把變化(WIFI/基站)通知給相應的監聽者。

複製代碼
 1AndroidWifiDataProvider和AndroidRadioDataProvider都是繼承了DeviceDataProviderImplBase,DeviceDataProviderImplBase的主要功能就是管理全部Listeners。    2  3static DeviceDataProvider *Register(ListenerInterface *listener) {    4    MutexLock mutex(&instance_mutex_);    5if (!instance_) {    6      instance_ =new DeviceDataProvider();    7    }    8    assert(instance_);    9    instance_->Ref();   10    instance_->AddListener(listener);   11return instance_;   12  }   13 14staticbool Unregister(ListenerInterface *listener) {   15    MutexLock mutex(&instance_mutex_);   16if (!instance_->RemoveListener(listener)) {   17returnfalse;   18    }   19if (instance_->Unref()) {   20      delete instance_;   21      instance_ = NULL;   22    }   23returntrue;   24  }  25
複製代碼

 

 



6.誰在監聽變化(WIFI/基站)

NetworkLocationProvider在監聽變化(WIFI/基站): 

1radio_data_provider_ = RadioDataProvider::Register(this);   2  wifi_data_provider_ = WifiDataProvider::Register(this);  


當有變化時,會調用函數DeviceDataUpdateAvailable:

 

代碼
複製代碼
// DeviceDataProviderInterface::ListenerInterface implementation. void NetworkLocationProvider::DeviceDataUpdateAvailable(
    RadioDataProvider *provider) {
  MutexLock lock(&data_mutex_);
  assert(provider == radio_data_provider_);
  is_radio_data_complete_ = radio_data_provider_->GetData(&radio_data_);

  DeviceDataUpdateAvailableImpl();
}void NetworkLocationProvider::DeviceDataUpdateAvailable(
    WifiDataProvider *provider) {
  assert(provider == wifi_data_provider_);
  MutexLock lock(&data_mutex_);
  is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_);

  DeviceDataUpdateAvailableImpl();
}
複製代碼

 

 


 不管是WIFI仍是基站變化,最後都會調用 DeviceDataUpdateAvailableImpl:

 

複製代碼
1void 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/基站)時,就準備請求服務器查詢位置。 

先等待:

 

複製代碼
1if (remaining_time >0) {2      thread_notification_event_.WaitWithTimeout(3          static_cast<int>(remaining_time));4    } else {5      thread_notification_event_.Wait();6    }
複製代碼

準備請求:

 

1if (make_request) {   2  MakeRequest();   3  remaining_time =1;   4}  



再來看MakeRequest的實現: 

先從cache中查找位置:

 

複製代碼
 1const Position *cached_position = 2      position_cache_->FindPosition(radio_data_, wifi_data_); 3  data_mutex_.Unlock(); 4if (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();14returntrue;15  }
複製代碼

 

 



若是找不到,再作實際的請求 

 

 

複製代碼
1return 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的實現: 

先對參數進行打包:

 

 

 

1if (!FormRequestBody(host_name_, access_token, radio_data, wifi_data,2                       request_address, address_language, latitude, longitude,3                       is_reverse_geocode_, &post_body_)) {4returnfalse;5  }


通知負責收發的線程 

 

1thread_event_.Signal();

 

 

8.負責收發的線程

 

複製代碼
 1void NetworkLocationRequest::Run() { 2while (true) { 3    thread_event_.Wait(); 4if (is_shutting_down_) { 5break; 6    } 7    MakeRequestImpl(); 8  } 9}10 11void NetworkLocationRequest::MakeRequestImpl() {12  WebCacheDB::PayloadInfo payload;
複製代碼


把打包好的數據經過HTTP請求,發送給服務器

複製代碼
 1 scoped_refptr<BlobInterface> payload_data; 2bool result = HttpPost(url_.c_str(), 3false,            // Not capturing, so follow redirects  4                         NULL,             // reason_header_value  5                         HttpConstants::kMimeApplicationJson,  // Content-Type  6                         NULL,             // mod_since_date  7                         NULL,             // required_cookie  8true,             // 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. 18if (is_aborted_) {19    LOG(("NetworkLocationRequest::Run() : HttpPost request was cancelled.\n"));20return;21  }22if (listener_) {23    Position position;24    std::string response_body;25if (result) {26// If HttpPost succeeded, payload_data is guaranteed to be non-NULL. 27      assert(payload_data.get());28if (!payload_data->Length() ||29!BlobToString(payload_data.get(), &response_body)) {30        LOG(("NetworkLocationRequest::Run() : Failed to get response body.\n"));31      }32    }
複製代碼

 

 

解析出位置信息 

 

1std::string16 access_token;2    GetLocationFromResponse(result, payload.status_code, response_body,3                            timestamp_, url_, is_reverse_geocode_,4&position, &access_token);

通知位置信息的監聽者

 

1bool server_error =2!result || (payload.status_code >=500&& payload.status_code <600);3    listener_->LocationResponseAvailable(position, server_error, access_token);4  }5}

 

 

有人會問,請求是發哪一個服務器的?固然是google了,缺省的URL是:

 

 

1staticconst 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測試的位置關聯起來。 

相關文章
相關標籤/搜索