Android GPS架构分析
来源:Linux社区 作者:Daniel Wood
通过调用GpsLocationProvider类的enable和enableLocationTracking函数就把GPS的LocationManager服务启动起来了。下面对这两个函数进行分析。
首先是enable函数。
GpsLocationProvider.java
public void enable() { synchronized (mHandler) { mHandler.removeMessages(ENABLE); Message m = Message.obtain(mHandler, ENABLE); m.arg1 = 1; mHandler.sendMessage(m); } } |
对了,这个要提一点,在2.2中加入了一个ProviderHandler类(extends Handler),这个在2.1中是没有的,其实是换汤不换药的,对于函数调用的过程来说没有本质的改变。对于Handler的机制我还没有研究过。
public void handleMessage(Message msg) { switch (msg.what) { case ENABLE: if (msg.arg1 == 1) { handleEnable(); } else { handleDisable(); } break; case ENABLE_TRACKING: handleEnableLocationTracking(msg.arg1 == 1); break; ... |
在
handleMessage函数中,定义了各种message对应的处理函数。对于ENABLE消息还带有一个参数,enable函数里面带的参数值为1,所以调用handleEnable函数。
private void handleEnable() { if (DEBUG) Log.d(TAG, "handleEnable"); if (mEnabled) return; mEnabled = native_init(); if (mEnabled) { if (mSuplServerHost != null) { native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort); } if (mC2KServerHost != null) { native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort); } // run event listener thread while we are enabled mEventThread = new GpsEventThread(); mEventThread.start(); } else { Log.w(TAG, "Failed to enable location provider"); } } |
在handleEnable函数中中主要做了3件事,不过有一件事情没有做成。先来看看哪三件事:
1)调用了native的初始化方法对gps进行初始化,
2)试图启动agps服务,
3)并启动一个线程去监听事件。
先来说说它没有做成的第二件事,启动agps服务。其实在GpsLocationProvider类构造的时候就试图去读取agps的配置文件"/etc/gps.conf",该文件里面储存着agps的服务器地址以及端口号,但是服务器地址以及端口号都是错误的,所以它基本上无法启动agps服务,而且对模拟器来说agps基本是个鸡肋。关于agps部分可能在以后的以后会提到。下面看它做成的第一和第三件事。
1)调用native方法native_init,就是JNI层的Android_location_GpsLocationProvider_init方法,在文件andoird_location_GpsLocationProvider.cpp中。
static jboolean Android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj) { if (!sGpsInterface) sGpsInterface = gps_get_interface(); if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0) return false; ... return true; } |
在初始化函数中会去确认GpsInterface是否已经得到,如果没有得到那么通过gps_get_interface()方法再次去得到,正如其实前面提到的那样该接口已经在Android_location_GpsLocationProvider_is_supported函数(第一个吃螃蟹的人)中得到了。然后在第二个if语句中调用初始化方法sGpsInterface->init。
Android_location_GpsLocationProvider_init的后半部分,试图通过GpsInterface->get_extension方法去得到gps相关的扩展接口,可是在2.2的模拟器实现中并没有实现这个函数,在gps_qume.c中明显写着return NULL。
gps_qume.c
static const void* qemu_gps_get_extension(const char* name) { return NULL; } |
言归正传,分析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
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(); } |
Gps启动过程图(基于Google Android 2.2代码)
下面再贴一张从GoogleI/O大会文档里面截来的图

完了 感谢作者 Daniel Wood
http://www.linuxidc.com/Linux/2011-08/40123p7.htm