您的位置:首页 > 移动开发 > Android开发

Android系列之Wifi定位

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

1.下载源代码

svn checkout http://gears.googlecode.com/svn/trunk/ gears-read-only

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

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

JAVA类AndroidRadioDataProvider是 PhoneStateListener的子类,用来监听Android电话的状态变化。当服务状态、信号强度和基站变化时,

就会用下面代码获取小区信息:

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 }

30

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

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

声明Native函数


1
JNINativeMethod 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);


}


}

先判断基站信息有没有变化,如果有变化则通知相关的监听者。


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扫描结果:


1
IntentFilter filter = new IntentFilter();


2
filter.addAction(mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION);


3
mContext.registerReceiver(this, filter, null,
handler);

当收到WIFI扫描结果后,调用Native函数 onUpdateAvailable,并把WIFI的扫描结果传递过去。


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 }

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


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
}


49


50
JNINativeMethod AndroidWifiDataProvider::native_methods_[] = {


51
{"onUpdateAvailable",


52 "(Ljava/util/List;J)V",


53
reinterpret_cast<void*>(AndroidWifiDataProvider::OnUpdateAvailable)


54
},


55
};


56


57 void AndroidWifiDataProvider::OnUpdateAvailable(JNIEnv* /*
env */,


58
jclass /* cls */,


59
jobject wifi_data,


60
jlong self) {


61
assert(self);


62
AndroidWifiDataProvider *self_ptr =


63
reinterpret_cast<AndroidWifiDataProvider*>(self);


64
WifiData new_wifi_data;


65 if (wifi_data)
{


66
InitFromJava(wifi_data, &new_wifi_data);


67
}


68 //
We notify regardless of whether new_wifi_data is empty


69 //
or not. The arbitrator will decide what to do with an empty


70 //
WifiData object.


71
self_ptr->NewWifiDataAvailable(&new_wifi_data);


72
}


73


74 void AndroidWifiDataProvider::NewWifiDataAvailable(WifiData*
new_wifi_data) {


75
assert(supported_);


76
assert(new_wifi_data);


77 bool is_update_available
= false;


78
data_mutex_.Lock();


79
is_update_available = wifi_data_.DiffersSignificantly(*new_wifi_data);


80
wifi_data_ = *new_wifi_data;


81 //
Avoid holding the mutex locked while notifying observers.


82
data_mutex_.Unlock();


83


84 if (is_update_available)
{


85
is_first_scan_complete_ = true;


86
NotifyListeners();


87
}


88


89
#if USING_CCTESTS


90 //
This is needed for running the WiFi test on the emulator.


91 //
See wifi_data_provider_android.h for details.


92 if (!first_callback_made_
&& wifi_data_.access_point_data.empty()) {


93
first_callback_made_ = true;


94
NotifyListeners();


95
}


96
#endif


97
}


98

从以上代码可以看出,WIFI定位和基站定位的逻辑差不多,只是前者获取的WIFI的扫描结果,而后者获取的基站信息。

后面代码的基本上就统一起来了,接下来我们继续看。

5.把变化(WIFI/基站)通知给相应的监听者。


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
}


25

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

NetworkLocationProvider在监听变化(WIFI/基站):


1
radio_data_provider_ = RadioDataProvider::Register(this);


2
wifi_data_provider_ = WifiDataProvider::Register(this);

当有变化时,会调用函数DeviceDataUpdateAvailable:



无论是WIFI还是基站变化,最后都会调用 DeviceDataUpdateAvailableImpl:


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/基站)时,就准备请求服务器查询位置。

先等待:


1 if (remaining_time
> 0) {


2
thread_notification_event_.WaitWithTimeout(


3
static_cast<int>(remaining_time));


4
} else {


5
thread_notification_event_.Wait();


6
}

准备请求:


1 if (make_request)
{


2
MakeRequest();


3
remaining_time = 1;


4
}

再来看MakeRequest的实现:

先从cache中查找位置:


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
}

如果找不到,再做实际的请求


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的实现:

先对参数进行打包:


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
}

通知负责收发的线程


1
thread_event_.Signal();

8.负责收发的线程


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请求,发送给服务器


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
}

解析出位置信息


1
std::string16 access_token;


2
GetLocationFromResponse(result, payload.status_code, response_body,


3
timestamp_, url_, is_reverse_geocode_,


4
&position, &access_token);

通知位置信息的监听者


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是:


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

本文出自 “失落的程序” 博客,请务必保留此出处http://350526.blog.51cto.com/340526/367139
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: