2014年8月14日 星期四

(5)Android硬體抽象層(HAL)模組編寫JNI方法提供Java訪問硬體服務接口

在上兩篇文章中,我們介紹了如何為Android系統的硬體編寫驅動程式,包括如何在Linux核心空間實現核心驅動程式和在用戶空間實現硬體抽象層接口。實現這兩者的目的是為了向更上一層提供硬體訪問接口,即為Android的Application Frameworks層提供硬體服務。我們知道,Android系統的應用程式是用Java語言編寫的,而硬體驅動程式是用C語言來實現的,那麼,Java接口如何去訪問C接口呢?眾所周知,Java提供了JNI方法調用,同樣,在Android系統中,Java應用程式通過JNI來調用硬體抽象層接口。在這一篇文章中,我們將介紹如何為Android硬體抽象層接口編寫JNI方法,以便使得上層的Java應用程式能夠使用下層提供的硬體服務。

一. 參照在Ubuntu上為Android增加硬體抽象層(HAL)模組訪問Linux核心驅動程式一文,準備好硬體抽象層模組,確保Android系統鏡像文件system.img已經包含hello.default模組。

二. 進入到frameworks/base/services/jni目錄,新建com_android_server_HelloService.cpp文件:

USER-NAME@MACHINE-NAME:~/Android$ cd frameworks/base/services/jni
USER-NAME@MACHINE-NAME:~/Android/frameworks/base/services/jni$vi com_android_server_HelloService.cpp

     在com_android_server_HelloService.cpp文件中,實現JNI方法。注意文件的命令方法,com_android_server前綴表示的是包名,表示硬體服務HelloService是放在frameworks/base/services/java目錄下的com/android/server目錄的,即存在一個命令為com.android.server.HelloService的類。這裡,我們暫時略去HelloService類的描述,在下一篇文章中,我們將回到HelloService類來。簡單地說,HelloService是一個提供Java接口的硬體訪問服務類。

     首先是包含相應的頭文件:

#define LOG_TAG "HelloService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/hello.h>
#include <stdio.h>

接著定義hello_init、hello_getVal和hello_setVal三個JNI方法:
namespace android
{
/*在硬體抽象層中定義的硬體訪問結構體,參考<hardware/hello.h>*/
       struct hello_device_t* hello_device = NULL;
/*通過硬體抽象層定義的硬體訪問接口寫入硬體暫存器val的值*/
       static void hello_setVal(JNIEnv* env, jobject clazz, jint value) {
int val = value;
LOGI("Hello JNI: set value %d to device.", val);
if(!hello_device) {
LOGI("Hello JNI: device is not open.");
return;
}
hello_device->set_val(hello_device, val);
}
       /*通過硬體抽象層定義的硬體訪問接口讀取硬體暫存器val的值*/
static jint hello_getVal(JNIEnv* env, jobject clazz) {
int val = 0;
if(!hello_device) {
LOGI("Hello JNI: device is not open.");
return val;
}
hello_device->get_val(hello_device, &val);
LOGI("Hello JNI: get value %d from device.", val);
return val;
}
       /*通過硬體抽象層定義的硬體模組打開接口打開硬體設備*/
static inline int hello_device_open(const hw_module_t* module, struct hello_device_t** device) {
return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);  //open HAL設備 寫法固定
}
       /*通過硬體模組ID來初始化指定的硬體抽象層模組並打開硬體*/
static jboolean hello_init(JNIEnv* env, jclass clazz) { //固定寫法
hello_module_t* module; //固定寫法
LOGI("Hello JNI: initializing......");
if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {
LOGI("Hello JNI: hello Stub found.");
if(hello_device_open(&(module->common), &hello_device) == 0) {
LOGI("Hello JNI: hello device is open.");
return 0;
}
LOGE("Hello JNI: failed to open hello device.");
return -1;
}
LOGE("Hello JNI: failed to get hello stub module.");
return -1;
}
       /*JNI方法表*/
static const JNINativeMethod method_table[] = {
{"init_native", "()Z", (void*)hello_init},
{"setVal_native", "(I)V", (void*)hello_setVal},
{"getVal_native", "()I", (void*)hello_getVal},
};
       /*註冊JNI方法*/
int register_android_server_HelloService(JNIEnv *env) {
    return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table));
}
};

     注意,在hello_init函數中,通過Android硬體抽象層提供的hw_get_module方法來加載模組ID為HELLO_HARDWARE_MODULE_ID的硬體抽象層模組,其中,HELLO_HARDWARE_MODULE_ID是在<hardware/hello.h>中定義的。Android硬體抽象層會根據HELLO_HARDWARE_MODULE_ID的值在Android系統的/system/lib/hw目錄中找到相應的模組,然後加載起來,並且返回hw_module_t接口給調用者使用。在jniRegisterNativeMethods函數中,第二個參數的值必須對應HelloService所在的包的路徑,即com.android.server.HelloService。

     三. 修改同目錄下的onload.cpp文件,首先在namespace android增加register_android_server_HelloService函數聲明:
     namespace android {
     .............................
     int register_android_server_HelloService(JNIEnv *env);
     };

     在JNI_onLoad增加register_android_server_HelloService函數調用:
     extern "C" jint JNI_onLoad(JavaVM* vm, void* reserved)
     {
     .............................
      register_android_server_HelloService(env);
     .............................
     }
     這樣,在Android系統初始化時,就會自動加載該JNI方法調用表。

     四. 修改同目錄下的Android.mk文件,在LOCAL_SRC_FILES變量中增加一行:
     LOCAL_SRC_FILES:= \
     com_android_server_AlarmManagerService.cpp \
     com_android_server_BatteryService.cpp \
     com_android_server_InputManager.cpp \
     com_android_server_LightsService.cpp \
     com_android_server_PowerManagerService.cpp \
     com_android_server_SystemServer.cpp \
     com_android_server_UsbService.cpp \
     com_android_server_VibratorService.cpp \
     com_android_server_location_GpsLocationProvider.cpp \
     com_android_server_HelloService.cpp /
     onload.cpp

五. 編譯system.img:
     USER-NAME@MACHINE-NAME:~/Android$ mmm frameworks/base/services/jni
     USER-NAME@MACHINE-NAME:~/Android$ make snod
     這樣,重新打包的system.img鏡像文件就包含我們剛才編寫的JNI方法了,也就是我們可以通過Android系統的Application Frameworks層提供的硬體服務HelloService來調用這些JNI方法,進而調用低層的硬體抽象層接口去訪問硬體了。前面提到,在這篇文章中,我們暫時忽略了HelloService類的實現,在下一篇文章中,我們將描述如何實現硬體服務HelloService。


沒有留言:

張貼留言