看Android的GPS模块有两个月了吧,终于可以写点东西出来。首先来看看GPS模块的代码结构:
Framework:
1.frameworks/base/location/java/android/location
这里主要是用来被App调用的,API包是android.location。
2.frameworks/base/location/java/com/android/internal/location
这个目录是Framework对Location服务的内部实现。
3.framework/services/java/com/android/server
这个目录只有一个文件
|-- LocationManagerService.java
是Location服务对内部实现的一种封装。
JNI:
frameworks/base/core/jni/android_location_GpsLocationProvider.cpp
JNI层只有一个文件,起到承上启下的作用。上层承接Framework,下层调用HAL层具体硬件抽象实现。
HAL:Hardware Abstract Layer 硬件抽象层
hardware/libhardware_legacy/gps
hardware/libhardware_legacy/include/hardware_legacy/gps.h
HAL层相当于一个linux应用程序接口,通过open,close等操作,操作硬件设备。Android的源代码只实现了模拟器的gps接口,具体在文件gps_qemu.c中。在2.2版本中提供了对QCOM公司的gps的实现,在以下目录:
/hardware/qcom
下面介绍几个重要的数据结构:
1. GpsInterface接口是gps模块中最重要的数据结构,它是底层驱动实现的接口,如果要porting到自己的板子上,就需要实现这些接口。该接口的定义在gps.h中,模拟器实现在gps_qemu.c中。
/** Represents the standard GPS interface. */ typedef struct { /** * Opens the interface and provides the callback routines * to the implemenation of this interface. */ int (*init)( GpsCallbacks* callbacks ); /** Starts navigating. */ int (*start)( void ); /** Stops navigating. */ int (*stop)( void ); /** Closes the interface. */ void (*cleanup)( void ); /** Injects the current time. */ int (*inject_time)(GpsUtcTime time, int64_t timeReference, int uncertainty); /** Injects current location from another location provider * (typically cell ID). * latitude and longitude are measured in degrees * expected accuracy is measured in meters */ int (*inject_location)(double latitude, double longitude, float accuracy); /** * Specifies that the next call to start will not use the * information defined in the flags. GPS_DELETE_ALL is passed for * a cold start. */ void (*delete_aiding_data)(GpsAidingData flags); /** * fix_frequency represents the time between fixes in seconds. * Set fix_frequency to zero for a single-shot fix. */ int (*set_position_mode)(GpsPositionMode mode, int fix_frequency); /** Get a pointer to extension information. */ const void* (*get_extension)(const char* name); } GpsInterface; |
2. GpsCallbacks回调函数
这个是回调函数结构体,定义也在gps.h中。它们的实现是在android_location_GpsLocationProvider.cpp中,google已经实现了,我们不需要做任何动作。
/** GPS callback structure. */ typedef struct { gps_location_callback location_cb; gps_status_callback status_cb; gps_sv_status_callback sv_status_cb; gps_nmea_callback nmea_cb; } GpsCallbacks; /** Callback with location information. */ typedef void (* gps_location_callback)(GpsLocation* location); /** Callback with status information. */ typedef void (* gps_status_callback)(GpsStatus* status); /** Callback with SV status information. */ typedef void (* gps_sv_status_callback)(GpsSvStatus* sv_info); /** Callback for reporting NMEA sentences. */ typedef void (* gps_nmea_callback)(GpsUtcTime timestamp, const char* nmea, int length); |
3. GpsLocation
表示Locatin数据信息,底层驱动获得Location的raw信息,通常是nmea码,然后通过解析就得到了location信息。
/** Represents a location. */
typedef struct {
/** Contains GpsLocationFlags bits. */
uint16_t flags;
/** Represents latitude in degrees. */
double latitude;
/** Represents longitude in degrees. */
double longitude;
/** Represents altitude in meters above the WGS 84 reference
* ellipsoid. */
double altitude;
/** Represents speed in meters per second. */
float speed;
/** Represents heading in degrees. */
float bearing;
/** Represents expected accuracy in meters. */
float accuracy;
/** Timestamp for the location fix. */
GpsUtcTime timestamp;
} GpsLocation;
initialize函数
LocationManagerService.java[frameworks/base/services/java/com/android/server]
private void initialize() { // Create a wake lock, needs to be done before calling loadProviders() below PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); // Load providers loadProviders(); ... |
initialize函数中最重要的就是loadProviders函数了,该函数调用loadProvidersLocked,然后loadProvidersLocked函数又调用_loadProvidersLocked函数。为什么要这么折腾呢?
先来看一部分的_loadProvidersLocked函数:
private void _loadProvidersLocked() { // Attempt to load "real" providers first if (GpsLocationProvider.isSupported()) { // Create a gps location provider GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this); mGpsStatusProvider = gpsProvider.getGpsStatusProvider(); mNetInitiatedListener = gpsProvider.getNetInitiatedListener(); addProvider(gpsProvider); mGpsLocationProvider = gpsProvider; } ... |
注意这个if语句,狠重要,因为在这个语句中得到了HAL层的GPS接口GpsInterface。就是通过调用GpsLocationProvider的isSupported()函数才调用到gps.cpp[hardware/libhardware_legacy/gps]中的gps_get_interface()。这个isSupported函数才是第一个吃螃蟹的人。(而不是JNI层的init函数,这个下面会提到)。
GpsLocationProvider.cpp [frameworks/base/location/java/com/android/internal/location]
public static boolean isSupported() { return native_is_supported(); } |
然而isSupported只有一句话,果然是高手,一击必中。然后就调用native方法,也就是JNI层定义的方法。native_is_supported函数对于JNI层是android_location_GpsLocationProvider_is_supported方法。
android_location_GpsLocationProvider.cpp [frameworks/base/core/jni]
static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, jclass clazz) { if (!sGpsInterface) sGpsInterface = gps_get_interface(); return (sGpsInterface != NULL); } |
前面已经提到JNI起到承上启下的作用,gps_get_interface函数属于HAL层的调用,在文件gps.cpp中。
gps.cpp [hardware/libhardware_legacy/gps]
const GpsInterface* gps_get_interface() { if (sGpsInterface == NULL) gps_find_hardware(); return sGpsInterface; } |
然后通过
gps_find_hardware函数去得到gps接口,下面只模拟器中的gpsinterface。
static void gps_find_hardware( void ) { #ifdef HAVE_QEMU_GPS_HARDWARE if (qemu_check()) { sGpsInterface = gps_get_qemu_interface(); if (sGpsInterface) { LOGD("using QEMU GPS Hardware emulation/n"); return; } } #endif #ifdef HAVE_GPS_HARDWARE sGpsInterface = gps_get_hardware_interface(); #endif if (!sGpsInterface) LOGD("no GPS hardware on this device/n"); } |
gps_qemu.c [hardware/libhardware_legacy/gps]
const GpsInterface* gps_get_qemu_interface() { return &qemuGpsInterface; } |
qemuGpsInterface的整体实现就在文件gps_qemu.c中。
static const GpsInterface qemuGpsInterface = {
qemu_gps_init,
qemu_gps_start,
qemu_gps_stop,
qemu_gps_cleanup,
qemu_gps_inject_time,
qemu_gps_inject_location,
qemu_gps_delete_aiding_data,
qemu_gps_set_position_mode,
qemu_gps_get_extension,
};
--------------------------------------------------------------------------------
在底层得到gps的接口之后, if (GpsLocationProvider.isSupported())(在文件LocationManagerService.java中调用)语句得到true,然后进行下一步操作,在这里new了一个GpsLocationProvider对象。代码如下:
GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this); |
注意GpsLocationProvider构造函数里面的两个参数:mContext, this。下面来看看GpsLocationProvider的构造函数的前面几句:
public GpsLocationProvider(Context context, ILocationManager locationManager) { mContext = context; mLocationManager = locationManager; mNIHandler = new GpsNetInitiatedHandler(context, this); ... } |
在GpsLocationProvider类里面的成员变量mLocationManager是构造函数的第二个参数,就是说是LocationManagerService对象。这一点在这里先明确。
接着看_loadProvidersLocked函数。
private void _loadProvidersLocked() { // Attempt to load "real" providers first if (GpsLocationProvider.isSupported()) { // Create a gps location provider GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this); mGpsStatusProvider = gpsProvider.getGpsStatusProvider(); mNetInitiatedListener = gpsProvider.getNetInitiatedListener(); addProvider(gpsProvider); mGpsLocationProvider = gpsProvider; } // create a passive location provider, which is always enabled PassiveProvider passiveProvider = new PassiveProvider(this); addProvider(passiveProvider); mEnabledProviders.add(passiveProvider.getName()); // initialize external network location and geocoder services Resources resources = mContext.getResources(); String serviceName = resources.getString( com.android.internal.R.string.config_networkLocationProvider); if (serviceName != null) { mNetworkLocationProvider = new LocationProviderProxy(mContext, LocationManager.NETWORK_PROVIDER, serviceName, mLocationHandler); addProvider(mNetworkLocationProvider); } serviceName = resources.getString(com.android.internal.R.string.config_geocodeProvider); if (serviceName != null) { mGeocodeProvider = new GeocoderProxy(mContext, serviceName); } updateProvidersLocked(); } |
在构造完GpsLocationProvider之后将其add到全局变量ArrayList<LocationProviderInterface> mProviders中,备以后调用。
在2.2中采取了一种PassiveProvider的类,而在2.1中是通过LocationProviderProxy代理类的方式。2.1中LocationProviderProxy作为GpsLocationProvider的代理作用在LocationManagerService中,而2.2中的PassiveProvider感觉这个类是个空壳。。。。。。。。有待研究。
然后启动了nerwork location和geocoder 两个service。但是可惜的是这两个服务都无法启动,因为他们是通过配置文件conifg.xml [framework/base/core/res/res/values]得到服务的名字,然后启动服务的。但是在这个配置文件中,两个服务的名字都是null。
conifg.xml [framework/base/core/res/res/values]
<!-- Component name of the service providing network location support. --> <string name="config_networkLocationProvider">@null</string> <!-- Component name of the service providing geocoder API support. --> <string name="config_geocodeProvider">@null</string> |
其实这也导致了,在调用GetFromLocationName和GetFromLocation两个函数时提示“Service not Available”,这个google Android 2.2的bug。
_loadProvidersLocked函数的最后一句是调用updateProvidersLocked函数,仍然在LocationManagerServic.java文件中。
LocationManagerServic.java
private void updateProvidersLocked() { for (int i = mProviders.size() - 1; i >= 0; i--) { LocationProviderInterface p = mProviders.get(i); boolean isEnabled = p.isEnabled(); String name = p.getName(); boolean shouldBeEnabled = isAllowedBySettingsLocked(name); if (isEnabled && !shouldBeEnabled) { updateProviderListenersLocked(name, false); } else if (!isEnabled && shouldBeEnabled) { updateProviderListenersLocked(name, true); } } } |
从上面_loadProvidersLocked函数的代码来看,在mProviders这个ArrayList中有两个元素(这一点未求证),一个是gpsProvider,另一个是passiveProvider。gpsProvider是GpsLocationProvider类型的,它的isEnabled函数返回的是false,因为它并没有被enable。而passiveProvider是PassiveProvider类型,它总是enable的。所以gpsProvider会调用else语句中的updateProviderListenersLocked(name, true)函数。我们主要分析这个else语句,对于passiveProvider不做分析。
言归正传,分析sGpsInterface->init方法。
gps_qume.c
static int qemu_gps_init(GpsCallbacks* callbacks) { GpsState* s = _gps_state; if (!s->init) gps_state_init(s); if (s->fd < 0) return -1; s->callbacks = *callbacks; return 0; } |
在
sGpsInterface->init中,也就是在qemu_gps_init方法,首先调用了gps_state_init,其次注册了回调函数,再说一次,这个回调函数就是在JNI层实现的,而且有JNI层传下来的函数。
static void gps_state_init( GpsState* state ) { state->init = 1; state->control[0] = -1; state->control[1] = -1; state->fd = -1; state->fd = qemu_channel_open( &state->channel, QEMU_CHANNEL_NAME, O_RDONLY ); if (state->fd < 0) { D("no gps emulation detected"); return; } D("gps emulation will read from '%s' qemud channel", QEMU_CHANNEL_NAME ); if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) { LOGE("could not create thread control socket pair: %s", strerror(errno)); goto Fail; } if ( pthread_create( &state->thread, NULL, gps_state_thread, state ) != 0 ) { LOGE("could not create gps thread: %s", strerror(errno)); goto Fail; } D("gps state initialized"); return; Fail: gps_state_done( state ); } |
在这个gps_state_init函数中,首先打开串口,然后建立socket通信,然后建立线程监听底层数据上报,分别对应于代码中黄低部分。
3)建立线程监听事件
mEventThread = new GpsEventThread(); mEventThread.start(); |
来看看GpsEventThread的run函数。
public void run() { if (DEBUG) Log.d(TAG, "GpsEventThread starting"); // Exit as soon as disable() is called instead of waiting for the GPS to stop. while (mEnabled) { // this will wait for an event from the GPS, // which will be reported via reportLocation or reportStatus native_wait_for_event(); } if (DEBUG) Log.d(TAG, "GpsEventThread exiting"); } } |
run函数中还是需要调用native函数:JNI:android_location_GpsLocationProvider_wait_for_event函数。这个函数就是在一个while循环里面等待事件的触发(由回调函数触发),然后调用GpsLocationProvider类的数据上报函数(Location数据)。这个在后面还会讲到。
static void android_location_GpsLocationProvider_wait_for_event(JNIEnv* env, jobject obj) { pthread_mutex_lock(&sEventMutex); while (sPendingCallbacks == 0) { pthread_cond_wait(&sEventCond, &sEventMutex); } ... }
|
分析完了enable函数以后就轮到enableLocationTracking函数了。
GpsLocationProvider.java
public void enableLocationTracking(boolean enable) { synchronized (mHandler) { mHandler.removeMessages(ENABLE_TRACKING); Message m = Message.obtain(mHandler, ENABLE_TRACKING); m.arg1 = (enable ? 1 : 0); mHandler.sendMessage(m); } } |
同样地,也采取Handler的方式。调用的是handleEnableLocationTracking函数。
private void handleEnableLocationTracking(boolean enable) { if (enable) { mTTFF = 0; mLastFixTime = 0; startNavigating(); } else { mAlarmManager.cancel(mWakeupIntent); mAlarmManager.cancel(mTimeoutIntent); stopNavigating(); } } |
调用startNavigating函数。
private void startNavigating() { if (!mStarted) { if (DEBUG) Log.d(TAG, "startNavigating"); mStarted = true; int positionMode; if (Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ASSISTED_GPS_ENABLED, 1) != 0) { positionMode = GPS_POSITION_MODE_MS_BASED; } else { positionMode = GPS_POSITION_MODE_STANDALONE; } if (!native_start(positionMode, false, 1)) { mStarted = false; Log.e(TAG, "native_start failed in startNavigating()"); return; } ... |
在startNavigating函数中,最有作用的语句就是调用native方法native_start。调用到了JNI层的android_location_GpsLocationProvider_start函数。
android_location_GpsLocationProvider.cpp
在startNavigating函数中,最有作用的语句就是调用native方法native_start。调用到了JNI层的android_location_GpsLocationProvider_start函数。
android_location_GpsLocationProvider.cpp
static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject obj, jint positionMode, jboolean singleFix, jint fixFrequency) { int result = sGpsInterface->set_position_mode(positionMode, (singleFix ? 0 : fixFrequency)); if (result) { return false; } return (sGpsInterface->start() == 0); } |
接下去就会调用sGpsInterface接口的实现gps_qemu.c中具体实现的函数。
static int qemu_gps_start() { GpsState* s = _gps_state; if (!s->init) { D("%s: called with uninitialized state !!", __FUNCTION__); return -1; } D("%s: called", __FUNCTION__); gps_state_start(s); return 0; } |
通过向底层发送命令,CMD_START来启动gps。其实这个所谓的底层就是在enable/init函数中启动的等待数据的线程。
static void gps_state_start( GpsState* s ) { char cmd = CMD_START; int ret; do { ret=write( s->control[0], &cmd, 1 ); } while (ret < 0 && errno == EINTR); if (ret != 1) D("%s: could not send CMD_START command: ret=%d: %s", __FUNCTION__, ret, strerror(errno)); } |
数据监听线程
static void* gps_state_thread( void* arg ) { ... // now loop for (;;) { ... if (cmd == CMD_QUIT) { D("gps thread quitting on demand"); goto Exit; }else if (cmd == CMD_START) { if (!started) { D("gps thread starting location_cb=%p", state>callbacks.location_cb); started = 1; nmea_reader_set_callback( reader, state->callbacks.location_cb ); } } else if (cmd == CMD_STOP) { ... } |
其实就是注册了一个回调函数,location_cb 这个回调函数就是对底层location数据上报的回调函数。
在enableLocationTracking函数调用完成以后,基本上gps服务已经启动完成了,也就是LocationManagerService中的updateProvidersLocked函数的完成,也就是loadProviders函数的完成,也就是initialize函数的完成,也就是run函数的完成,也就是2.2中反馈机制systemReady的完成。
void systemReady() {
// we defer starting up the service until the system is ready
Thread thread = new Thread(null, this, "LocationManagerService");
thread.start();
}