前言
意外在网上发现了这扁文章,看后感觉很有必要分享,所以整理并上传,希望大家喜欢。
Android?硬件抽象层(HAL)概要介绍和学习计划
Android?的硬件抽象层,简单来说,就是对Linux?内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。也就是说,把
对硬件的支持分成了两层,一层放在用户空间(User?Space),一层放在内核空间(Kernel?Space),其中,硬件抽象层运行在
用户空间,而Linux?内核驱动程序运行在内核空间。为什么要这样安排呢?把硬件抽象层和内核驱动整合在一起放在内核空间
不可行吗?从技术实现的角度来看,是可以的,然而从商业的角度来看,把对硬件的支持逻辑都放在内核空间,可能会损害
厂家的利益。我们知道,Linux?内核源代码版权遵循GNU?License,而Android?源代码版权遵循Apache?License,前者在发布产
品时,必须公布源代码,而后者无须发布源代码。如果把对硬件支持的所有代码都放在Linux?驱动层,那就意味着发布时要公
开驱动程序的源代码,而公开源代码就意味着把硬件的相关参数和实现都公开了,在手机市场竞争激烈的今天,这对厂家来
说,损害是非常大的。因此,Android?才会想到把对硬件的支持分成硬件抽象层和内核驱动层,内核驱动层只提供简单的访问
硬件逻辑,例如读写硬件寄存器的通道,至于从硬件中读到了什么值或者写了什么值到硬件中的逻辑,都放在硬件抽象层中
去了,这样就可以把商业秘密隐藏起来了。也正是由于这个分层的原因,Android?被踢出了Linux?内核主线代码树中。大家想
想,Android?放在内核空间的驱动程序对硬件的支持是不完整的,把Linux?内核移植到别的机器上去时,由于缺乏硬件抽象层
的支持,硬件就完全不能用了,这也是为什么说Android?是开放系统而不是开源系统的原因。撇开这些争论,学习Android?硬
件抽象层,对理解整个Android?整个系统,都是极其有用的,因为它从下到上涉及到了Android?系统的硬件驱动层、硬件抽象
层、运行时库和应用程序框架层等等,下面这个图阐述了硬件抽象层在Android?系统中的位置,以及它和其它层的关系:
在学习Android?硬件抽象层的过程中,我们将会学习如何在内核空间编写硬件
驱动程序、如何在硬件抽象层中添加接口支持访问硬件、如何在系统启动时
提供硬件访问服务以及?如何编写JNI?使得可以通过Java?接口来访问硬件,而
作为中间的一个小插曲,我们还将学习一下如何在Android?系统中添加一个C
可执行程序来访问硬件驱动程序。由于这是一个系统的学习过程,笔者将分
成六篇文章来描述每一个学习过程,包括:?
一.?在Android?内核源代码工程中编写硬件驱动程序。
二.?在Android?系统中增加C?可执行程序来访问硬件驱动程序。
三.?在Android?硬件抽象层增加接口模块访问硬件驱动程序。
四.?在Android?系统中编写JNI?方法在应用程序框架层提供Java?接口访问硬件。
五.?在Android?系统的应用程序框架层增加硬件服务接口。
六.?在Android?系统中编写APP?通过应用程序框架层访问硬件服务。
学习完这六篇文章,相信大家对Android?系统就会有一个更深刻的认识了,敬请关注。
在Ubuntu?上为Android?系统编写Linux?内核驱动程序
这里,我们不会为真实的硬件设备编写内核驱动程序。为了方便描述为Android?系统编写内核驱动程序的过程,我们使用一
个虚拟的硬件设备,这个设备只有一个4?字节的寄存器,它可读可写。想起我们第一次学习程序语言时,都喜欢用“Hello,?World”
作为例子,这里,我们就把这个虚拟的设备命名为“hello”,而这个内核驱动程序也命名为hello?驱动程序。其实,Android?内
核驱动程序和一般Linux?内核驱动程序的编写方法是一样的,都是以Linux?模块的形式实现的,具体可参考前面Android?学习
启动篇一文中提到的Linux?Device?Drivers?一书。不过,这里我们还是从Android?系统的角度来描述Android?内核驱动程序的编
写和编译过程。
一.?参照这两篇文章在Ubuntu?上下载、编译和安装Android?最新源代码和在Ubuntu?上下载、编译和安装Android?最新内核源
代码(Linux?Kernel)准备好Android?内核驱动程序开发环境。
二.?进入到kernel/common/drivers?目录,新建hello?目录:
[email protected]:~/Android$?cd?kernel/common/drivers
[email protected]:~/Android/kernel/common/drivers$?mkdir?hello
三.?在hello?目录中增加hello.h?文件:
1.#ifndef?_HELLO_Android_H_
2.#define?_HELLO_ANDROID_H_
3.#include?<linux/cdev.h>
4.#include?<linux/semaphore.h>
5.#define?HELLO_DEVICE_NODE_NAME?"hello"
8.#define?HELLO_DEVICE_FILE_NAME?"hello"
9.#define?HELLO_DEVICE_PROC_NAME?"hello"
10.#define?HELLO_DEVICE_CLASS_NAME?"hello"
12.struct?hello_android_dev?{
13.?int?val;
14.?struct?semaphore?sem;
15.?struct?cdev?dev;
16.};
17.#endif
这个头文件定义了一些字符串常量宏,在后面我们要用到。此外,还定义了一个字符设备结构体hello_Android_dev,这个就
是我们虚拟的硬件设备了,val?成员变量就代表设备里面的寄存器,它的类型为int,sem?成员变量是一个信号量,是用同步
访问寄存器val?的,dev?成员变量是一个内嵌的字符设备,这个Linux?驱动程序自定义字符设备结构体的标准方法。
四.在hello?目录中增加hello.c?文件,这是驱动程序的实现部分。驱动程序的功能主要是向上层提供访问设备的寄存器的值,
包括读和写。这里,提供了三种访问设备寄存器的方法,一是通过proc?文件系统来访问,二是通过传统的设备文件的方法来
访问,三是通过devfs?文件系统来访问。下面分段描述该驱动程序的实现。
首先是包含必要的头文件和定义三种访问设备的方法:
1.#include?<linux/init.h>
2.#include?<linux/module.h>
3.#include?<linux/types.h>
4.#include?<linux/fs.h>
5.#include?<linux/proc_fs.h>
6.#include?<linux/device.h>
7.#include?<asm/uaccess.h>
8.
9.#include?"hello.h"
10.
11./*主设备和从设备号变量*/
12.static?int?hello_major?=?0;
13.static?int?hello_minor?=?0;
14.
15./*设备类别和设备变量*/
16.static?struct?class*?hello_class?=?NULL;
17.static?struct?hello_Android_dev*?hello_dev?=?NULL;
18.
19./*传统的设备文件操作方法*/
20.static?int?hello_open(struct?inode*?inode,?struct?file*?filp);
21.static?int?hello_release(struct?inode*?inode,?struct?file*?filp);
22.static?ssize_t?hello_read(struct?file*?filp,?char?__user?*buf,?size_t?count,?loff_t*?f_pos);
23.static?ssize_t?hello_write(struct?file*?filp,?const?char?__user?*buf,?size_t?count,?loff_t*?f_pos);
24.
25./*设备文件操作方法表*/
26.static?struct?file_operations?hello_fops?=?{
27.?.owner?=?THIS_MODULE,
28.?.open?=?hello_open,
29.?.release?=?hello_release,
30.?.read?=?hello_read,
31.?.write?=?hello_write,
32.};
33.
34./*定义设备属性*/
35.static?DEVICE_ATTR(val,?S_IRUGO?|?S_IWUSR,?hello_val_show,?hello_val_store);
36.
37./*访问设置属性方法*/
38.static?ssize_t?hello_val_show(struct?device*?dev,?struct?device_attribute*?attr,?char*?buf);
39.static?ssize_t?hello_val_store(struct?device*?dev,?struct?device_attribute*?attr,?const?char*?buf,?size_t?count);?定义传统的设备文
件访问方法,主要是定义hello_open、hello_release、hello_read?和hello_write?这四个打开、释放、读和写设备文件的方法:
1./*打开设备方法*/
2.static?int?hello_open(struct?inode*?inode,?struct?file*?filp)?{
3.?struct?hello_Android_dev*?dev;
4.
5.?/*将自定义设备结构体保存在文件指针的私有数据域中,以便访问设备时拿来用*/
6.?dev?=?container_of(inode->i_cdev,?struct?hello_android_dev,?dev);
7.?filp->private_data?=?dev;
8.
9.?return?0;
10.}
11.
12./*设备文件释放时调用,空实现*/
13.static?int?hello_release(struct?inode*?inode,?struct?file*?filp)?{
14.?return?0;
15.}
16.
17./*读取设备的寄存器val?的值*/
18.static?ssize_t?hello_read(struct?file*?filp,?char?__user?*buf,?size_t?count,?loff_t*?f_pos)?{
19.?ssize_t?err?=?0;
20.?struct?hello_android_dev*?dev?=?filp->private_data;
21.
22.?/*同步访问*/
23.?if(down_interruptible(&(dev->sem)))?{
24.?return?-ERESTARTSYS;
25.?}
26.
27.?if(count?<?sizeof(dev->val))?{
28.?goto?out;
29.?}
30.
31.?/*将寄存器val?的值拷贝到用户提供的缓冲区*/
32.?if(copy_to_user(buf,?&(dev->val),?sizeof(dev->val)))?{
33.?err?=?-EFAULT;
34.?goto?out;
35.?}
36.
37.?err?=?sizeof(dev->val);
38.
39.out:
40.?up(&(dev->sem));
41.?return?err;
42.}
43.
44./*写设备的寄存器值val*/
45.static?ssize_t?hello_write(struct?file*?filp,?const?char?__user?*buf,?size_t?count,?loff_t*?f_pos)?{
46.?struct?hello_android_dev*?dev?=?filp->private_data;
47.?ssize_t?err?=?0;
48.
49.?/*同步访问*/
50.?if(down_interruptible(&(dev->sem)))?{
51.?return?-ERESTARTSYS;
52.?}
53.
54.?if(count?!=?sizeof(dev->val))?{
55.?goto?out;
56.?}
57.
58.?/*将用户提供的缓冲区的值写到设备寄存器去*/
59.?if(copy_from_user(&(dev->val),?buf,?count))?{
60.?err?=?-EFAULT;
61.?goto?out;
62.?}
63.
64.?err?=?sizeof(dev->val);
65.
66.out:
67.?up(&(dev->sem));
68.?return?err;
69.}
定义通过devfs?文件系统访问方法,这里把设备的寄存器val?看成是设备的一个属性,通过读写这个属性来对设备进行访问,
主要是实现hello_val_show?和hello_val_store?两个方法,同时定义了两个内部使用的访问val?值的方法__hello_get_val?和
__hello_set_val:
1.?/*读取寄存器val?的值到缓冲区buf?中,内部使用*/
2.?static?ssize_t?__hello_get_val(struct?hello_Android_dev*?dev,?char*?buf)?{
3.?int?val?=?0;
4.
5.?/*同步访问*/
6.?if(down_interruptible(&(dev->sem)))?{
7.?return?-ERESTARTSYS;
8.?}
9.
10.?val?=?dev->val;
11.?up(&(dev->sem));
12.
13.?return?snprintf(buf,?PAGE_SIZE,?"%d/n",?val);
14.?}
15.
16.?/*把缓冲区buf?的值写到设备寄存器val?中去,内部使用*/
17.?static?ssize_t?__hello_set_val(struct?hello_Android_dev*?dev,?const?char*?buf,?size_t?count)?{
18.?int?val?=?0;
19.
20.?/*将字符串转换成数字*/
21.?val?=?simple_strtol(buf,?NULL,?10);
22.
23.?/*同步访问*/
24.?if(down_interruptible(&(dev->sem)))?{
25.?return?-ERESTARTSYS;
26.?}
27.
28.?dev->val?=?val;
29.?up(&(dev->sem));
30.
31.?return?count;
32.?}
33.
34.?/*读取设备属性val*/
35.?static?ssize_t?hello_val_show(struct?device*?dev,?struct?device_attribute*?attr,?char*?buf)?{
36.?struct?hello_Android_dev*?hdev?=?(struct?hello_android_dev*)dev_get_drvdata(dev);
37.
38.?return?__hello_get_val(hdev,?buf);
39.?}
40.
41.?/*写设备属性val*/
42.?static?ssize_t?hello_val_store(struct?device*?dev,?struct?device_attribute*?attr,?const?char*?buf,?size_t?coun
t)?{
43.?struct?hello_Android_dev*?hdev?=?(struct?hello_android_dev*)dev_get_drvdata(dev);
44.
45.?return?__hello_set_val(hdev,?buf,?count);
46.?}
定义通过proc?文件系统访问方法,主要实现了hello_proc_read?和hello_proc_write?两个方法,同时定义了在proc?文件系统
创建和删除文件的方法hello_create_proc?和hello_remove_proc:
1.?/*读取设备寄存器val?的值,保存在page?缓冲区中*/
2.?static?ssize_t?hello_proc_read(char*?page,?char**?start,?off_t?off,?int?count,?int*?eof,?void*?data)?{
3.?if(off?>?0)?{
4.?*eof?=?1;
5.?return?0;
6.?}
7.
8.?return?__hello_get_val(hello_dev,?page);
9.?}
10.
11.?/*把缓冲区的值buff?保存到设备寄存器val?中去*/
12.?static?ssize_t?hello_proc_write(struct?file*?filp,?const?char?__user?*buff,?unsigned?long?len,?void*?data)?{
13.?int?err?=?0;
14.?char*?page?=?NULL;
15.
16.?if(len?>?PAGE_SIZE)?{
17.?printk(KERN_ALERT"The?buff?is?too?large:?%lu./n",?len);
18.?return?-EFAULT;
19.?}
20.
21.?page?=?(char*)__get_free_page(GFP_KERNEL);
22.?if(!page)?{
23.?printk(KERN_ALERT"Failed?to?alloc?page./n");
24.?return?-ENOMEM;
25.?}
26.
27.?/*先把用户提供的缓冲区值拷贝到内核缓冲区中去*/
28.?if(copy_from_user(page,?buff,?len))?{
29.?printk(KERN_ALERT"Failed?to?copy?buff?from?user./n");
30.?err?=?-EFAULT;
31.?goto?out;
32.?}
33.
34.?err?=?__hello_set_val(hello_dev,?page,?len);
35.
36.?out:
37.?free_page((unsigned?long)page);
38.?return?err;
39.?}
40.
41.?/*创建/proc/hello?文件*/
42.?static?void?hello_create_proc(void)?{
43.?struct?proc_dir_entry*?entry;
44.
45.?entry?=?create_proc_entry(HELLO_DEVICE_PROC_NAME,?0,?NULL);
46.?if(entry)?{
47.?entry->owner?=?THIS_MODULE;
48.?entry->read_proc?=?hello_proc_read;
49.?entry->write_proc?=?hello_proc_write;
50.?}
51.?}
52.
53.?/*删除/proc/hello?文件*/
54.?static?void?hello_remove_proc(void)?{
55.?remove_proc_entry(HELLO_DEVICE_PROC_NAME,?NULL);
56.?}
最后,定义模块加载和卸载方法,这里只要是执行设备注册和初始化操作:
1./*初始化设备*/
2.static?int?__hello_setup_dev(struct?hello_Android_dev*?dev)?{
3.?int?err;
4.?dev_t?devno?=?MKDEV(hello_major,?hello_minor);
5.
6.?memset(dev,?0,?sizeof(struct?hello_Android_dev));
7.
8.?cdev_init(&(dev->dev),?&hello_fops);
9.?dev->dev.owner?=?THIS_MODULE;
10.?dev->dev.ops?=?&hello_fops;
11.
12.?/*注册字符设备*/
13.?err?=?cdev_add(&(dev->dev),devno,?1);
14.?if(err)?{
15.?return?err;
16.?}
17.
18.?/*初始化信号量和寄存器val?的值*/
19.?init_MUTEX(&(dev->sem));
20.?dev->val?=?0;
21.
22.?return?0;?23.}
24.
25./*模块加载方法*/
26.static?int?__init?hello_init(void){
27.?int?err?=?-1;
28.?dev_t?dev?=?0;
29.?struct?device*?temp?=?NULL;
30.
31.?printk(KERN_ALERT"Initializing?hello?device./n");
32.
33.?/*动态分配主设备和从设备号*/
34.?err?=?alloc_chrdev_region(&dev,?0,?1,?HELLO_DEVICE_NODE_NAME);
35.?if(err?<?0)?{
36.?printk(KERN_ALERT"Failed?to?alloc?char?dev?region./n");
37.?goto?fail;
38.?}
39.
40.?hello_major?=?MAJOR(dev);
41.?hello_minor?=?MINOR(dev);
42.
43.?/*分配helo?设备结构体变量*/
44.?hello_dev?=?kmalloc(sizeof(struct?hello_Android_dev),?GFP_KERNEL);
45.?if(!hello_dev)?{
46.?err?=?-ENOMEM;
47.?printk(KERN_ALERT"Failed?to?alloc?hello_dev./n");
48.?goto?unregister;
49.?}
50.
51.?/*初始化设备*/
52.?err?=?__hello_setup_dev(hello_dev);
53.?if(err)?{
54.?printk(KERN_ALERT"Failed?to?setup?dev:?%d./n",?err);
55.?goto?cleanup;
56.?}
57.
58.?/*在/sys/class/目录下创建设备类别目录hello*/
59.?hello_class?=?class_create(THIS_MODULE,?HELLO_DEVICE_CLASS_NAME);
60.?if(IS_ERR(hello_class))?{
61.?err?=?PTR_ERR(hello_class);
62.?printk(KERN_ALERT"Failed?to?create?hello?class./n");
63.?goto?destroy_cdev;
64.?}
65.
66.?/*在/dev/目录和/sys/class/hello?目录下分别创建设备文件hello*/
67.?temp?=?device_create(hello_class,?NULL,?dev,?"%s",?HELLO_DEVICE_FILE_NAME);
68.?if(IS_ERR(temp))?{
69.?err?=?PTR_ERR(temp);
70.?printk(KERN_ALERT"Failed?to?create?hello?device.");
71.?goto?destroy_class;
72.?}
73.
74.?/*在/sys/class/hello/hello?目录下创建属性文件val*/
75.?err?=?device_create_file(temp,?&dev_attr_val);
76.?if(err?<?0)?{
77.?printk(KERN_ALERT"Failed?to?create?attribute?val.");
78.?goto?destroy_device;
79.?}
80.
81.?dev_set_drvdata(temp,?hello_dev);
82.
83.?/*创建/proc/hello?文件*/
84.?hello_create_proc();
85.
86.?printk(KERN_ALERT"Succedded?to?initialize?hello?device./n");
87.?return?0;
88.
89.destroy_device:
90.?device_destroy(hello_class,?dev);
91.
92.destroy_class:
93.?class_destroy(hello_class);
94.
95.destroy_cdev:
96.?cdev_del(&(hello_dev->dev));
97.
98.cleanup:
99.?kfree(hello_dev);
100.
101.unregister:
102.?unregister_chrdev_region(MKDEV(hello_major,?hello_minor),?1);
103.
104.fail:
105.?return?err;
106.}
107.
108./*模块卸载方法*/
109.static?void?__exit?hello_exit(void)?{
110.?dev_t?devno?=?MKDEV(hello_major,?hello_minor);
111.
112.?printk(KERN_ALERT"Destroy?hello?device./n");
113.
114.?/*删除/proc/hello?文件*/
115.?hello_remove_proc();
116.
117.?/*销毁设备类别和设备*/
118.?if(hello_class)?{
119.?device_destroy(hello_class,?MKDEV(hello_major,?hello_minor));
120.?class_destroy(hello_class);
121.?}
122.
123.?/*删除字符设备和释放设备内存*/
124.?if(hello_dev)?{
125.?cdev_del(&(hello_dev->dev));
126.?kfree(hello_dev);
127.?}
128.
129.?/*释放设备号*/
130.?unregister_chrdev_region(devno,?1);
131.}
132.
133.MODULE_LICENSE("GPL");
134.MODULE_DESCRIPTION("First?Android?Driver");
135.
136.module_init(hello_init);
137.module_exit(hello_exit);
五.在hello?目录中新增Kconfig?和Makefile?两个文件,其中Kconfig?是在编译前执行配置命令make?menuconfig?时用到的,而
Makefile?是执行编译命令make?是用到的:
Kconfig?文件的内容
config?HELLO
tristate?"First?Android?Driver"
default?n
help
This?is?the?first?Android?driver.
Makefile?文件的内容
obj-$(CONFIG_HELLO)?+=?hello.o
在Kconfig?文件中,tristate?表示编译选项HELLO?支持在编译内核时,hello?模块支持以模块、内建和不编译三种编译方
法,默认是不编译,因此,在编译内核前,我们还需要执行make?menuconfig?命令来配置编译选项,使得hello?可以以模块或
者内建的方法进行编译。
在Makefile?文件中,根据选项HELLO?的值,执行不同的编译方法。
六.?修改arch/arm/Kconfig?和drivers/kconfig?两个文件,在menu?"Device?Drivers"和endmenu?之间添加一行:
source?"drivers/hello/Kconfig"
这样,执行make?menuconfig?时,就可以配置hello?模块的编译选项了。.
七.?修改drivers/Makefile?文件,添加一行:
obj-$(CONFIG_HELLO)?+=?hello/
八.?配置编译选项:
[email protected]:~/Android/kernel/common$?make?menuconfig
找到"Device?Drivers"?=>?"First?Android?Drivers"选项,设置为y。
注意,如果内核不支持动态加载模块,这里不能选择m,虽然我们在Kconfig?文件中配置了HELLO?选项为tristate。
要支持动态加载模块选项,必须要在配置菜单中选择Enable?loadable?module?support?选项;在支持动态卸载模块选项,必须
要在Enable?loadable?module?support?菜单项中,选择Module?unloading?选项。
九.?编译:
[email protected]:~/Android/kernel/common$?make
编译成功后,就可以在hello?目录下看到hello.o?文件了,这时候编译出来的zImage?已经包含了hello?驱动。
十.?参照在Ubuntu?上下载、编译和安装Android?最新内核源代码(Linux?Kernel)一文所示,运行新编译的内核文件,
验证hello?驱动程序是否已经正常安装:
[email protected]:[email protected]ge?&
[email protected]:~/Android$?adb?shell
进入到dev?目录,可以看到hello?设备文件:
[email protected]:/?#?cd?dev
[email protected]:/dev?#?ls
进入到proc?目录,可以看到hello?文件:
[email protected]:/?#?cd?proc
[email protected]:/proc?#?ls
访问hello?文件的值:
[email protected]:/proc?#?cat?hello
0
[email protected]:/proc?#?echo?'5'?>?hello
[email protected]:/proc?#?cat?hello
5
进入到sys/class?目录,可以看到hello?目录:
[email protected]:/?#?cd?sys/class
[email protected]:/sys/class?#?ls
进入到hello?目录,可以看到hello?目录:
[email protected]:/sys/class?#?cd?hello
[email protected]:/sys/class/hello?#?ls
进入到下一层hello?目录,可以看到val?文件:
[email protected]:/sys/class/hello?#?cd?hello
[email protected]:/sys/class/hello/hello?#?ls
访问属性文件val?的值:
[email protected]:/sys/class/hello/hello?#?cat?val
5
[email protected]:/sys/class/hello/hello?#?echo?'0'?>?val
[email protected]:/sys/class/hello/hello?#?cat?val
0
至此,我们的hello?内核驱动程序就完成了,并且验证一切正常。这里我们采用的是系统提供的方法和驱动程序进行
交互,也就是通过proc?文件系统和devfs?文件系统的方法,下一篇文章中,我们将通过自己编译的C?语言程序来访问/dev/hello
文件来和hello?驱动程序交互,敬请期待。
在Android?系统中增加C?可执行程序来访问硬件驱动程序。
在前一篇文章http://www.linuxidc.com/Linux/2011-07/38977.htm?中,我们介绍了如何在Ubuntu?上为Android?系统编写Linux
内核驱动程序。在这个名为hello?的Linux?内核驱动程序中,创建三个不同的文件节点来供用户空间访问,分别是传统的设备
文件/dev/hello、proc?系统文件/proc/hello?和devfs?系统属性文件/sys/class/hello/hello/val。进一步,还通过cat?命令来直接访
问/proc/hello?和/sys/class/hello/hello/val?文件来,以验证驱动程序的正确性。在这一篇文章里,我们将通过自己编写的C?可执
行程序来访问设备文件/dev/hello。可能读者会觉得奇怪,怎么能在Android?系统中用C?语言来编写应用程序呢?Android?系统
上的应用程序不都是Java?应用程序吗?其实是可以的,读者不妨用adb?shell?命令连上Android?模拟器,在/system/bin?目录下
可以看到很多C?可执行程序,如cat?命令。今天,我们就来学习一下怎么在Android?系统中添加用C?语言编写的可执行程序吧。
一.?参照在Ubuntu?上为Android?系统编写Linux?内核驱动程序一文,准备好Linux?驱动程序。使用Android?模拟器加载
包含这个Linux?驱动程序的内核文件,并且使用adb?shell?命令连接上模拟,验证在/dev?目录中存在设备文件hello。
二.?进入到Android?源代码工程的external?目录,创建hello?目录:
[email protected]:~/Android$?cd?external
[email protected]:~/Android/external$?mkdir?hello
三.?在hello?目录中新建hello.c?文件:
1.#include?<stdio.h>
2.#include?<stdlib.h>
3.#include?<fcntl.h>
4.#define?DEVICE_NAME?"/dev/hello"
5.int?main(int?argc,?char**?argv)
6.{
7.?int?fd?=?-1;
8.?int?val?=?0;
9.?fd?=?open(DEVICE_NAME,?O_RDWR);
10.?if(fd?==?-1)?{
11.?printf("Failed?to?open?device?%s./n",?DEVICE_NAME);
12.?return?-1;
13.?}
14.
15.?printf("Read?original?value:/n");
16.?read(fd,?&val,?sizeof(val));
17.?printf("%d./n/n",?val);
18.?val?=?5;
19.?printf("Write?value?%d?to?%s./n/n",?val,?DEVICE_NAME);
20.?write(fd,?&val,?sizeof(val));
21.
22.?printf("Read?the?value?again:/n");
23.?read(fd,?&val,?sizeof(val));
24.?printf("%d./n/n",?val);
25.?close(fd);
26.?return?0;
27.}
这个程序的作用中,打开/dev/hello?文件,然后先读出/dev/hello?文件中的值,接着写入值5?到/dev/hello?中去,最后再
次读出/dev/hello?文件中的值,看看是否是我们刚才写入的值5。从/dev/hello?文件读写的值实际上就是我们虚拟的硬件的寄
存器val?的值。
四.?在hello?目录中新建Android.mk?文件:
LOCAL_PATH?:=?$(call?my-dir)
include?$(CLEAR_VARS)
LOCAL_MODULE_TAGS?:=?optional
LOCAL_MODULE?:=?hello
LOCAL_SRC_FILES?:=?$(call?all-subdir-c-files)
include?$(BUILD_EXECUTABLE)
注意,BUILD_EXECUTABLE?表示我们要编译的是可执行程序。
五.?参照如何单独编译Android?源代码中的模块一文,使用mmm?命令进行编译:
[email protected]:~/Android$?mmm?./external/hello
编译成功后,就可以在out/target/product/gerneric/system/bin?目录下,看到可执行文件hello?了。
六.?重新打包Android?系统文件system.img:
[email protected]:~/Android$?make?snod
这样,重新打包后的system.img?文件就包含刚才编译好的hello?可执行文件了。
七.?运行模拟器,使用/system/bin/hello?可执行程序来访问Linux?内核驱动程序:
[email protected]:[email protected]ge?&
[email protected]:~/Android$?adb?shell
[email protected]:/?#?cd?system/bin
[email protected]:/system/bin?#?./hello
Read?the?original?value:
0.
Write?value?5?to?/dev/hello.
Read?the?value?again:
5.
看到这个结果,就说我们编写的C?可执行程序可以访问我们编写的Linux?内核驱动程序了。
介绍完了如何使用C?语言编写的可执行程序来访问我们的Linux?内核驱动程序,读者可能会问,能不能在Android?的
Application?Frameworks?提供Java?接口来访问Linux?内核驱动程序呢?可以的,接下来的几篇文章中,我们将介绍如何在Android
的Application?Frameworks?中,增加Java?接口来访问Linux?内核驱动程序,敬请期待。
在Ubuntu?上为Android?增加硬件抽象层(HAL)模块访问Linux?内核驱动程序
在Android?硬件抽象层(HAL)概要介绍和学习计划一文中,我们简要介绍了在Android?系统为为硬件编写驱动程序的方法。
简单来说,硬件驱动程序一方面分布在Linux?内核中,另一方面分布在用户空间的硬件抽象层中。接着,在Ubuntu?上为Android
系统编写Linux?内核驱动程序
一文中举例子说明了如何在Linux?内核编写驱动程序。在这一篇文章中,我们将继续介绍Android?系统硬件驱动程序的另一
方面实现,即如何在硬件抽象层中增加硬件模块来和内核驱动程序交互。在这篇文章中,我们还将学习到如何在Android?系
统创建设备文件时用类似Linux?的udev?规则修改设备文件模式的方法。
一.?参照在Ubuntu?上为Android?系统编写Linux?内核驱动程序一文所示,准备好示例内核驱动序。完成这个内核驱动程序后,
便可以在Android?系统中得到三个文件,分别是/dev/hello、/sys/class/hello/hello/val?和/proc/hello。在本文中,我们将通过设
备文件/dev/hello?来连接硬件抽象层模块和Linux?内核驱动程序模块。
二.?进入到在hardware/libhardware/include/hardware?目录,新建hello.h?文件:
[email protected]:~/Android$?cd?hardware/libhardware/include/hardware
[email protected]:~/Android/hardware/libhardware/include/hardware$?vi?hello.h
hello.h?文件的内容如下:
1.#ifndef?Android_HELLO_INTERFACE_H
2.#define?ANDROID_HELLO_INTERFACE_H
3.#include?<hardware/hardware.h>
4.
5.__BEGIN_DECLS
6.
7./*定义模块ID*/
8.#define?HELLO_HARDWARE_MODULE_ID?"hello"
9.
10./*硬件模块结构体*/
11.struct?hello_module_t?{
12.?struct?hw_module_t?common;
13.};
14.
15./*硬件接口结构体*/
16.struct?hello_device_t?{
17.?struct?hw_device_t?common;
18.?int?fd;
19.?int?(*set_val)(struct?hello_device_t*?dev,?int?val);
20.?int?(*get_val)(struct?hello_device_t*?dev,?int*?val);
21.};
22.
23.__END_DECLS
24.
25.#endif?这里按照Android?硬件抽象层规范的要求,分别定义模块ID、模块结构体以及硬件接口结构体。在硬件接口结构
体中,fd?表示设备文件描述符,对应我们将要处理的设备文件"/dev/hello",set_val?和get_val?为该HAL?对上提供的函数接口。
三.?进入到hardware/libhardware/modules?目录,新建hello?目录,并添加hello.c?文件。?hello.c?的内容较多,我们分段来看。
首先是包含相关头文件和定义相关结构:
1.#define?LOG_TAG?"HelloStub"
2.
3.#include?<hardware/hardware.h>
4.#include?<hardware/hello.h>
5.#include?<fcntl.h>
6.#include?<errno.h>
7.#include?<cutils/log.h>
8.#include?<cutils/atomic.h>
9.
10.#define?DEVICE_NAME?"/dev/hello"
11.#define?MODULE_NAME?"Hello"
12.#define?MODULE_AUTHOR?"[email protected]"
13.
14./*设备打开和关闭接口*/
15.static?int?hello_device_open(const?struct?hw_module_t*?module,?const?char*?name,?struct?hw_device_t**?device);
16.static?int?hello_device_close(struct?hw_device_t*?device);
17.
18./*设备访问接口*/
19.static?int?hello_set_val(struct?hello_device_t*?dev,?int?val);
20.static?int?hello_get_val(struct?hello_device_t*?dev,?int*?val);
21.
22./*模块方法表*/
23.static?struct?hw_module_methods_t?hello_module_methods?=?{
24.?open:?hello_device_open
25.};
26.
27./*模块实例变量*/
28.struct?hello_module_t?HAL_MODULE_INFO_SYM?=?{
29.?common:?{
30.?tag:?HARDWARE_MODULE_TAG,
31.?version_major:?1,
32.?version_minor:?0,
33.?id:?HELLO_HARDWARE_MODULE_ID,
34.?name:?MODULE_NAME,
35.?author:?MODULE_AUTHOR,
36.?methods:?&hello_module_methods,
37.?}
38.};
这里,实例变量名必须为HAL_MODULE_INFO_SYM,tag?也必须为HARDWARE_MODULE_TAG,这是Android?硬件抽象层规范规
定的。
定义hello_device_open?函数:
1.static?int?hello_device_open(const?struct?hw_module_t*?module,?const?char*?name,?struct?hw_device_t**?device)?{
2.?struct?hello_device_t*?dev;dev?=?(struct?hello_device_t*)malloc(sizeof(struct?hello_device_t));
2.
3.?if(!dev)?{
4.?LOGE("Hello?Stub:?failed?to?alloc?space");
5.?return?-EFAULT;
6.?}
7.
8.?memset(dev,?0,?sizeof(struct?hello_device_t));
9.?dev->common.tag?=?HARDWARE_DEVICE_TAG;
10.?dev->common.version?=?0;
11.?dev->common.module?=?(hw_module_t*)module;
12.?dev->common.close?=?hello_device_close;
13.?dev->set_val?=?hello_set_val;dev->get_val?=?hello_get_val;
14.
15.?if((dev->fd?=?open(DEVICE_NAME,?O_RDWR))?==?-1)?{
16.?LOGE("Hello?Stub:?failed?to?open?/dev/hello?--?%s.",?strerror(errno));free(dev);
17.?return?-EFAULT;?18.?}
19.
20.?*device?=?&(dev->common);
21.?LOGI("Hello?Stub:?open?/dev/hello?successfully.");
22.
23.?return?0;
24.}
DEVICE_NAME?定义为"/dev/hello"。由于设备文件是在内核驱动里面通过device_create?创建的,而device_create?创建的设备文
件默认只有root?用户可读写,而hello_device_open?一般是由上层APP?来调用的,这些APP?一般不具有root?权限,这时候就
导致打开设备文件失败:
Hello?Stub:?failed?to?open?/dev/hello?--?Permission?denied.
解决办法是类似于Linux?的udev?规则,打开Android?源代码工程目录下,进入到system/core/rootdir?目录,里面有一个名为
uevent.rc?文件,往里面添加一行:
/dev/hello?0666?root?root
定义hello_device_close、hello_set_val?和hello_get_val?这三个函数:
1.static?int?hello_device_close(struct?hw_device_t*?device)?{
2.?struct?hello_device_t*?hello_device?=?(struct?hello_device_t*)device;
3.
4.?if(hello_device)?{
5.?close(hello_device->fd);
6.?free(hello_device);
7.?}
8.
9.?return?0;
10.}
11.
12.static?int?hello_set_val(struct?hello_device_t*?dev,?int?val)?{
13.?LOGI("Hello?Stub:?set?value?%d?to?device.",?val);
14.
15.?write(dev->fd,?&val,?sizeof(val));
16.
17.?return?0;
18.}
19.
20.static?int?hello_get_val(struct?hello_device_t*?dev,?int*?val)?{
21.?if(!val)?{
22.?LOGE("Hello?Stub:?error?val?pointer");
23.?return?-EFAULT;
24.?}
25.
26.?read(dev->fd,?val,?sizeof(*val));
27.
28.?LOGI("Hello?Stub:?get?value?%d?from?device",?*val);
29.
30.?return?0;
31.}
四.?继续在hello?目录下新建Android.mk?文件:
LOCAL_PATH?:=?$(call?my-dir)
include?$(CLEAR_VARS)
LOCAL_MODULE_TAGS?:=?optional
LOCAL_PRELINK_MODULE?:=?false
LOCAL_MODULE_PATH?:=?$(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SHARED_LIBRARIES?:=?liblog
LOCAL_SRC_FILES?:=?hello.c
LOCAL_MODULE?:=?hello.default
include?$(BUILD_SHARED_LIBRARY)
注意,LOCAL_MODULE?的定义规则,hello?后面跟有default,hello.default?能够保证我们的模块总能被硬象抽象层加载到。
五.?编译:
[email protected]:~/Android$?mmm?hardware/libhardware/moudles/hello
编译成功后,就可以在out/target/product/generic/system/lib/hw?目录下看到hello.default.so?文件了。
六.?重新打包Android?系统镜像system.img:
[email protected]:~/Android$?make?snod
重新打包后,system.img?就包含我们定义的硬件抽象层模块hello.default?了。
虽然我们在Android?系统为我们自己的硬件增加了一个硬件抽象层模块,但是现在Java?应用程序还不能访问到我们的
硬件。我们还必须编写JNI?方法和在Android?的Application?Frameworks?层增加API?接口,才能让上层Application?访问我们的
硬件。在接下来的文章中,我们还将完成这一系统过程,使得我们能够在Java?应用程序中访问我们自己定制的硬件。
为Android?硬件抽象层(HAL)模块编写JNI?方法提供Java?访问硬件服务接口
在上两篇文章中(http://www.linuxidc.com/Linux/2011-07/38978.htm?与?http://www.linuxidc.com/Linux/2011-07/38980.htm),我
们介绍了如何为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?文件:
[email protected]:~/Android$?cd?frameworks/base/services/jni
[email protected]:[email protected]elloService.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?接口的硬件访问服务类。
首先是包含相应的头文件:
1.#define?LOG_TAG?"HelloService"
2.#include?"jni.h"
3.#include?"JNIHelp.h"
4.#include?"Android_runtime/AndroidRuntime.h"
5.#include?<utils/misc.h>
6.#include?<utils/Log.h>
7.#include?<hardware/hardware.h>
8.#include?<hardware/hello.h>
9.#include?<stdio.h>?接着定义hello_init、hello_getVal?和hello_setVal?三个JNI?方法:
1.namespace?Android
2.{
3.?/*在硬件抽象层中定义的硬件访问结构体,参考<hardware/hello.h>*/
4.?struct?hello_device_t*?hello_device?=?NULL;
5.?/*通过硬件抽象层定义的硬件访问接口设置硬件寄存器val?的值*/
6.?static?void?hello_setVal(JNIEnv*?env,?jobject?clazz,?jint?value)?{
7.?int?val?=?value;
8.?LOGI("Hello?JNI:?set?value?%d?to?device.",?val);
9.?if(!hello_device)?{
10.?LOGI("Hello?JNI:?device?is?not?open.");
11.?return;
12.?}
13.
14.?hello_device->set_val(hello_device,?val);
15.?}
16.?/*通过硬件抽象层定义的硬件访问接口读取硬件寄存器val?的值*/
17.?static?jint?hello_getVal(JNIEnv*?env,?jobject?clazz)?{
18.?int?val?=?0;
19.?if(!hello_device)?{
20.?LOGI("Hello?JNI:?device?is?not?open.");
21.?return?val;
22.?}
23.?hello_device->get_val(hello_device,?&val);
24.
25.?LOGI("Hello?JNI:?get?value?%d?from?device.",?val);
26.
27.?return?val;
28.?}
29.?/*通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/
30.?static?inline?int?hello_device_open(const?hw_module_t*?module,?struct?hello_device_t**?device)?{
31.?return?module->methods->open(module,?HELLO_HARDWARE_MODULE_ID,?(struct?hw_device_t**)device);
32.?}
33.?/*通过硬件模块ID?来加载指定的硬件抽象层模块并打开硬件*/
34.?static?jboolean?hello_init(JNIEnv*?env,?jclass?clazz)?{
35.?hello_module_t*?module;
36.
37.?LOGI("Hello?JNI:?initializing......");
38.?if(hw_get_module(HELLO_HARDWARE_MODULE_ID,?(const?struct?hw_module_t**)&module)?==?0)?{
39.?LOGI("Hello?JNI:?hello?Stub?found.");
40.?if(hello_device_open(&(module->common),?&hello_device)?==?0)?{
41.?LOGI("Hello?JNI:?hello?device?is?open.");
42.?return?0;
43.?}
44.?LOGE("Hello?JNI:?failed?to?open?hello?device.");
45.?return?-1;
46.?}
47.?LOGE("Hello?JNI:?failed?to?get?hello?stub?module.");
48.?return?-1;
49.?}
50.?/*JNI?方法表*/
51.?static?const?JNINativeMethod?method_table[]?=?{
52.?{"init_native",?"()Z",?(void*)hello_init},
53.?{"setVal_native",?"(I)V",?(void*)hello_setVal},
54.?{"getVal_native",?"()I",?(void*)hello_getVal},
55.?};
56.?/*注册JNI?方法*/
57.?int?register_Android_server_HelloService(JNIEnv?*env)?{
58.?return?jniRegisterNativeMethods(env,?"com/Android/server/HelloService",?method_table,?NELEM(method_table));
59.?}
60.};
注意,在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(JNIEnv?*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:
[email protected]:~/Android$?mmm?frameworks/base/services/jni
[email protected]:~/Android$?make?snod
这样,重新打包的system.img?镜像文件就包含我们刚才编写的JNI?方法了,也就是我们可以通过Android?系统的
Application?Frameworks?层提供的硬件服务HelloService?来调用这些JNI?方法,进而调用低层的硬件抽象层接口去访问硬件了。
前面提到,?在这篇文章中,?我们暂时忽略了HelloService?类的实现,?在下一篇文章中
http://www.linuxidc.com/Linux/2011-07/38982.htm,我们将描述如何实现硬件服务HelloService。
在Ubuntu?上为Android?系统的Application?Frameworks?层增加硬件访问服务
在数字科技日新月异的今天,软件和硬件的完美结合,造就了智能移动设备的流行。今天大家对iOS?和Android?系统的趋之若
鹜,一定程度上是由于这两个系统上有着丰富多彩的各种应用软件。因此,软件和硬件的关系,在一定程度上可以说,硬件
是为软件服务的。硬件工程师研发出一款硬件设备,自然少了软件工程师为其编写驱动程序;而驱动程序的最终目的,是为
了使得最上层的应用程序能够使用这些硬件提供的服务来为用户提供软件功能。对Android?系统上的应用软件来说,就是要
在系统的Application?Frameworks?层为其提供硬件服务。在前面的几篇文章中,我们着重介绍了Linux?内核层、硬件抽象层和
运行时库层提供的自定义硬件服务接口,这些接口都是通过C?或者C++语言来实现的。在这一篇文章中,我们将介绍如何在
Android?系统的Application?Frameworks?层提供Java?接口的硬件服务。
一.?参照在Ubuntu?为Android?硬件抽象层(HAL)模块编写JNI?方法提供Java?访问硬件服务接口一文所示,为硬件抽象层模块
准备好JNI?方法调用层。
二.?在Android?系统中,硬件服务一般是运行在一个独立的进程中为各种应用程序提供服务。因此,调用这些硬件服务的应用
程序与这些硬件服务之间的通信需要通过代理来进行。为此,?我们要先定义好通信接口。进入到
frameworks/base/core/java/android/os?目录,新增IHelloService.aidl?接口定义文件:
[email protected]:~/Android$?cd?frameworks/base/core/java/android/os
[email protected]:[email protected]e.aidl
IHelloService.aidl?定义了IHelloService?接口:
1.package?Android.os;
2.
3.interface?IHelloService?{
4.?void?setVal(int?val);
5.?int?getVal();?6.}
IHelloService?接口主要提供了设备和获取硬件寄存器val?的值的功能,分别通过setVal?和getVal?两个函数来实现。
三.返回到frameworks/base?目录,打开Android.mk?文件,修改LOCAL_SRC_FILES?变量的值,增加IHelloService.aidl?源文件:
##?READ?ME:?########################################################
##
##?When?updating?this?list?of?aidl?files,?consider?if?that?aidl?is
##?part?of?the?SDK?API.?If?it?is,?also?add?it?to?the?list?below?that
##?is?preprocessed?and?distributed?with?the?SDK.?This?list?should
##?not?contain?any?aidl?files?for?parcelables,?but?the?one?below?should
##?if?you?intend?for?3rd?parties?to?be?able?to?send?those?objects
##?across?process?boundaries.
##
##?READ?ME:?########################################################
LOCAL_SRC_FILES?+=?/
core/java/Android/os/IVibratorService.aidl?/
core/java/Android/os/IHelloService.aidl?/
core/java/Android/service/urlrenderer/IUrlRendererService.aidl?/
四.?编译IHelloService.aidl?接口:
[email protected]:~/Android$?mmm?frameworks/base
这样,就会根据IHelloService.aidl?生成相应的IHelloService.Stub?接口。
五.进入到frameworks/base/services/java/com/Android/server?目录,新增HelloService.java?文件:
1.package?com.Android.server;
2.import?Android.content.Context;
2.import?Android.os.IHelloService;
3.import?Android.util.Slog;
4.public?class?HelloService?extends?IHelloService.Stub?{
5.?private?static?final?String?TAG?=?"HelloService";
6.?HelloService()?{
7.?init_native();
8.?}
9.?public?void?setVal(int?val)?{
10.?setVal_native(val);
11.?}
12.?public?int?getVal()?{
13.?return?getVal_native();
14.?}
15.
16.?private?static?native?boolean?init_native();
17.?private?static?native?void?setVal_native(int?val);
18.?private?static?native?int?getVal_native();
19.};
HelloService?主要是通过调用JNI?方法init_native、setVal_native?和getVal_native(见在Ubuntu?为Android?硬件抽象层(HAL)模
块编写JNI?方法提供Java?访问硬件服务接口一文)来提供硬件服务。
六.?修改同目录的SystemServer.java?文件,在ServerThread::run?函数中增加加载HelloService?的代码:
@Override
public?void?run()?{
try?{
Slog.i(TAG,?"DiskStats?Service");
ServiceManager.addService("diskstats",?new?DiskStatsService(context));
}?catch?(Throwable?e)?{
Slog.e(TAG,?"Failure?starting?DiskStats?Service",?e);
}
try?{
Slog.i(TAG,?"Hello?Service");
ServiceManager.addService("hello",?new?HelloService());
}?catch?(Throwable?e)?{
Slog.e(TAG,?"Failure?starting?Hello?Service",?e);
}
}
七.?编译HelloService?和重新打包system.img:
[email protected]:~/Android$?mmm?frameworks/base/services/java
[email protected]:~/Android$?make?snod
这样,重新打包后的system.img?系统镜像文件就在Application?Frameworks?层中包含了我们自定义的硬件服务HelloService
了,并且会在系统启动的时候,自动加载HelloService。这时,应用程序就可以通过Java?接口来访问Hello?硬件服务了。我们
将?在下一篇文章http://www.linuxidc.com/Linux/2011-07/38983.htm中描述如何编写一个Java?应用程序来调用这个HelloService
接口来访问硬件。
为Android?系统内置Java?应用程序测试Application?Frameworks?层的硬件服务
我们在Android?系统增加硬件服务的目的是为了让应用层的APP?能够通过Java?接口来访问硬件服务。那么,?APP?如何通过Java
接口来访问Application?Frameworks?层提供的硬件服务呢?在这一篇文章中,我们将在Android?系统的应用层增加一个内置的
应用程序,这个内置的应用程序通过ServiceManager?接口获取指定的服务,然后通过这个服务来获得硬件服务。
一.?参照在Ubuntu?上为Android?系统的Application?Frameworks?层增加硬件访问服务
一文,在Application?Frameworks?层定义好自己的硬件服务HelloService,并提供IHelloService?接口提供访问服务。
二.?为了方便开发,我们可以在IDE?环境下使用Android?SDK?来开发Android?应用程序。开发完成后,再把程序源代码移植到
Android?源代码工程目录中。使用Eclipse?的Android?插件ADT?创建Android?工程很方便,这里不述,可以参考网上其它资料。
工程名称为Hello,下面主例出主要文件:
主程序是src/shy/luo/hello/Hello.java:
1.package?shy.luo.hello;
2.
3.import?shy.luo.hello.R;
4.import?Android.app.Activity;
5.import?Android.os.ServiceManager;
6.import?Android.os.Bundle;
7.import?Android.os.IHelloService;
8.import?Android.os.RemoteException;
9.import?Android.util.Log;
10.import?Android.view.View;
11.import?Android.view.View.OnClickListener;
12.import?Android.widget.Button;
13.import?Android.widget.EditText;
14.
15.public?class?Hello?extends?Activity?implements?OnClickListener?{
16.?private?final?static?String?LOG_TAG?=?"shy.luo.renju.Hello";
17.
18.?private?IHelloService?helloService?=?null;
19.
20.?private?EditText?valueText?=?null;
21.?private?Button?readButton?=?null;
22.?private?Button?writeButton?=?null;
23.?private?Button?clearButton?=?null;
24.
25.?/**?Called?when?the?activity?is?first?created.?*/
27.?public?void?onCreate(Bundle?savedInstanceState)?{
28.?super.onCreate(savedInstanceState);
29.?setContentView(R.layout.main);
30.
31.?helloService?=?IHelloService.Stub.asInterface(
32.?ServiceManager.getService("hello"));
33.
34.?valueText?=?(EditText)findViewById(R.id.edit_value);
35.?readButton?=?(Button)findViewById(R.id.button_read);
36.?writeButton?=?(Button)findViewById(R.id.button_write);
37.?clearButton?=?(Button)findViewById(R.id.button_clear);
38.
39.?readButton.setOnClickListener(this);
40.?writeButton.setOnClickListener(this);
41.?clearButton.setOnClickListener(this);
42.
43.?Log.i(LOG_TAG,?"Hello?Activity?Created");
44.?}
45.
47.?public?void?onClick(View?v)?{
48.?if(v.equals(readButton))?{
49.?try?{
50.?int?val?=?helloService.getVal();
51.?String?text?=?String.valueOf(val);
52.?valueText.setText(text);
53.?}?catch?(RemoteException?e)?{
54.?Log.e(LOG_TAG,?"Remote?Exception?while?reading?value?from?device.");
55.?}
56.?}
57.?else?if(v.equals(writeButton))?{
58.?try?{
59.?String?text?=?valueText.getText().toString();
60.?int?val?=?Integer.parseInt(text);
61.?helloService.setVal(val);
62.?}?catch?(RemoteException?e)?{
63.?Log.e(LOG_TAG,?"Remote?Exception?while?writing?value?to?device.");
64.?}
65.?}
66.?else?if(v.equals(clearButton))?{
67.?String?text?=?"";
68.?valueText.setText(text);
69.?}
70.?}
71.}
程序通过ServiceManager.getService("hello")来获得HelloService,接着通过IHelloService.Stub.asInterface?函数转换为IHelloService
接口。其中,服务名字“hello”是系统启动时加载HelloService?时指定的,而IHelloService?接口定义在Android.os.IHelloService
中,具体可以参考在Ubuntu?上为Android?系统的Application?Frameworks?层增加硬件访问服务一文。这个程序提供了简单的读
定自定义硬件有寄存器val?的值的功能,通过IHelloService.getVal?和IHelloService.setVal?两个接口实现。
界面布局文件res/layout/main.xml:
1.<?xml?version="1.0"?encoding="utf-8"?>
2.?<LinearLayout?xmlns:Android="http://schemas.android.com/apk/res/android"
3.?Android:orientation="vertical"
4.?Android:layout_width="fill_parent"
5.?Android:layout_height="fill_parent">
6.?<LinearLayout
7.?Android:layout_width="fill_parent"
8.?Android:layout_height="wrap_content"
9.?Android:orientation="vertical"
10.?Android:gravity="center">
11.?<TextView
12.?Android:layout_width="wrap_content"
13.?Android:layout_height="wrap_content"
14.?Android:text="@string/value">
15.?</TextView>
16.?<EditText
17.?Android:layout_width="fill_parent"
18.?Android:layout_height="wrap_content"
19.?Android:id="@+id/edit_value"
20.?Android:hint="@string/hint">
21.?</EditText>
22.?</LinearLayout>
23.?<LinearLayout
24.?Android:layout_width="fill_parent"
25.?Android:layout_height="wrap_content"
26.?Android:orientation="horizontal"
27.?Android:gravity="center">
28.?<Button
29.?Android:id="@+id/button_read"
30.?Android:layout_width="wrap_content"
31.?Android:layout_height="wrap_content"
32.?Android:text="@string/read">
33.?</Button>
34.?<Button
35.?Android:id="@+id/button_write"
36.?Android:layout_width="wrap_content"
37.?Android:layout_height="wrap_content"
38.?Android:text="@string/write">
39.?</Button>
40.?<Button
41.?Android:id="@+id/button_clear"
42.?Android:layout_width="wrap_content"
43.?Android:layout_height="wrap_content"
44.?Android:text="@string/clear">
45.?</Button>
46.?</LinearLayout>
47.?</LinearLayout>?字符串文件res/values/strings.xml:
1.<?xml?version="1.0"?encoding="utf-8"?>
2.?<resources>
3.?<string?name="app_name">Hello</string>
4.?<string?name="value">Value</string>
5.?<string?name="hint">Please?input?a?value...</string>
6.?<string?name="read">Read</string>
7.?<string?name="write">Write</string>
8.?<string?name="clear">Clear</string>
9.?</resources>?程序描述文件AndroidManifest.xml:
1.<?xml?version="1.0"?encoding="utf-8"?>
2.?<manifest?xmlns:Android="http://schemas.android.com/apk/res/android"
3.?package="shy.luo.hello"
4.?Android:versionCode="1"
5.?Android:versionName="1.0">
6.?<application?Android:icon="@drawable/icon"?android:label="@string/app_name">
7.?<activity?Android:name=".Hello"
8.?Android:label="@string/app_name">
9.?<intent-filter>
10.?<action?Android:name="android.intent.action.MAIN"?/>
11.?<category?Android:name="android.intent.category.LAUNCHER"?/>
12.?</intent-filter>
13.?</activity>
14.?</application>
15.?<uses-sdk
16.?Android:minSdkVersion="7"
17.?Android:targetSdkVersion="7">
18.?</uses-sdk>
19.?</manifest>
三.?将Hello?目录拷贝至packages/experimental?目录,新增Android.mk?文件:
[email protected]:~/Android/packages/experimental$?vi?Android.mkAndroid.mk?的文件内容如下:
LOCAL_PATH:=?$(call?my-dir)
include?$(CLEAR_VARS)
LOCAL_MODULE_TAGS?:=?optional
LOCAL_SRC_FILES?:=?$(call?all-subdir-java-files)
LOCAL_PACKAGE_NAME?:=?Hello
include?$(BUILD_PACKAGE)
四.?编译:
[email protected]:~/Android$?mmm?packages/experimental/Hello
编译成功后,便可以在out/target/product/generic/system/app?目录下看到Hello.apk?文件了。
五.?重新打包系统镜像文件system.img:
[email protected]:~/Android$?make?snod
重新打包后的system.img?文件就内置了Hello.apk?文件了。
六.?运行Android?模拟器:
[email protected]:[email protected]?&
在Home?Screen?中可以看到Hello?应用程序:
打开Hello?应用程序:
点击Read?按钮,可以从HelloService?中读取硬件寄存器val?的值;点击Clear?按钮,可以清空文本框的值;在文本框中输入一
个数值,再点击Write?按钮,便可以将这个值写入到硬件寄存器val?中去,可以再次点击Read?按钮来验证是否正确写入了值。
至此,我们就完整地学习了在Android?的Linux?内核空间添加硬件驱动程序、在Android?的硬件抽象层添加硬件接口、在Android
的Application?Frameworks?层提供硬件服务以及在Android?的应用层调用硬件服务的整个过程了,希望能为读者进入Android
系统提供入门帮助。重新学习整个过程,请参考Android?硬件抽象层(HAL)概要介绍和学习计划。
本篇文章来源于?Linux?公社网站,原文链接:http://www.linuxidc.com/Linux/2011-07/38980.htm__