Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android在framework層增加自己的service---仿照GPS

android在framework層增加自己的service---仿照GPS

編輯:關於Android編程

     不少公司在開發android產品的時候,都需要在android中增加自己的service,尤其是定制的工業用途的設備,我們公司的項目中就涉及到要增加一個service,是一個北斗通信service,具體的內容不便透露,涉及到保密。但是增加service的過程大概能描述一下,具體代碼就按著不重要的來帖,大家見諒!!

其實增加自己的service不論是誰來,我想都會仿照現有service來做,在android現有service中,最簡單明了的是vibrator service,其次的是location service也就是GPS,這兩個service 雖然簡單,但是service的架構都是相同,我們仿照的目標就是要這樣,明了的架構,往裡面填東西就是體力活了。下面我們從下往上一一來看每個步驟。

1.kernel層

我們的硬件連接到設備上一個串口,因此kernel層我們就不用什麼改動。當然如果你們添加的硬件設備需要驅動的話,自己加進去就是了,這裡不多說了。

2.HAL層

我們先找到了GPS的HAL層代碼 \android\hardware\imx\libgps中一共兩個文件,仔細分析來看,這兩個文件主要功能是生成一個動態鏈接庫,向下與硬件通信,向上為系統提供訪問的接口函數,知道了這個,我們就明白了HAL層大概的功能,我們的工作就是選一個適合自己硬件的方法實現這個功能。

我們的硬件是一個串口通信的設備,無非就是可以由上層控制來發送命令和接受命令,因此我選的是串口通信很常見的方式: 每條發送命令都單獨寫一個函數,有上層來控制發送哪條。接收命令就啟動一個接收線程,每當收到數據後,進行一系列的判定,把有效的數據傳送到上層。

下面我們來看具體代碼在android\hardware\imx\librd目錄中:

首先是我們與硬件通信的幾個函數:

static const RDInterface  goldtelrdInterface = {
	sizeof(RDInterface),
	goldtel_rd_init,
	goldtel_rd_close,
	goldtel_rd_send_XTZJ,
	goldtel_rd_send_ICJC,
	goldtel_rd_send_SJSC,
	goldtel_rd_send_DWSQ,
	goldtel_rd_send_BBDQ,
	goldtel_rd_send_YHZL_SJTX,
	goldtel_rd_send_TXSQ,
};

具體的函數功能就不多說了,反正就是發送命令初始化和關閉。當上層打開這個service的時候會調用goldtel_rd_init()這個函數,因此在這個函數裡面我們要實現一系列的初始化功能,包括打開設備電源,打開串口,創建接收進程等等。具體代碼:

static int goldtel_rd_init(RDCallbacks* callbacks)
{
	RdState*  s = _rd_state;
	//lijianzhang
	write_sysfs("/sys/devices/platform/bd_power/enable_rdss","1",1); //給設備上電
	usleep(1500000);
	if (!s->init)
		rd_state_init(s, callbacks);//進行一系列的初始化 注意這裡的一個參數callbacks,
									//這是一些回調函數,是在jni層實現的,傳到底層來運行
									//android很多都是這麼來實現,如果以後看別的代碼看到
									//類似方式就不要慌亂,去jni層裡肯定能找到

	if (s->fd < 0)
		return -1;
    
	rd_state_start(s); //開始工作

	return 0;
}

然後是rd_state_init()

static void
rd_state_init( RdState*  state, RDCallbacks* callbacks )
{
	.................
	一系列初始化
	。。。。。。。。。。。。。。。。。。。
	state->thread = callbacks->create_thread_cb( "rd_state_thread", rd_state_thread, state );//這個函數裡最主要的功能就是創建接收線程

	。。。。。。。。。。。。。。。。。。。
	。。。。。。。。。。。。。。。。。。。
}
下面來看接收線程裡的東西:
這裡面用到了很多的epoll通信機制,其實邏輯很簡單一看就明白,下面把代碼貼出來,大家給個參考指正

