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电话的状态变化。当服务状态、信号强度和基站变化时,
就会用下面代码获取小区信息:
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测试的位置关联起来。