Android 硬件抽象层(HAL)概要介绍和学习计划

Android驱动开发全过程(有图有真相) - 莫回头 - 博客园 (cnblogs.com)

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 目录:

linuxidc@www.linuxidc.com:~/Android$ cd kernel/common/drivers

linuxidc@www.linuxidc.com:~/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_openhello_releasehello_readhello_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_showhello_val_store两个方法,同时定义了两个内部使用的访问val值的方法__hello_get_val

__hello_set_val

1./*读取寄存器val的值到缓冲区buf中,内部使用*/

2.staticssize_t __hello_get_val(structhello_Android_dev* dev,char* buf) {

3.intval = 0;

4.

5./*同步访问*/

6.if(down_interruptible(&(dev->sem))) {

7.return-ERESTARTSYS;

8. }

9.

10. val = dev->val;

11. up(&(dev->sem));

12.

13.returnsnprintf(buf, PAGE_SIZE,"%d/n", val);

14. }

15.

16./*把缓冲区buf的值写到设备寄存器val中去,内部使用*/

17.staticssize_t __hello_set_val(structhello_Android_dev* dev,constchar* buf,size_tcount) {

18.intval = 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.returncount;

32. }

33.

34./*读取设备属性val*/

35.staticssize_t hello_val_show(structdevice* dev,structdevice_attribute* attr,char* buf) {

36.structhello_Android_dev* hdev = (structhello_android_dev*)dev_get_drvdata(dev);

37.

38.return__hello_get_val(hdev, buf);

39. }

40.

41./*写设备属性val*/

42.staticssize_t hello_val_store(structdevice* dev,structdevice_attribute* attr,constchar* buf,size_tcoun

t) {

43.structhello_Android_dev* hdev = (structhello_android_dev*)dev_get_drvdata(dev);

44.

45.return__hello_set_val(hdev, buf, count);

46. }

定义通过proc文件系统访问方法,主要实现了hello_proc_readhello_proc_write两个方法,同时定义了在proc文件系统

创建和删除文件的方法hello_create_prochello_remove_proc

1./*读取设备寄存器val的值,保存在page缓冲区中*/

2.staticssize_t hello_proc_read(char* page,char** start, off_t off,intcount,int* eof,void* data) {

3.if(off > 0) {

4. *eof = 1;

5.return0;

6. }

7.

8.return__hello_get_val(hello_dev, page);

9. }

10.

11./*把缓冲区的值buff保存到设备寄存器val中去*/

12.staticssize_t hello_proc_write(structfile* filp,constchar__user *buff, unsignedlonglen,void* data) {

13.interr = 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.gotoout;

32. }

33.

34. err = __hello_set_val(hello_dev, page, len);

35.

36. out:

37. free_page((unsignedlong)page);

38.returnerr;

39. }

40.

41./*创建/proc/hello文件*/