static void
rd_state_thread( void*  arg )
{
	RdState*   state = (RdState*) arg;
	NmeaReader  reader[1];
	int         epoll_fd   = epoll_create(2);
	int         started    = 0;
	int         rd_fd     = state->fd;
	int         control_fd = state->control[1];

	nmea_reader_init( reader );

	// 注冊epoll 文件
	epoll_register( epoll_fd, control_fd );
	epoll_register( epoll_fd, rd_fd );

	LOGE("RD thread running");

	// now loop
	for (;;) {
		struct epoll_event   events[2];
		int                  ne, nevents;

		nevents = epoll_wait( epoll_fd, events, 2, -1 ); //等待epoll消息
		if (nevents < 0) {
			if (errno != EINTR)
				E("epoll_wait() unexpected error: %s", strerror(errno));
			continue;
		}
		D("rd thread received %d events", nevents);
		for (ne = 0; ne < nevents; ne++) {	//處理每條消息
			if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) {
				E("EPOLLERR or EPOLLHUP after epoll_wait() !?");
				return;
			}
			if ((events[ne].events & EPOLLIN) != 0) {
				int  fd = events[ne].data.fd;

				if (fd == control_fd)	//如果這個消息是控制命令的話,這裡的控制命令其實就兩條,
										//服務開始和服務結束,下面就是分別對著兩條命令進行處理
				{
					char  cmd = 255;
					int   ret;
					D("rd control fd event");
					do {
						ret = read( fd, &cmd, 1 );
					} while (ret < 0 && errno == EINTR);

					if (cmd == CMD_QUIT) {	//服務退出
						if (started) {

							started = 0;
							nmea_reader_set_DWXX_callback(reader,NULL);
							nmea_reader_set_BBXX_callback(reader,NULL);
							nmea_reader_set_FKXX_callback(reader,NULL);
							nmea_reader_set_ICXX_callback(reader,NULL);
							nmea_reader_set_ZJXX_callback(reader,NULL);
							nmea_reader_set_TXHZ_callback(reader,NULL);
							nmea_reader_set_TXXX_callback(reader,NULL);	
						}
						return;
					}
					else if (cmd == CMD_START) {	//服務開始
						if (!started) {
							LOGE("rd_state_thread start!");
							started = 1;
							nmea_reader_set_DWXX_callback(reader,state->callbacks.dwxx_cb);
						    nmea_reader_set_BBXX_callback(reader,state->callbacks.bbxx_cb);
						    nmea_reader_set_FKXX_callback(reader,state->callbacks.fkxx_cb);
						    nmea_reader_set_ICXX_callback(reader,state->callbacks.icxx_cb);
						    nmea_reader_set_ZJXX_callback(reader,state->callbacks.zjxx_cb);
						    nmea_reader_set_TXHZ_callback(reader,state->callbacks.txhz_cb);
						    nmea_reader_set_TXXX_callback(reader,state->callbacks.txxx_cb);
						}
					}
					
				}
				else if (fd == rd_fd)	//如果是串口通信的消息
				{
				   // LOGE("start read data");
					
					char  buff[32];
				
					for (;;) {
						int  nn, ret;

						ret = read( fd, buff, sizeof(buff) );	//從串口中讀出數據
						if (ret < 0) {
							if (errno == EINTR)
								continue;
							if (errno != EWOULDBLOCK)
								E("error while reading from gps daemon socket: %s:", strerror(errno));
							break;
						}
						
						for (nn = 0; nn < ret; nn++)
						
                         {		
                           //LOGE("start read data %02X",buff[nn]);						 
						   nmea_reader_addc( reader, buff[nn] );	//判定數據有效性並進行處理
						}
					}
				
				}
				else
				{
					E("epoll_wait() returned unkown fd %d ?", fd);
				}
			}
		}
	}

 }

下面重要的函數就是nmea_reader_addc( NmeaReader* r, int c ) 這裡作為涉密內容不做過多的說明了,明白就是判定命令並與上層通信就可以了。

進程創建完成了,hal層基本功能就完成了,下一步就是將這些功能聲稱一個.so動態鏈接庫,方法就是下面的代碼:

static const RDInterface* get_rd_hardware_interface()
{
	return &goldtelrdInterface;
}

static int open_rd(const struct hw_module_t* module, char const* name,
        struct hw_device_t** device)
{
    struct rd_device_t *dev = malloc(sizeof(struct rd_device_t));
    memset(dev, 0, sizeof(*dev));


    dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = 0;
    dev->common.module = (struct hw_module_t*)module;
    dev->get_rd_interface = get_rd_hardware_interface; 

    *device = (struct hw_device_t*)dev;
    return 0;
}


static struct hw_module_methods_t rd_module_methods = {
    .open = open_rd
};

const struct hw_module_t HAL_MODULE_INFO_SYM = {
    .tag = HARDWARE_MODULE_TAG,
    .version_major = 1,
    .version_minor = 0,
    .id = RD_HARDWARE_MODULE_ID,
    .name = "Real6410 rd Module",
    .author = "The Android Open Source Project",
    .methods = &rd_module_methods,
};

3.JNI層

動態鏈接庫生成完了,下面就到了framework層,我們找到了location service的代碼在目錄\android\frameworks\base\services\jni\中功能就是打開這個動態鏈接庫然後,向java層提供函數接口,功能很簡單我們直接來看我寫的代碼在android\\frameworks\base\services\jni\com_android_server_rdmessage_RDMessageDispatch.cpp中

首先是打開動態鏈接庫

