Gears Android WIFI/基站定位原始碼分析

來源:互聯網
上載者:User

文章出處:http://www.limodev.cn/blog
作者連絡方式:李先靜 <xianjimli at hotmail dot com>

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電話的狀態變化。當服務狀態、訊號強度和基站變化時,就會用下面代碼擷取小區資訊:

      RadioData radioData = new RadioData();
      GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation;

      // Extract the cell id, LAC, and signal strength.
      radioData.cellId = gsmCellLocation.getCid();
      radioData.locationAreaCode = gsmCellLocation.getLac();
      radioData.signalStrength = signalStrength;

      // Extract the home MCC and home MNC.
      String operator = telephonyManager.getSimOperator();
      radioData.setMobileCodes(operator, true);

      if (serviceState != null) {
        // Extract the carrier name.
        radioData.carrierName = serviceState.getOperatorAlphaLong();

        // Extract the MCC and MNC.
        operator = serviceState.getOperatorNumeric();
        radioData.setMobileCodes(operator, false);
      }

      // Finally get the radio type.
      int type = telephonyManager.getNetworkType();
      if (type == TelephonyManager.NETWORK_TYPE_UMTS) {
        radioData.radioType = RADIO_TYPE_WCDMA;
      } else if (type == TelephonyManager.NETWORK_TYPE_GPRS
                 || type == TelephonyManager.NETWORK_TYPE_EDGE) {
        radioData.radioType = RADIO_TYPE_GSM;
      }

然後調用用C代碼實現的onUpdateAvailable函數。

2.Native函數onUpdateAvailable是在radio_data_provider_android.cc裡實現的。

聲明Native函數

JNINativeMethod AndroidRadioDataProvider::native_methods_[] = {
{"onUpdateAvailable",
   "(L" GEARS_JAVA_PACKAGE "/AndroidRadioDataProvider$RadioData;J)V",
   reinterpret_cast<void*>(AndroidRadioDataProvider::OnUpdateAvailable)
},
};

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(
    RadioData* new_radio_data) {
bool is_update_available = false;
data_mutex_.Lock();
if (new_radio_data && !radio_data_.Matches(*new_radio_data)) {
    radio_data_ = *new_radio_data;
    is_update_available = true;
}
// Avoid holding the mutex locked while notifying observers.
data_mutex_.Unlock();

if (is_update_available) {
    NotifyListeners();
}
}

接下來的過程,基站定位和WIFI定位是一樣的,後面我們再來介紹。下面我們先看WIFI定位。

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

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

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

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