42.static voidhello_create_proc(void) {

43.structproc_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 voidhello_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目录中新增KconfigMakefile两个文件,其中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/Kconfigdrivers/kconfig两个文件,在menu "Device Drivers"endmenu之间添加一行:

source "drivers/hello/Kconfig"

这样,执行make menuconfig时,就可以配置hello模块的编译选项了。.

.修改drivers/Makefile文件,添加一行:

obj-$(CONFIG_HELLO) += hello/

.配置编译选项:

linuxidc@www.linuxdc.com:~/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选项。

.编译:

linuxidc@www.linuxdc.com:~/Android/kernel/common$ make

编译成功后,就可以在hello目录下看到hello.o文件了,这时候编译出来的zImage已经包含了hello驱动。

.参照在Ubuntu上下载、编译和安装Android最新内核源代码(Linux Kernel)一文所示,运行新编译的内核文件,

验证hello驱动程序是否已经正常安装:

linuxidc@www.linuxdc.com:~/Android$ emulator -kernel ./kernel/common/arch/arm/boot/zImage &

linuxidc@www.linuxdc.com:~/Android$ adb shell

进入到dev目录,可以看到hello设备文件:

root@Android:/ # cd dev

root@Android:/dev # ls

进入到proc目录,可以看到hello文件:

root@Android:/ # cd proc

root@Android:/proc # ls

访问hello文件的值:

root@Android:/proc # cat hello

0

root@Android:/proc # echo '5' > hello

root@Android:/proc # cat hello

5

进入到sys/class目录,可以看到hello目录:

root@Android:/ # cd sys/class

root@Android:/sys/class # ls

进入到hello目录,可以看到hello目录:

root@Android:/sys/class # cd hello

root@Android:/sys/class/hello # ls

进入到下一层hello目录,可以看到val文件:

root@Android:/sys/class/hello # cd hello

root@Android:/sys/class/hello/hello # ls

访问属性文件val的值:

root@Android:/sys/class/hello/hello # cat val

5

root@Android:/sys/class/hello/hello # echo '0' > val

root@Android:/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

内核驱动程序。在这个名为helloLinux内核驱动程序中,创建三个不同的文件节点来供用户空间访问,分别是传统的设备

文件/dev/helloproc系统文件/proc/hellodevfs系统属性文件/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目录:

linuxidc@www.linuxidc.com:~/Android$ cd external

linuxidc@www.linuxidc.com:~/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命令进行编译:

linuxidc@www.linuxidc.com:~/Android$ mmm ./external/hello

编译成功后,就可以在out/target/product/gerneric/system/bin目录下,看到可执行文件hello了。

.重新打包Android系统文件system.img

linuxidc@www.linuxidc.com:~/Android$ make snod

这样,重新打包后的system.img文件就包含刚才编译好的hello可执行文件了。

.运行模拟器,使用/system/bin/hello可执行程序来访问Linux内核驱动程序:

linuxidc@www.linuxidc.com:~/Android$ emulator -kernel ./kernel/common/arch/arm/boot/zImage &

linuxidc@www.linuxidc.com:~/Android$ adb shell

root@Android:/ # cd system/bin

root@Android:/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

统创建设备文件时用类似Linuxudev规则修改设备文件模式的方法。

.参照在Ubuntu上为Android系统编写Linux内核驱动程序一文所示,准备好示例内核驱动序。完成这个内核驱动程序后,

便可以在Android系统中得到三个文件,分别是/dev/hello/sys/class/hello/hello/val/proc/hello。在本文中,我们将通过设

备文件/dev/hello来连接硬件抽象层模块和Linux内核驱动程序模块。

.进入到在hardware/libhardware/include/hardware目录,新建hello.h文件:

linuxidc@www.linuxidc.com:~/Android$ cd hardware/libhardware/include/hardware

linuxidc@www.linuxidc.com:~/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_valget_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 "shyluo@gmail.com"

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_SYMtag也必须为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.

解决办法是类似于Linuxudev规则,打开Android源代码工程目录下,进入到system/core/rootdir目录,里面有一个名为

uevent.rc文件,往里面添加一行:

/dev/hello 0666 root root

定义hello_device_closehello_set_valhello_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后面跟有defaulthello.default能够保证我们的模块总能被硬象抽象层加载到。

.编译:

USER-NAME@MACHINE-NAME:~/Android$ mmm hardware/libhardware/moudles/hello

编译成功后,就可以在out/target/product/generic/system/lib/hw目录下看到hello.default.so文件了。

.重新打包Android系统镜像system.img

USER-NAME@MACHINE-NAME:~/Android$ make snod

重新打包后,system.img就包含我们定义的硬件抽象层模块hello.default了。

虽然我们在Android系统为我们自己的硬件增加了一个硬件抽象层模块,但是现在Java应用程序还不能访问到我们的

硬件。我们还必须编写JNI方法和在AndroidApplication Frameworks层增加API接口,才能让上层Application访问我们的

硬件。在接下来的文章中,我们还将完成这一系统过程,使得我们能够在Java应用程序中访问我们自己定制的硬件。

Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口

在上两篇文章中(http://www.linuxidc.com/Linux/2011-07/38978.htmhttp://www.linuxidc.com/Linux/2011-07/38980.htm),我

们介绍了如何为Android系统的硬件编写驱动程序,包括如何在Linux内核空间实现内核驱动程序和在用户空间实现硬件抽象

层接口。实现这两者的目的是为了向更上一层提供硬件访问接口,即为AndroidApplication 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文件:

linuxidc@www.linuxidc.com:~/Android$ cd frameworks/base/services/jni

linuxidc@www.linuxidc.com:~/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接口的硬件访问服务类。

首先是包含相应的头文件:

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_inithello_getValhello_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方法来加载模块IDHELLO_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

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类的实现, 在下一篇文章中

http://www.linuxidc.com/Linux/2011-07/38982.htm,我们将描述如何实现硬件服务HelloService

Ubuntu上为Android系统的Application Frameworks层增加硬件访问服务

在数字科技日新月异的今天,软件和硬件的完美结合,造就了智能移动设备的流行。今天大家对iOSAndroid系统的趋之若

鹜,一定程度上是由于这两个系统上有着丰富多彩的各种应用软件。因此,软件和硬件的关系,在一定程度上可以说,硬件

是为软件服务的。硬件工程师研发出一款硬件设备,自然少了软件工程师为其编写驱动程序;而驱动程序的最终目的,是为

了使得最上层的应用程序能够使用这些硬件提供的服务来为用户提供软件功能。对Android系统上的应用软件来说,就是要

在系统的Application Frameworks层为其提供硬件服务。在前面的几篇文章中,我们着重介绍了Linux内核层、硬件抽象层和

运行时库层提供的自定义硬件服务接口,这些接口都是通过C或者C++语言来实现的。在这一篇文章中,我们将介绍如何在

Android系统的Application Frameworks层提供Java接口的硬件服务。

.参照在UbuntuAndroid硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口一文所示,为硬件抽象层模块

准备好JNI方法调用层。

.Android系统中,硬件服务一般是运行在一个独立的进程中为各种应用程序提供服务。因此,调用这些硬件服务的应用

程序与这些硬件服务之间的通信需要通过代理来进行。为此, 我们要先定义好通信接口。进入到

frameworks/base/core/java/android/os目录,新增IHelloService.aidl接口定义文件:

linuxidc@www.linuxidc.com:~/Android$ cd frameworks/base/core/java/android/os

linuxidc@www.linuxidc.com:~/Android/frameworks/base/core/java/android/os$ vi IHelloService.aidl

IHelloService.aidl定义了IHelloService接口:

1.package Android.os;

2.

3.interface IHelloService {

4. void setVal(int val);

5. int getVal(); 6.}

IHelloService接口主要提供了设备和获取硬件寄存器val的值的功能,分别通过setValgetVal两个函数来实现。

.返回到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接口:

linuxidc@www.linuxidc.com:~/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_nativesetVal_nativegetVal_native(见在UbuntuAndroid硬件抽象层(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

linuxidc@www.linuxidc.com:~/Android$ mmm frameworks/base/services/java

linuxidc@www.linuxidc.com:~/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源代码工程目录中。使用EclipseAndroid插件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. */

26. @Override

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.

46. @Override

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.getValIHelloService.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文件:

linuxidc@www.linuxidc.com:~/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)

.编译:

linuxidc@www.linuxidc.com:~/Android$ mmm packages/experimental/Hello

编译成功后,便可以在out/target/product/generic/system/app目录下看到Hello.apk文件了。

.重新打包系统镜像文件system.img

linuxidc@www.linuxidc.com:~/Android$ make snod

重新打包后的system.img文件就内置了Hello.apk文件了。

.运行Android模拟器:

linuxidc@www.linuxidc.com:~/Android$ emulator -kernel kernel/common/arch/arm/boot/zImage &

Home Screen中可以看到Hello应用程序:

打开Hello应用程序:

点击Read按钮,可以从HelloService中读取硬件寄存器val的值;点击Clear按钮,可以清空文本框的值;在文本框中输入一

个数值,再点击Write按钮,便可以将这个值写入到硬件寄存器val中去,可以再次点击Read按钮来验证是否正确写入了值。

至此,我们就完整地学习了在AndroidLinux内核空间添加硬件驱动程序、在Android的硬件抽象层添加硬件接口、在Android

Application Frameworks层提供硬件服务以及在Android的应用层调用硬件服务的整个过程了,希望能为读者进入Android

系统提供入门帮助。重新学习整个过程,请参考Android硬件抽象层(HAL)概要介绍和学习计划。

本篇文章来源于Linux公社网站,原文链接:http://www.linuxidc.com/Linux/2011-07/38980.htm__