主要是在
~/Android_4.2.2_SourceCode/frameworks/base/services/jni个夹子里面操作的。
根据老罗的方法我也是实现成功了。
但是和上一篇文章一样,同样需要将LOGI改为ALOGI,LOGE改为ALOGE。
他也写了很多知识方面的内容,但是目前还只是实现了部分,待后面JAVA方面调用的例程写好了,我串起来总结一下。。。
地址:http://blog.csdn.net/luoshengyang/article/details/6575988
在上两篇文章中,我们介绍了如何为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);
- }
- /*通过硬件模块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);
};
-
顶
- 28
-
踩
- 0
-
30楼
longshijun 6天前 17:32发表 [回复]
- 罗老师好啊,新接触你的博客,写的太好了,按照你的方法一步步来,遇到了问题No rule to make target `out/target/product/generic/obj/lib/libhardware.so', needed by `out/target/product/generic/obj/SHARED_LIBRARIES/libandroid_servers_intermediates/LINKED/libandroid_servers.so'. Stop.,我的源码编译后的工程目录smdkv210,可是默认是.so文件是在generic下,我在smdkv210下的新建system/, 把的看smdk210下的lib拷贝过去,编译没问题,生成.so文件在generic/system/lib/hw下,可是板子的product都在smdk210目录下,然后在编程镜像在板子上怎么都找不到我自己编的.so文件。。搞了几天了。。。上面老师提到的方法也用了,问题没解决 啊。老师,,求助
-
29楼
xinxianzhongndsc 2013-04-23 12:37发表 [回复]
-
编译之后模拟器起不来,求指点。去除之后再走一遍还是老样子
I/DEBUG ( 36): beaaead0 00009082 /system/bin/app_process
I/DEBUG ( 36): beaaead4 401453db /system/lib/libandroid_runtime.so
I/ServiceManager( 32): service 'media.audio_flinger' died
I/ServiceManager( 32): service 'media.player' died
I/ServiceManager( 32): service 'media.camera' died
I/ServiceManager( 32): service 'media.audio_policy' died
I/Netd ( 475): Netd 1.0 starting
E/Netd ( 475): Unable to bind netlink socket: No such file or directory
E/Netd ( 475): Unable to open quota2 logging socket
I/ ( 474): ServiceManager: 0xf958
I/AudioFlinger( 474): Loaded primary audio interface from LEGACY Audio HW HAL (audio)
I/AudioFlinger( 474): Using 'LEGACY Audio HW HAL' (audio.primary) as the primary audio interface-
Re:
lch1987326 2013-06-19 01:39发表 [回复]
-
回复xinxianzhongndsc:楼主我也碰到这个问题了,你解决了没?
-
Re:
xinxianzhongndsc 2013-06-19 09:26发表 [回复]
- 回复lch1987326:没有,不知道什么原因,查找各种解决方案无果
-
28楼
zhangshuliai 2013-03-18 23:48发表 [回复]
- 大神,打扰一下,问一个问题,类似于91助手这样的连接PC和手机的程序大概是怎样做的?
-
27楼
woluzhi 2013-01-16 17:15发表 [回复]
-
01.namespace android
02.{
03. /*在硬件抽象层中定义的硬件访问结构体,参考<hardware/hello.h>*/
04. struct hello_device_t* hello_device = NULL;
05. /*通过硬件抽象层定义的硬件访问接口设置硬件寄存器val的值*/
06. static void hello_setVal(JNIEnv* env, jobject clazz, jint value) {
07. int val = value;
08. LOGI("Hello JNI: set value %d to device.", val);
09. if(!hello_device) {
10. LOGI("Hello JNI: device is not open.");
11. return;
12. }
13.
14. hello_device->set_val(hello_device, val);
15. }
罗哥,struct hello_device_t* hello_device = NULL;
所以到 if(!hello_device) 就return出去了
下面这个 hello_device->set_val(hello_device, val);
怎么执行的呢?
-
26楼
yuanfen139re 2012-10-10 15:07发表 [回复]
-
- if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {
老罗,这行代码里(const struct hw_module_t**)&module是不是写错了,我觉得应该是(const struct xx_module_t**)&module,否则的话如何实现下面的module->common?望指点,谢谢!-
Re:
罗升阳 2012-10-10 15:20发表 [回复]
-
回复yuanfen139re:没有错,module本来就是一个类型为hello_module_t的指针,所以module->common是没有问题的。
-
Re:
yuanfen139re 2012-10-11 15:13发表 [回复]
- 回复Luoshengyang:明白了,谢谢啊
-
25楼
gyy939774006 2012-09-27 19:27发表 [回复]
-
'android::register_android_server_HelloService(_JNIEnv*)'
这个函数是在哪里定义的??
-
24楼
gyy939774006 2012-09-27 19:26发表 [回复]
-
罗老师,出现这样的问题
frameworks/base/services/jni/onload.cpp:39: error: undefined reference to 'android::register_android_server_HelloService(_JNIEnv*)'
是什么情况?
-
23楼
chengfengpeak001 2012-08-22 13:22发表 [回复]
- 没有积分了
-
22楼
LOVE___________YOU 2012-08-22 11:08发表 [回复]
-
罗老师,您好。我现在想在Android应用层直接调用Linux内核下的.so的动态库,应该在HAL层怎么写啊。只需要返回一个hello world!就可以了。我是个菜鸟,搞了快两周了,一点头绪都没有。
-
Re:
kevinyujm 2013-03-13 12:07发表 [回复]
-
回复LOVE___________YOU:我想你的疑惑是java怎么调用.so里面的函数?
这其实就是Java怎么调用c/cpp库的问题,答案就是使用jni。
如果不明白,可以搜索“jni调用so库”
-
Re:
罗升阳 2012-08-22 11:31发表 [回复]
-
回复LOVE___________YOU:不是很明白你的需求,你是想写一个纯粹的so,还是想写一个HAL模块,两者还是有区别的。
-
Re:
LOVE___________YOU 2012-08-22 13:51发表 [回复]
- 回复Luoshengyang:或者简单的就是我现在有个返回hello的.so动态库,现在hal层应该怎么来写,和这个动态库应该放在什么地方才能让应用层识别啊。
-
Re:
LOVE___________YOU 2012-08-22 13:47发表 [回复]
- 回复Luoshengyang:就是我想将linux下的驱动,移植到android中来,可是看您写的是将驱动编译到内核中。有没有直接写个hal层然后,用这个hal层来操作.so的动态库的例子啊。
-
Re:
LOVE___________YOU 2012-08-22 13:42发表 [回复]
- 回复Luoshengyang:罗老师,我就是想直接做一个linux下的动态库,然后直接在应用层调用,可是我不知道中间这层应该怎么写的。我看您这个文章都是讲驱动编译到内核里面了,我现在不想往内核里面编译,就是想直接操作动态库.so文件,应该怎么搞啊。
-
21楼
houyizi313 2012-08-08 10:47发表 [回复]
-
罗哥:你的程序HAL层与Driver连接是通过设备文件fd;我看overlay.h/cpp没有找到跟Driver联系fd啊?一般android的HAL层封装底层linux驱动都靠什么连接???谢谢……
-
Re:
罗升阳 2012-08-09 01:38发表 [回复]
-
回复houyizi337825770:HAL层是通过设备文件来和内核层中的硬件驱动程序交互的。HAL层的overlay模块只是一个骨架,即实现规范,里面的内容是需要由厂家来定制的,因此,在这个模块里面的overlay.h和overlay.cpp里面是看不到有设备文件的。一个具体的实现可以参考hardware/ti/omap3/liboverlay模块,里面通过/dev/video1和/dev/video2设备文件来和overlay设备通信。
-
Re:
houyizi313 2012-09-05 15:50发表 [回复]
- 回复Luoshengyang:谢谢罗哥:这阵子虽然也在做android,但是没时间看书了,天天在实践了,根据官网在搞pandaboard的板子;所有好久没来看罗哥的博客了;因为pandaboard也遇到了很多问题;也遇到了很多问题;现在是刚把官网的android编译通过……但是怎么都刷不到SD上……堵这了%^再慢慢解决……
-
20楼
tgt007 2012-08-03 14:05发表 [回复]
- 我按照你说的设置一下环境变量TARGET_PRODUCT=crespo,还是报错make: *** [out/target/product/generic/obj/SHARED_LIBRARIES/libandroid_servers_intermediates/LINKED/libandroid_servers.so] 错误1,怎么回事楼主?
-
19楼
huangzhenyu1983 2012-07-31 16:43发表 [回复]
-
有个笔误,吹毛求疵一下:)
四. 修改同目录下的Android.mk文件
.......
com_android_server_HelloService.cpp /
应该是 com_android_server_HelloService.cpp \ (反斜杠)
-
18楼
newsten 2012-07-27 17:34发表 [回复]
-
楼主请问我在JNI里面直接open /dev/hello返回不是-1,但驱动的open并没有走到,用那个linux下的小程序system/bin/hello在adb shell下就可以,我在open函数里加了printk可以看到log的,请问是怎么回事啊?
-
Re:
罗升阳 2012-07-31 17:01发表 [回复]
-
回复newsten:应该是权限问题了,看看这篇文章:http://blog.csdn.net/luoshengyang/article/details/6573809
-
Re:
newsten 2012-07-31 22:22发表 [回复]
- 回复Luoshengyang:我在ueventd.rc已经添加/dev/hello 0666 root root了,shell下也chmod了,open在jni里返回并不是-1,但和shell下的hello小程序返回值不一样,我在shell下用echo、cat和那个hello小程序操作/dev/hello是可以看到驱动open函数里添加的printk打印的消息,但是在jni里open就不行,我又添加了HAL层在JNI和驱动之间还是不行,好像程序并没有走到驱动里面…………
-
17楼
zhongjihao100 2012-07-16 23:03发表 [回复]
- 真不错的文章
-
16楼
wantianpei 2012-03-17 10:23发表 [回复]
-
大哥,jni怎么返回“中文字符串”呀?搞了一天,试了各种方法都只能返回英文字符串,中文的就报错。
中文的这样写就可以,
jchar * newstring;
jstring ret=0;
newstring=new jchar[1];
newstring[0]=27979;//测字的unicode 码
ret=env->NewString(newstring,1);
直接搞个"测"就不行。 大哥有高招没有罗。我都整两天了也没搞定。英文的就可以,中文的不是报错就是乱码.-
Re:
xiaofeishou 2012-04-17 14:26发表 [回复]
- 回复wantianpei:返回一个byte的数组 然后上面用string的构造函数转化
-
15楼
mayugang 2012-02-14 23:59发表 [回复]
- 想请教一下博主JNI方面的内容:按照JNI的规定,native方法采用C++编写时,要用extern “C” 声明一下,可是在这个地方并没有看到类似的代码,是Android在其他地方做了处理了?
-
13楼
floweriswho 2011-11-23 23:29发表 [回复]
- 不错!
-
12楼
m313856126 2011-09-28 20:55发表 [回复]
-
楼主大神你好 请教个问题
我目前正在Android系统上添加一个支持硬件的模块
采用的是老架构 没有aidl 上层代码直接load“xxx.so”
JNI层代码放在framework\base\myJNI\下
目前需要在该目录下添加Android.mk文件
目的是执行mm时生成xxx.so
请问这个Android.mk该肿么写 我调了好久都没有编译通过
不知道楼主大神有没有这种生成动态链接库的例子
小生不胜感激 313856126@qq.com
-
11楼
wantianpei 2011-09-08 20:55发表 [回复]
-
楼主:
Android.mk
com_android_server_HelloService.cpp /
应该改成:
com_android_server_HelloService.cpp \
上面所有的' / '都应该改成' \ '
-
10楼
yanlingzhen 2011-09-05 18:33发表 [回复]
-
我在c 语言封闭的库当中已经将log所用的动态库加上,并且其原型的打印函数在我的c中可以使用,但logcat 当中始终没有信息。
如
#include <android/log.h>
#include <utils/Log.h>
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , "ProjectName", __VA_ARGS__)
我在程序当中这样调的:
LOGI("this is my first program ");
-
9楼
yanlingzhen 2011-09-03 16:53发表 [回复]
- 你好,如果我的c语言当中的参数含有结构体指针或其它类型的指针(如int *)JNINativeMethod 当中的第二个参怎么表示,我知道到jni.h里去找,但是没有找到,请指点一下。
-
8楼
yanlingzhen 2011-09-01 09:52发表 [回复]
-
主要是加了
register_android_server_HelloService(env);这个语句产生的。去掉就可以。我有点晕了。-
Re:
jqyp324 2012-02-29 18:07发表 [回复]
-
回复yanlingzhen:解决了吗?我也碰到这个问题了,楼主的代码有问题阿,作实验做不下去了
-
Re:
FzChen_2012 2012-04-17 11:54发表 [回复]
- 回复jqyp324:我也碰到了同样的问题,不知道老兄解决了吗?
-
7楼
yanlingzhen 2011-09-01 08:30发表 [回复]
-
<6>eth0: link up
<6>warning: `rild' uses 32-bit capabilities (legacy support in use)
<6>request_suspend_state: wakeup (3->0) at 31550002285 (2011-09-01 00:30:08.389096500 UTC)
<3>init: untracked pid 29 exited
<3>init: untracked pid 33 exited
<6>request_suspend_state: wakeup (0->0) at 35143873821 (2011-09-01 00:30:11.983138164 UTC)
<3>init: untracked pid 68 exited
<3>init: untracked pid 67 exited
<6>request_suspend_state: wakeup (0->0) at 39724385133 (2011-09-01 00:30:16.563648782 UTC)
<3>init: untracked pid 78 exited
<3>init: untracked pid 77 exited
<6>request_suspend_state: wakeup (0->0) at 45310642402 (2011-09-01 00:30:22.149906097 UTC)
<3>init: untracked pid 88 exited
<3>init: untracked pid 87 exited
-
6楼
yanlingzhen 2011-08-31 19:55发表 [回复]
- 我也按这样做的,但是android 启动不了,内核可以起来,不知道什么原因。
-
5楼
hui05504 2011-08-14 11:05发表 [回复]
- 楼主,有个问题,为了方便移植,我将这个教程里新增的文件都放在源码的根目录下的一个新建的文件夹下面,所以如何修改onload.cpp文件呢,因为我的com.android.server.HelloService不是在那个文件夹下面,这个怎么处理呢?
-
4楼
hui05504 2011-08-01 17:14发表 [回复]
-
楼主,http://blog.csdn.net/hongtao_liu/article/details/6060734这个有看过么,我下载了这个代码,发现里面的init.rc文件有一处不太明白,不知道是否和下面讨论的这个问题有关。在init.rc中:
insmod /system/lib/modules/led.ko
mknod /dev/led c 230 0
这个的意思是载入led的模块以及创建led字符设备文件。
不知道这里需要添加不?-
Re:
罗升阳 2011-08-01 18:07发表 [回复]
-
回复hui05504:这两个命令前一个是用来动态加载led这个驱动模块,后一个是用来创建设备文件的,后面两个数字一个主设备号,一个是从设备号。或许你可以继续研究一下这个文件,看看有没有线索。据我之前的了解,设备文件权限修改的地方就是在system/core/init/init.c这个里面做的,可以好好研究一下~旧的版本的代码的确是没有ueventd这个机制,我也是偶然发现的,网络上没有找到关于这个的资料。
-
Re:
hui05504 2011-08-01 21:38发表 [回复]
-
回复Luoshengyang:楼主,帮忙看下这个文章http://my.chinaunix.net/space.php?uid=20564848&do=blog&id=186359,你觉得采用这个device /dev/hello 644 system system ,这样就可以修改hello的默认创建的权限,这样可行不?
-
Re:
罗升阳 2011-08-01 22:43发表 [回复]
-
回复hui05504:看起来是可以的,可以试一下
-
Re:
hui05504 2011-08-02 11:27发表 [回复]
-
回复Luoshengyang:折腾啊,楼主,你那里可以运行模拟器,可以帮忙搞下这个问题不,目前比较急,都卡在这里了:(
-
Re:
罗升阳 2011-08-02 11:49发表 [回复]
-
回复hui05504:刚想起一个问题,你的源代码工程是2.2版本的吧,而你下载的内核代码是最新的,可能是跟这个有关系
-
Re:
hui05504 2011-08-02 13:29发表 [回复]
- 回复Luoshengyang:不是的,都是一套的,我这里是德州仪器提供的代码,可能模拟器方面的功能是不行的,这个权限的问题还要请楼主多多帮忙啊,估计这个对楼主来说可能半个小时就搞定,但是对我来说,要花个一个星期:(麻烦了
-
3楼
大只辉 2011-08-01 14:50发表 [回复]
-
版主,loadon.cpp这个有错
register_android_server_HelloService(JNIEnv *env);
应该是
register_android_server_HelloService(env);
-
2楼
hui05504 2011-07-31 20:46发表 [回复]
-
怎么不能连续输入3次呢,杯具,楼主,没有dev/binder这个字符呢,这个有什么建议呢,或者有文章推荐也可以
-
Re:
罗升阳 2011-07-31 21:03发表 [回复]
-
回复hui05504:建议你扩大范围搜索一下代码,例如在system/core目录下找一下,肯定能发现线索的,不然用于进程间通信的/dev/binder设备文件除了root用户,其它用户也都访问不了
-
Re:
hui05504 2011-07-31 21:31发表 [回复]
-
如果让app拥有root的权限,应该可以访问hello的设备吧
-
Re:
罗升阳 2011-07-31 22:49发表 [回复]
-
回复hui05504:这样做不现实,也实现不了的吧,赋予app的root功能,那就有安全问题了,app可以为所欲为了。
-
Re:
hui05504 2011-08-01 09:37发表 [回复]
- 回复Luoshengyang:你网上搜下,有很多这方面的帖子,但是我没有试过,我看了2.3.3和2.2的源码都没有你说的那个文件,楼主,你这个是哪个版本的源码呢?
-
1楼
hui05504 2011-07-28 23:09发表 [回复]
-
楼主,按照你上面的教程,出现了以下错误,
No rule to make target `out/target/product/generic/obj/lib/libhardware.so', needed by `out/target/product/generic/obj/SHARED_LIBRARIES/libandroid_servers_intermediates/LINKED/libandroid_servers.so'. Stop.
,麻烦指导下,谢谢