public void onReceive(Context context, Intent intent) {
    if (intent.getAction().equals(
            mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
      if (Config.LOGV) {
        Log.v(TAG, "Wifi scan resulst available");
      }
      onUpdateAvailable(mWifiManager.getScanResults(), mNativeObject);
    }
}

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

JNINativeMethod AndroidWifiDataProvider::native_methods_[] = {
{"onUpdateAvailable",
   "(Ljava/util/List;J)V",
   reinterpret_cast<void*>(AndroidWifiDataProvider::OnUpdateAvailable)
},
};

void AndroidWifiDataProvider::OnUpdateAvailable(JNIEnv* /* env */,
                                                jclass /* cls */,
                                                jobject wifi_data,
                                                jlong self) {
assert(self);
AndroidWifiDataProvider *self_ptr =
      reinterpret_cast<AndroidWifiDataProvider*>(self);
WifiData new_wifi_data;
if (wifi_data) {
    InitFromJava(wifi_data, &new_wifi_data);
}
// We notify regardless of whether new_wifi_data is empty
// or not. The arbitrator will decide what to do with an empty
// WifiData object.
self_ptr->NewWifiDataAvailable(&new_wifi_data);
}

void AndroidWifiDataProvider::NewWifiDataAvailable(WifiData* new_wifi_data) {
assert(supported_);
assert(new_wifi_data);
bool is_update_available = false;
data_mutex_.Lock();
is_update_available = wifi_data_.DiffersSignificantly(*new_wifi_data);
wifi_data_ = *new_wifi_data;
// Avoid holding the mutex locked while notifying observers.
data_mutex_.Unlock();

if (is_update_available) {
    is_first_scan_complete_ = true;
    NotifyListeners();
}

#if USING_CCTESTS
// This is needed for running the WiFi test on the emulator.
// See wifi_data_provider_android.h for details.
if (!first_callback_made_ && wifi_data_.access_point_data.empty()) {
    first_callback_made_ = true;
    NotifyListeners();
}
#endif
}

從以上代碼可以看出,WIFI定位和基站定位的邏輯差不多,只是前者擷取的WIFI的掃描結果,而後者擷取的基站資訊。後面代碼的基本上就統一起來了,接下來我們繼續看。

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

AndroidWifiDataProvider和AndroidRadioDataProvider都是繼承了DeviceDataProviderImplBase,DeviceDataProviderImplBase的主要功能就是管理所有Listeners。

static DeviceDataProvider *Register(ListenerInterface *listener) {
    MutexLock mutex(&instance_mutex_);
    if (!instance_) {
      instance_ = new DeviceDataProvider();
    }
    assert(instance_);
    instance_->Ref();
    instance_->AddListener(listener);
    return instance_;
}

static bool Unregister(ListenerInterface *listener) {
    MutexLock mutex(&instance_mutex_);
    if (!instance_->RemoveListener(listener)) {
      return false;
    }
    if (instance_->Unref()) {
      delete instance_;
      instance_ = NULL;
    }
    return true;
}

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

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

radio_data_provider_ = RadioDataProvider::Register(this);
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:

void NetworkLocationProvider::DeviceDataUpdateAvailableImpl() {
timestamp_ = GetCurrentTimeMillis();

// Signal to the worker thread that new data is available.
is_new_data_available_ = true;
thread_notification_event_.Signal();
}

這裡面只是發了一個signal,通知另外一個線程去處理。

7.誰在等待thread_notification_event_

線程函數NetworkLocationProvider::Run在一個迴圈中等待thread_notification_event,當有變化(WIFI/基站)時,就準備請求伺服器查詢位置。

先等待:

    if (remaining_time > 0) {
      thread_notification_event_.WaitWithTimeout(
          static_cast<int>(remaining_time));
    } else {
      thread_notification_event_.Wait();
    }

準備請求:

    if (make_request) {
      MakeRequest();
      remaining_time = 1;
    }

再來看MakeRequest的實現:

先從cache中尋找位置:

const Position *cached_position =
      position_cache_->FindPosition(radio_data_, wifi_data_);
data_mutex_.Unlock();
if (cached_position) {
    assert(cached_position->IsGoodFix());
    // Record the position and update its timestamp.
    position_mutex_.Lock();
    position_ = *cached_position;
    position_.timestamp = timestamp_;
    position_mutex_.Unlock();

    // Let listeners know that we now have a position available.
    UpdateListeners();
    return true;
}

如果找不到,再做實際的請求

return request_->MakeRequest(access_token,
                               radio_data_,
                               wifi_data_,
                               request_address_,
                               address_language_,
                               kBadLatLng, // We don't have a position to pass
                               kBadLatLng, // to the server.
                               timestamp_);

7.用戶端協議封裝

前面的request_是NetworkLocationRequest執行個體,先看MakeRequest的實現:

先對參數進行打包:

if (!FormRequestBody(host_name_, access_token, radio_data, wifi_data,
                       request_address, address_language, latitude, longitude,
                       is_reverse_geocode_, &post_body_)) {
    return false;
}

通知負責收發的線程

thread_event_.Signal();

8.負責收發的線程

void NetworkLocationRequest::Run() {
while (true) {
    thread_event_.Wait();
    if (is_shutting_down_) {
      break;
    }
    MakeRequestImpl();
}
}

void NetworkLocationRequest::MakeRequestImpl() {
WebCacheDB::PayloadInfo payload;

把打包好的資料通過HTTP請求,發送給伺服器

scoped_refptr<BlobInterface> payload_data;
bool result = HttpPost(url_.c_str(),
                         false,            // Not capturing, so follow redirects
                         NULL,             // reason_header_value
                         HttpConstants::kMimeApplicationJson, // Content-Type
                         NULL,             // mod_since_date
                         NULL,             // required_cookie
                         true,             // disable_browser_cookies
                         post_body_.get(),
                         &payload,
                         &payload_data,
                         NULL,             // was_redirected
                         NULL,             // full_redirect_url
                         NULL);            // error_message

MutexLock lock(&is_processing_response_mutex_);
// is_aborted_ may be true even if HttpPost succeeded.
if (is_aborted_) {
    LOG(("NetworkLocationRequest::Run() : HttpPost request was cancelled.\n"));
    return;
}
if (listener_) {
    Position position;
    std::string response_body;
    if (result) {
      // If HttpPost succeeded, payload_data is guaranteed to be non-NULL.
      assert(payload_data.get());
      if (!payload_data->Length() ||
          !BlobToString(payload_data.get(), &response_body)) {
        LOG(("NetworkLocationRequest::Run() : Failed to get response body.\n"));
      }
    }

解析出位置資訊

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

通知位置資訊的監聽者。

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

有人會問,請求是發哪個伺服器的?當然是google了,預設的URL是:

static const char16 *kDefaultLocationProviderUrl =
    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測試的位置關聯起來。

參考資料:
Gears: http://gears.googlecode.com/
Google 地圖 API: http://code.google.com/intl/zh-CN/apis/maps/documentation/reference.html
wifi定位技術: http://blog.csdn.net/NewMap/archive/2009/03/17/3999337.aspx

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.