Android驱动开发全过程

Android驱动开发全过程(有图有真相)

前言

意外在网上发现了这扁文章,看后感觉很有必要分享,所以整理并上传,希望大家喜欢。

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./*传统的设备文件操作方法*/ open/release/read/write

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 文件系统访问方法  show/store/get/set

这里把设备的寄存器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 文件系统访问方法. read/write/create/remove

主要实现了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/

八. 配置编译选项:

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 系统中增加可执行程序来访问硬件驱动程序。

在前一篇文章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 目录:

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 系

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

一. 参照在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_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 "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_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 能够保证我们的模块总能被硬象抽象层加载到。

五. 编译:

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 方法和在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 文件:

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_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:

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 层增加硬件访问服务

在数字科技日新月异的今天,软件和硬件的完美结合,造就了智能移动设备的流行。今天大家对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 接口定义文件:

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 的值的功能,分别通过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 接口:

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_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:

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 源代码工程目录中。使用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. */

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

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 按钮来验证是否正确写入了值。

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

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

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

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

  • 34
    点赞
  • 252
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Android开发案例驱动教程》 配套代码。 注: 由于第12,13,14章代码太大,无法上传到一个包中。 这三节代码会放到其他压缩包中。 作者:关东升,赵志荣 Java或C++程序员转变成为Android程序员 采用案例驱动模式展开讲解知识点,即介绍案例->案例涉及技术->展开知识点->总结的方式 本书作者从事多年一线开发和培训,讲解知识点力求细致,深入浅出 目 录 前言 第1章 Android操作系统概述 1 1.1 Android历史介绍 1 1.2 Android架构 1 1.3 Android平台介绍 2 1.4 现有智能手机操作系统比较 4 第2章 Android开发环境搭建 5 2.1 Eclipse和ADT插件 5 2.1.1 Eclipse安装 5 2.1.2 ADT插件 6 2.2 Android SDK 8 2.2.1 Android SDK的获得 8 2.2.2 Android SDK版本说明 10 2.2.3 ADT配置 10 2.3 Android开发模拟器 11 2.3.1 创建模拟器 11 2.3.2 启动模拟器 13 2.3.3 键盘映射与模拟器控制 13 2.3.4 横屏与竖屏切换 14 第3章 第一个Android程序 15 3.1 HelloAndroid 15 3.1.1 在Eclipse中创建项目 15 3.1.2 编写程序项目代码 17 3.1.3 运行HelloAndroid 18 3.1.4 Android工程目录 19 3.1.5 AndroidManifest.xml文件 21 3.2 Android中的组件介绍 22 3.3 使用Android SDK帮助 23 3.3.1 Android SDK API文档 23 3.3.2 Android SDK开发指南 24 3.3.3 Android SDK samples 24 3.4 使用DDMS帮助调试程序 26 3.4.1 启动DDMS 26 3.4.2 Device 28 3.4.3 Emulator Control 29 3.4.4 File Explorer 30 3.4.5 LogCat 31 3.5 使用ADB帮助调试程序 33 3.5.1 查询模拟器实例和设备 34 3.5.2 进入shell 34 3.5.3 导入导出文件 35 3.6 应用程序的打包、安装和卸载 37 3.6.1 应用程序打包 37 3.6.2 应用程序安装 40 3.6.3 应用程序卸载 40 本章小结 42 第4章 UI基础知识 43 4.1 Android UI组件概述 43 4.1.1 View 43 4.1.2 ViewGroup 44 4.1.3 布局管理器 44 4.2 UI设计工具 44 4.2.1 DroidDraw工具 44 4.2.2 ADT插件UI设计工具 46 4.3 事件处理模型 47 4.3.1 接口实现事件处理模型 47 4.3.2 内部类事件处理模型 49 4.3.3 匿名内部类事件处理模型 51 4.4 Activity中的常用事件 53 4.4.1 触摸事件 53 4.4.2 键盘事件 55 4.5 菜单 57 4.5.1 文本菜单 57 4.5.2 图片文本菜单 59 本章小结 60 第5章 UI基础控件 61 5.1 按钮 61 5.1.1 Button 62 5.1.2 ImageButton 63 5.1.3 ToggleButton 64 5.2 TextView 64 5.3 EditText 65 5.4 RadioButton和RadioGroup 66 5.4.1 RadioButton 66 5.4.2 RadioGroup 67 5.5 CheckBox 68 5.6 ImageView 70 5.7 Progress Bar 70 5.7.1 条状进度条 71 5.7.2 圆形进度条 73 5.7.3 对话框进度条 74 5.7.4 标题栏中进度条 75 5.8 SeekBar 76 5.9 RatingBar 78 本章小结 82 第6章 UI高级控件 83 6.1 列表类控件 83 6.1.1 Adapter概念 83 6.1.2 AutoComplete 84 6.1.3 Spinner 87 6.1.4 ListView 90 6.1.5 GridView 96 6.1.6 Gallery 99 6.2 Toast 103 6.2.1 文本类型 103 6.2.2 图片类型 104 6.2.3 复合类型 105 6.2.4 自定义显示位置Toast 106 6.3 对话框 107 6.3.1 文本信息对话框 107 6.3.2 简单列表项对话框 109 6.3.3 单选项列表项对话框 111 6.3.4 复选框列表项对话框 113 6.3.5 复杂布局列表项对话框 115 6.4 Android国际化和本地化 118 本章小结 121 第7章 UI布局 122 7.1 FrameLayout 122 7.1.1 TextSwitcher 124 7.1.2 ImageSwitcher 126 7.1.3 DatePicker 129 7.1.4 TimePicker 131 7.1.5 ScrollView 133 7.1.6 选项卡 134 7.2 LinearLayout 138 7.3 RelativeLayout 139 7.4 AbsoluteLayout 141 7.5 TableLayout 143 7.6 布局嵌套 146 7.7 屏幕旋转 152 本章小结 154 第8章 多线程 155 8.1 多线程案例--计时器 155 8.2 线程概念 156 8.2.1 进程概念 156 8.2.2 线程概念 156 8.3 Java中的线程 157 8.3.1 Java中的实现线程体方式1 157 8.3.2 Java中的实现线程体方式2 160 8.3.3 Java中的实现线程体方式3 162 8.4 Android中的线程 163 8.4.1 Android线程应用中的问题与分析 164 8.4.2 Message和MessageQueue 169 8.4.3 Handler 169 8.4.4 Looper和HandlerThread 172 本章小结 178 第9章 Activity和Intent 179 9.1 Activity 179 9.1.1 创建Activity 179 9.1.2 Activity生命周期 180 9.2 Intent 183 9.2.1 显式Intent 184 9.2.2 隐式Intent 186 9.2.3 匹配组件 186 9.3 多Activity之间跳转 188 9.3.1 多个Activity之间数据传递 189 9.3.2 跳转与返回 192 9.3.3 任务与标志 196 9.4 Android系统内置Intent 199 本章小结 201 第10章 数据存储 203 10.1 健康助手案例 203 10.2 Android数据存储概述 205 10.3 本地文件 205 10.3.1 访问SD卡 207 10.3.2 访问应用文件目录 212 10.4 SQLite数据库 216 10.4.1 SQLite数据类型 216 10.4.2 Android平台下管理SQLite数据库 216 10.5 编写访问SQLite数据库组件 220 10.5.1 DBHelper类 220 10.5.2 数据插入 222 10.5.3 数据删除 224 10.5.4 数据修改 224 10.5.5 数据查询 227 10.6 案例重构 229 10.6.1 系统架构设计 229 10.6.2 重构数据访问层 230 10.7 为案例增加参数设置功能 238 10.7.1 Shared Preferences 240 10.7.2 Preferences控件介绍 243 10.7.3 使用Preferences控件的案例 248 本章小结 250 第11章 Content Provider 251 11.1 Content Provider概述 251 11.2 Content URI 252 11.2.1 Content URI含义 252 11.2.2 内置的Content URI 253 11.3 通过Content Provider访问联系人 253 11.3.1 查询联系人 255 11.3.2 通过联系人ID查询联系人的Email 258 11.3.3 按照过滤条件查询Email 259 11.3.4 查询联系人的电话 261 11.4 通过Content Provider访问通话记录 262 11.4.1 查询通话记录 262 11.4.2 按照过滤条件查询通话记录 264 11.5 通过Content Provider访问短信 266 11.6 自定义Content Provider实现数据访问 269 11.6.1 编写Content Provider 269 11.6.2 在不同的应用中调用Content Provider 277 11.6.3 重构Content Provider调用 278 本章小结 281 第12章 多媒体 282 12.1 多媒体文件介绍 282 12.1.1 音频多媒体文件介绍 282 12.1.2 视频多媒体文件介绍 283 12.2 Android音频播放 284 12.2.1 Android音频/视频播放状态 284 12.2.2 音频播放案例介绍 286 12.2.3 资源音频文件播放 287 12.2.4 本地音频文件播放 291 12.2.5 网络音频文件播放 292 12.2.6 完善案例其他功能 293 12.3 Android音频录制 303 12.3.1 Android音频/视频录制状态 303 12.3.2 音频录制案例介绍 303 12.3.3 音频录制案例实现 305 12.4 Android视频播放 309 12.4.1 视频播放案例 309 12.4.2 采用MediaPlayer类播放视频 310 12.4.3 使用VideoView控件重构案例 315 本章小结 316 第13章 Service 317 13.1 Service概述 317 13.1.1 本地Service生命周期 317 13.1.2 远程Service生命周期 318 13.2 本地Service 319 13.2.1 本地Service案例 319 13.2.2 编写AudioService 320 13.2.3 调用Service 322 13.2.4 重构案例 323 13.3 远程Service 325 13.3.1 远程Service调用原理 325 13.3.2 远程Service案例 326 13.3.3 设计AIDL文件 327 13.3.4 编写AudioService 331 13.3.5 调用远程Service 336 13.3.6 组件间参数传递 343 本章小结 347 第14章 Broadcast Receiver和Notification 348 14.1 Broadcast Receiver 348 14.1.1 音频播放案例 349 14.1.2 编写音频播放Broadcast Receiver 350 14.1.3 注册音频播放Broadcast Receiver 351 14.1.4 接收系统的广播 353 14.1.5 MP3下载服务案例 353 14.2 Notification 358 14.2.1 完善MP3下载服务案例 358 14.2.2 完善音频播放案例 363 14.2.3 其他形式的Notification 369 本章小结 371 第15章 云端应用 372 15.1 典型云端应用--城市天气信息服务 372 15.2 网络通信技术与实现 374 15.2.1 网络通信技术介绍 376 15.2.2 Java URL类实现方式 377 15.2.3 Apache HttpClient实现方式 378 15.3 数据交换格式 380 15.3.1 纯文本格式 381 15.3.2 XML格式 381 15.3.3 JSON格式 385 15.4 自定义服务器端程序实例 387 15.4.1 Java Servlet概述 387 15.4.2 编写城市信息服务的Servlet 388 15.4.3 编写城市天气服务的Servlet 393 15.4.4 再次探讨HttpClient的POST请求 395 15.5 云端应用案例优化 400 本章小结 404 第16章 Google Map和定位服务 405 16.1 MyMap服务系统案例 405 16.2 Android Google Map 406 16.2.1 申请Google Map Android API Key 407 16.2.2 编写Android Google Map骨架程序 409 16.2.3 控制地图 412 16.2.4 地图的显示模式 416 16.2.5 地图的图层 419 16.2.6 查询与定位 422 16.3 Android定位服务 430 16.3.1 开启定位服务 431 16.3.2 模拟测试 433 16.3.3 GPS与Google Map结合 435 16.4 案例重构 437 16.4.1 重构"定位查询"方法 438 16.4.2 重构"查询周围"方法 440 本章小结 443 第17章 Android通信应用 444 17.1 电话应用开发 444 17.1.1 拨打电话功能 444 17.1.2 呼入电话状态 446 17.2 短信和彩信应用开发 450 17.2.1 Android内置的发送短信/彩信功能 450 17.2.2 自己编写发送文本内容的短信 452 17.2.3 自己编写接收文本内容的短信 458 17.2.4 自己编写发送二进制内容的短信 459 17.2.5 自己编写接收二进制内容的短信 461 17.3 蓝牙通信 463 17.3.1 Android 2 BluetoothChat案例 464 17.3.2 Android 2 蓝牙API介绍 464 17.3.3 TCP Socket与蓝牙Socket的区别 465 17.3.4 BluetoothChat中的类 466 17.3.5 初始化本地蓝牙设备 467 17.3.6 查找蓝牙设备 471 17.3.7 管理连接 476 17.3.8 互相之间的通信 480 17.4 WiFi通信 484 17.4.1 管理WiFi 484 17.4.2 扫描热点 487 17.4.3 Socket通信 489
学习全仓代码下载编译、拉单仓和极速构建需要掌握以下几个方面: 1. 版本控制工具:Git是目前最流行的版本控制工具,你需要学会使用Git来管理代码。 2. 编译工具:Android的编译工具是Gradle,你需要学会如何使用Gradle进行编译。 3. 构建工具:Android的构建工具是Fastlane,你需要学会如何使用Fastlane进行快速构建。 4. 熟悉Android的架构和驱动开发:你需要了解Android的架构和驱动开发的基本概念和流程,这样才能更好地理解全仓代码的编译和拉单仓的过程。 5. 学习相关的命令行工具:在编译和构建过程中,你需要使用一些命令行工具,如adb、make等,因此你需要学习这些命令行工具的使用方法。 建议你可以通过以下方式来学习以上技能: 1. 参加相关的培训课程:可以参加一些针对Android驱动开发的培训课程,例如官方提供的Android驱动开发培训课程,或者一些知名的在线培训机构提供的相关课程。 2. 学习相关的书籍:可以购买一些相关的书籍,例如《Android系统源代码情景分析》、《深入理解Android:卷1》等。 3. 学习在线教程:可以在一些知名的在线教程网站上学习相关知识,例如CSDN、Stack Overflow等。 4. 参与开源项目:可以参与一些开源项目,通过参与项目的开发来学习相关技能。 总之,学习全仓代码下载编译、拉单仓和极速构建需要你具备一定的编程基础和相关的知识积累,需要不断学习、实践和总结,才能掌握这些技能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值