android gps機制分析--之二

Achillisjack發表於2017-05-21

1,概述

在android系統中,GPS對應的系統服務為LocationManagerService,本文主要論述LocationManagerService服務的啟動以及初始化過程。

   SystemServer.java的startOtherServices方法中新增LocationManagerService方法的程式碼如下,

location = new LocationManagerService(context);
ServiceManager.addService(Context.LOCATION_SERVICE, location);

apk中獲取gps服務代理的程式碼如下,

mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

當然, LocationManager只是系統服務的一個代理。

新增gps服務到系統之後, SystemServer.java的startOtherServices方法中

locationF.systemRunning();

呼叫LocationManagerService的systemRunning方法,完成LocationManagerService服務的初始化。

2, 回撥方法的實現

當然在呼叫systemRunning之前,在新增到系統過程中,會呼叫LocationManagerService的構造方法,構造方法如下,

mContext = context; //傳入系統服務程式上下文

systemRunning方法中會呼叫loadProvidersLocked方法,

loadProvidersLocked();
updateProvidersLocked();

loadProvidersLocked方法主要是新增裝置上支援的GPS定位Provider,

if (GpsLocationProvider.isSupported()) {
            // Create a gps location provider
            GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this,
                    mLocationHandler.getLooper());
            mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
            mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
            addProviderLocked(gpsProvider);
            mRealProviders.put(LocationManager.GPS_PROVIDER, gpsProvider);
            mGpsMeasurementsProvider = gpsProvider.getGpsMeasurementsProvider();
            mGpsNavigationMessageProvider = gpsProvider.getGpsNavigationMessageProvider();
            mGpsGeofenceProxy = gpsProvider.getGpsGeofenceProxy();
        }

裝置如果支援GpsLocationProvider,就會新建GpsLocationProvider物件,然後新增到mProviders和mProvidersByName等list中。

在這裡分為3個步驟,

1, isSupported方法的判斷。

2, updateProvidersLocked方法。

在這個章節,主要論述回撥方法的實現以及isSupported方法,下個章節論述updateProvidersLocked方法。

在GpsLocationProvider的構造方法和isSupported之前,會呼叫class_init_native方法,

static { class_init_native(); }

該方法是一個native方法,

private static native void class_init_native();
GpsLocationProvider.java對應的C/C++檔案為com_android_server_location_GpsLocationProvider.cpp。

2.1 Framework呼叫JNI層方法

GpsLocationProvider.java中呼叫的native方法較多,部分如下,


com_android_server_location_GpsLocationProvider.cpp的sMethods中詳細的列舉了對應的native方法,部分如下,