static const RDInterface* get_rd_interface() {
    int err;
    hw_module_t* module;
    const RDInterface* interface = NULL;

    err = hw_get_module(RD_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
    if (err == 0) {
        hw_device_t* device;
        err = module->methods->open(module, RD_HARDWARE_MODULE_ID, &device);
        if (err == 0) {
            rd_device_t* rd_device = (rd_device_t *)device;
            interface = rd_device->get_rd_interface(rd_device);
        }
    }

    return interface;
}
最後的是想上層提供的函數接口

static JNINativeMethod sMethods[] = {
	/* name, signature, funcPtr */
	{"class_init_native", "()V", (void *)android_rdmessage_RDMessageDispatch_class_init_native},
        {"native_is_supported","()Z",(void*)android_rdmessage_RDMessageDispatch_is_supported},
	{"native_BDMessage_start", "()Z", (void*)android_rdmessage_RDMessageDispatch_native_BDMessage_start},
	{"native_close","()V",(void*)android_rdmessage_RDMessageDispatch_native_close},
	{"native_sendXTZJ", "(I)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendXTZJ},
	{"native_sendICJC", "()Z", (void*)android_rdmessage_RDMessageDispatch_native_sendICJC},
	{"native_sendSJSC", "(I)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendSJSC},
	{"native_sendDWSQ", "(ZZ)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendDWSQ},
	{"native_sendBBDQ", "()Z", (void*)android_rdmessage_RDMessageDispatch_native_sendBBDQ},
	{"native_sendYHZL_SJTX", "(II)Z", (void*)android_rdmessage_RDMessageDispatch_native_sendYHZL_SJTX},
	{"native_SendBDMessage", "(I[BIIZ)Z", (void*)android_rdmessage_RDMessageDispatch_native_SendBDMessage},
	{"read_TXXX_message", "([BI)I", (void*)android_rdmessage_RDMessageDispatch_read_TXXX_message},
        {"read_DWXX_message","([B[B[B[B[B)V",(void*)android_rdmessage_RDMessageDispatch__read_DWXX_message},	
};
中間就是實現這兩者之間的轉換,我們來說轉換過程中比較重要的幾點首先是發送命令,很簡單,就是直接調用動態鏈接庫中提供的發送函數,看代碼

static jboolean  android_rdmessage_RDMessageDispatch_native_sendICJC
  (JNIEnv *env, jobject obj)
{
	
	if (!sRdInterface)
		return false;

	if(sRdInterface->rd_send_ICJC()!=0)
	return false;
	
	return true;

}

然後是,收到串口數據,傳送的上層的方法,這裡就涉及到了剛才提到的回調函數,在這裡實現但是在HAL層執行,函數含簡單就是調用函數將數據發送上去,看代碼

 static void ZJXX_callback(ZJXXInfo *zjxx)
 {
	 JNIEnv* env = AndroidRuntime::getJNIEnv();
    
	 env->CallVoidMethod(mCallbacksObj,method_reportZJXX,zjxx->UserId,zjxx->ICStatus,
		 zjxx->YJStatus,zjxx->DCStatus,zjxx->RZStatus,zjxx->bs1Status,zjxx->bs2Status,
		 zjxx->bs3Status,zjxx->bs4Status,zjxx->bs5Status,zjxx->bs6Status);
	 checkAndClearExceptionFromCallback(env, __FUNCTION__);
 }

其他的就不多說了下面來看framework層

4. framework層

我們看到location service的代碼在android\frameworks\base\services\java\com\android\server\LocationManagerService.java

android\frameworks\base\services\java\com\android\server\location目錄 android\\frameworks\base\location\java\android\location目錄中

分析一下架構,就是向下接口JNI層的 代碼,在此基礎上封裝service實現函數,最後在servicemanager中運行這個服務,還有增加這個服務的aidl文件,以便於所有的app都能訪問到。所以我們所做的工作也是這些,其實比較簡單,主要是為界面提供支持,比如更新狀態欄北斗信號強度等等,這裡就不再貼代碼了,大家可以自己看gps是怎麼做的仿照來就行。

實現了這些函數最後要在android\frameworks\base\services\java\com\android\server\SystemServer.java中增加

	   try {		//啟動這個service
                Slog.i(TAG, "RdMessage Manager");
                rdmessage = new RdMessageManagerService(context);
                ServiceManager.addService(Context.RD_MESSAGE_SERVICE, rdmessage);
            } catch (Throwable e) {
                reportWtf("starting RdMessage Manager", e);
            }


		try {
                    if (rdmessageF != null) rdmessageF.systemReady();	//告訴系統服務啟動完成
                } catch (Throwable e) {
                    reportWtf("making RdMessage Service ready", e);
                }

增加這些代碼後就系統就可以啟動服務了,
最後在\frameworks\base\core\java\android\os\增加IRdMessageManager.aidl, RdMessageManager.java這兩個文件,是提供給上層使用的短信服務。

到了這裡整個service添加就完成了,我們就可以通過app來訪問這個service了。


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved