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测试的位置关联起来。 

相关文章
相关标签/搜索