前面几张我们理解了如何在kernel中写驱动程序,如何访问这个驱动,在HAL层如何读写hello驱动的寄存器值,接下来我们想从App层(一个apk程序)能对hello驱动的寄存器值进行读写操作,但是APP层使用的是JAVA语言,HAL层以及kernel使用的是C\C++语言进行编写,我们如何才能进行跨语言的调用呢? Android给我们提供了一个很好的方法JNI(Java Native Interface)来进行调用。
Android的应用程序使用java编写,Java类编译为Dex型式的Bytecode之后,必须靠Dalvik虚拟机(VM: Virtual Machine)来执行。在执行Java类的过程中,如果Java类需要与C组件沟通时,VM就会去载入C组件,然后让Java的函数顺利地调用到C组件的函数。此时,VM扮演着桥梁的角色,让Java与C组件能通过标准的JNI介面而相互沟通。
我们这里JAVA应用程序将会通过JNI方法来调用硬件抽象层接口,来实现对寄存器值的读写操作。
- JNI方法创建
在frameworks/base/services/core/jni目录,新建一个com_android_server_HelloService.cpp
cd frameworks/base/services/core/jni
vim 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是一个提供Java接口的硬件访问服务类。
代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70#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>
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;
ALOGI("Hello JNI: set value %d to device.", val);
if(!hello_device) {
ALOGI("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) {
ALOGI("Hello JNI: device is not open.");
return val;
}
hello_device->get_val(hello_device, &val);
ALOGI("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);
}
/*通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件*/
static jboolean hello_init(JNIEnv* env, jclass clazz) {
hello_module_t* module;
ALOGI("Hello JNI: initializing......");
if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {
ALOGI("Hello JNI: hello Stub found.");
if(hello_device_open(&(module->common), &hello_device) == 0) {
ALOGI("Hello JNI: hello device is open.");
return 0;
}
ALOGE("Hello JNI: failed to open hello device.");
return -1;
}
ALOGE("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是在
2.修改onload.cpp
cd \frameworks\base\services\core\jni
增加下面两行代码1
2
3
4
5
6
7
8
9
10
11
12namespace 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方法调用表。
3.修改同目录下的Android.mk文件,在LOCAL_SRC_FILES变量中增加一行:1
2
3com_android_server_location_GpsLocationProvider.cpp \
com_android_server_HelloService.cpp /
onload.cpp
4.编译
mmm frameworks/base/services/core/jni/
make snod
至此,我们生成的system.img就包括了我们的JNI方法,这个方法可以提供给App Framework层进行调用,从而来读写hello驱动的寄存器值。
注:代码程序主要参考《老罗的Android之旅》来完成。