{"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
{"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported},
{"native_is_agps_ril_supported", "()Z", (void*)android_location_GpsLocationProvider_is_agps_ril_supported},
•••

這樣Java上層就可以通過JNI呼叫natvie的方法了。

2.2 JNI層呼叫Framework方法

com_android_server_location_GpsLocationProvider還定義著對GpsLocationProvide.java的回撥方法。部分定義如下,

static jmethodID method_reportLocation;
static jmethodID method_reportStatus;
static jmethodID method_reportSvStatus;
•••

這些回撥方法都在android_location_GpsLocationProvider_class_init_native方法中進行賦值,部分程式碼如下,

method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V");
method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V");
•••

因此,method_reportLocation 對應GpsLocationProvide.java的reportLocation方法。

這樣com_android_server_location_GpsLocationProvider就可以回撥Java方法了。

2.3 JNI層呼叫gps庫方法

com_android_server_location_GpsLocationProvider的class_init_native方法中獲取gps.so庫中的sGpsInterface結構程式碼如下,

hw_module_t* module;
err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
    if (err == 0) {
        hw_device_t* device;
        err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);
        if (err == 0) {
            gps_device_t* gps_device = (gps_device_t *)device;
            sGpsInterface = gps_device->get_gps_interface(gps_device);
        }
    }

首先呼叫hw_get_module方法載入gps相關的so庫,獲取hw_module_t結構體。

然後利用該結構體開啟so庫,獲取hw_device_t結構體,

最後利用hw_device_t結構體獲取GpsInterface結構體。

這樣,com_android_server_location_GpsLocationProvider就可以通過GpsInterface結構體呼叫so庫中的方法了。

根據 HAL  載入so文章分析,

GpsInterface結構體指向hardware\qcom\gps\loc_api\libloc_api_50001中loc.cpp的sLocEngInterface。

除了GpsInterface結構體之外,so庫中還定義了其他大量的結構體(包含回撥方法以及變數供上層呼叫),

因此com_android_server_location_GpsLocationProvider還需要獲取,

static const GpsInterface* sGpsInterface = NULL;
static const GpsXtraInterface* sGpsXtraInterface = NULL;
static const AGpsInterface* sAGpsInterface = NULL;
static const GpsNiInterface* sGpsNiInterface = NULL;
static const GpsDebugInterface* sGpsDebugInterface = NULL;
static const AGpsRilInterface* sAGpsRilInterface = NULL;
static const GpsGeofencingInterface* sGpsGeofencingInterface = NULL;
static const GpsMeasurementInterface* sGpsMeasurementInterface = NULL;
static const GpsNavigationMessageInterface* sGpsNavigationMessageInterface = NULL;
static const GnssConfigurationInterface* sGnssConfigurationInterface = NULL;

這些結構體都通過GpsInterface的get_extension方法獲取,不同方法只是以字元區分,

sGpsXtraInterface = (const GpsXtraInterface*)sGpsInterface->get_extension(GPS_XTRA_INTERFACE);
•••

這些字元都定義在gps.h檔案中,

#define GPS_XTRA_INTERFACE      "gps-xtra"
#define GPS_DEBUG_INTERFACE      "gps-debug"
#define AGPS_INTERFACE      "agps"
•••

loc.cpp的loc_get_extension方法如下,

const void* loc_get_extension(const char* name)
{
    ENTRY_LOG();
    const void* ret_val = NULL;

   LOC_LOGD("%s:%d] For Interface = %s\n",__func__, __LINE__, name);
   if (strcmp(name, GPS_XTRA_INTERFACE) == 0)
   {
       ret_val = &sLocEngXTRAInterface;
   }
   else if (strcmp(name, AGPS_INTERFACE) == 0)
   {
       ret_val = &sLocEngAGpsInterface;
   }
•••
EXIT_LOG(%p, ret_val);
    return ret_val;
}

根據不同的字元返回不同的結構體。

這些結構體和GpsInterface完全一樣,gps.h檔案中定義,然後在loc.cpp中實現。

這樣, com_android_server_location_GpsLocationProvider就可以通過這些結構體呼叫so庫中的方法了。

2.4 gps庫呼叫JNI層方法

com_android_server_location_GpsLocationProvider.cpp中定義了一些gps庫調回撥的結構體,

例如sGpsCallbacks,主要是gps庫中上傳資料的資訊,定義如下,

GpsCallbacks sGpsCallbacks = {
    sizeof(GpsCallbacks),
    location_callback,
    status_callback,
    sv_status_callback,
    nmea_callback,
    set_capabilities_callback,
    acquire_wakelock_callback,
    release_wakelock_callback,
    create_thread_callback,
    request_utc_time_callback,
};

這些結構體在gps庫中也都有對應的結構體,相當於JNI到so庫中的一個轉換。

都是通過android_location_GpsLocationProvider_init方法中呼叫gps庫中sGpsInterface/ sGpsXtraInterface

等結構體的init方法傳遞到so庫中。部分程式碼如下,

if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0)
        return JNI_FALSE;
if (sGpsXtraInterface && sGpsXtraInterface->init(&sGpsXtraCallbacks) != 0)
        sGpsXtraInterface = NULL;
if (sAGpsInterface)
        sAGpsInterface->init(&sAGpsCallbacks);
•••

gps庫中gps.h對應的GpsCallbacks定義如下,

typedef struct {
    /** set to sizeof(GpsCallbacks) */
    size_t      size;
    gps_location_callback location_cb;
    gps_status_callback status_cb;
    gps_sv_status_callback sv_status_cb;
    gps_nmea_callback nmea_cb;
    gps_set_capabilities set_capabilities_cb;
    gps_acquire_wakelock acquire_wakelock_cb;
    gps_release_wakelock release_wakelock_cb;
    gps_create_thread create_thread_cb;
    gps_request_utc_time request_utc_time_cb;
} GpsCallbacks;

因此,gps庫中呼叫location_cb方法就是相當於呼叫JNI的location_callback方法,其他的結構體方法也完全相同,都是一一對應。

這樣,gps庫就可以回撥JNI層的方法了。


最後,GpsLocationProvider的isSupported也是一個native方法,
public static boolean isSupported() {
        return native_is_supported();
    }

JNI中的android_location_GpsLocationProvider_is_supported方法如下,

static jboolean android_location_GpsLocationProvider_is_supported(
        JNIEnv* /* env */, jclass /* clazz */)
{
    return (sGpsInterface != NULL) ?  JNI_TRUE : JNI_FALSE;
}

如果可以開啟so庫,獲取sGpsInterface結構體就說明支援GPS,否則就不支援。


小結:主要論述了Java層,JNI層以及so庫的回撥方法的實現,這樣就打通了架構中方法的互相呼叫。

其實, com_android_server_location_GpsLocationProvide只是Framework和HAL之間的一個橋樑。

相關文章