现在的位置: 首页 > 综合 > 正文

Android系列之Wifi定位

2018年04月17日 ⁄ 综合 ⁄ 共 14840字 ⁄ 字号 评论关闭
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);            
}        
}    
 
先判断基站信息有没有变化,如果有变化则通知相关的监听者。
 
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的扫描结果传递过去。
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",    
"(Ljava/util/List;J)V",    
4
reinterpret_cast<void*>(AndroidWifiDataProvider::OnUpdateAvailable)    
5
},    
6
};    
7    
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:
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/基站)时,就准备请求服务器查询位置。 


先等待:
if (remaining_time
> 0) { 
2            
thread_notification_event_.WaitWithTimeout( 
3                    
static_cast<int>(remaining_time)); 
4        
else { 
5            
thread_notification_event_.Wait(); 
6        
}

准备请求:

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.负责收发的线程

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

抱歉!评论已关闭.