android 开发六层全解释--转自合并老罗博客

Android五层框架驱动编写(完整篇) Android五层框架驱动编写 一、框架介绍 1.应用程序层(java) 应用程序。 2.应用程序框架层(java) 与系统运行库被称为“C库层”相对应,应用程序框架层往往被冠以“JAVA库”的称号。这是因为框架层所提供的组件一般都是以JAVA语言编写而成,他一方面为上层应用程序提供了API接口;另一方面也囊括了不少系统级服务进程的实现,是与Android应用程序开发者关系最直接的一层。 3.系... 阅读详情

      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通过应用程序框架层访问硬件服务。

 

 

一:在Ubuntu上为Android系统编写Linux内核驱动程序

  在智能手机时代,每个品牌的手机都有自己的个性特点。正是依靠这种与众不同的个性来吸引用户,营造品牌凝聚力和用户忠城度,典型的代表非iphone莫属了。据统计,截止2011年5月,AppStore的应用软件数量达381062个,位居第一,而Android Market的应用软件数量达294738,紧随AppStore后面,并有望在8月份越过AppStore。随着Android系统逐步扩大市场占有率,终端设备的多样性亟需更多的移动开发人员的参与。据业内统计,Android研发人才缺口至少30万。目前,对Android人才需求一类是偏向硬件驱动的Android人才需求,一类是偏向软件应用的Android人才需求。总的来说,对有志于从事Android硬件驱动的开发工程师来说,现在是一个大展拳脚的机会。那么,就让我们一起来看看如何为Android系统编写内核驱动程序吧。

这里,我们不会为真实的硬件设备编写内核驱动程序。为了方便描述为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目录:

USER-NAME@MACHINE-NAME:~/Android$ cdkernel/common/drivers

USER-NAME@MACHINE-NAME:~/Android/kernel/common/drivers$mkdir hello

三. 在hello目录中增加hello.h文件:

[cpp]viewplaincopyprint?

1. #ifndef _HELLO_ANDROID_H_

2. #define _HELLO_ANDROID_H_

3.  

4. #include <linux/cdev.h>

5. #include <linux/semaphore.h>

6.  

7. #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"

11. 

12.struct hello_android_dev {

13.int val;

14.struct semaphore sem;

15.struct cdev dev;

16.};

17. 

18.#endif

#ifndef _HELLO_ANDROID_H_

#define _HELLO_ANDROID_H_

 

#include <linux/cdev.h>

#include <linux/semaphore.h>

 

#define HELLO_DEVICE_NODE_NAME "hello"

#define HELLO_DEVICE_FILE_NAME "hello"

#define HELLO_DEVICE_PROC_NAME "hello"

#define HELLO_DEVICE_CLASS_NAME "hello"

 

struct hello_android_dev {

  int val;

  struct semaphore sem;

  struct cdev dev;

};

 

#endif

这个头文件定义了一些字符串常量宏,在后面我们要用到。此外,还定义了一个字符设备结构体hello_android_dev,这个就是我们虚拟的硬件设备了,val成员变量就代表设备里面的寄存器,它的类型为int,sem成员变量是一个信号量,是用同步访问寄存器val的,dev成员变量是一个内嵌的字符设备,这个Linux驱动程序自定义字符设备结构体的标准方法。

四.在hello目录中增加hello.c文件,这是驱动程序的实现部分。驱动程序的功能主要是向上层提供访问设备的寄存器的值,包括读和写。这里,提供了三种访问设备寄存器的方法,一是通过proc文件系统来访问,二是通过传统的设备文件的方法来访问,三是通过devfs文件系统来访问。下面分段描述该驱动程序的实现。

首先是包含必要的头文件和定义三种访问设备的方法:

[cpp]viewplaincopyprint?

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.staticint hello_major = 0;

13.staticint hello_minor = 0;

14. 

15./*设备类别和设备变量*/

16.staticstructclass* hello_class = NULL;

17.staticstruct hello_android_dev* hello_dev = NULL;

18. 

19./*传统的设备文件操作方法*/

20.staticint hello_open(struct inode* inode, struct file* filp);

21.staticint 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, constchar __user *buf, size_t count, loff_t* f_pos);

24. 

25./*设备文件操作方法表*/

26.staticstruct 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 ssize_t hello_val_show(struct device* dev, struct device_attribute* attr, char* buf);

36.static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, constchar* buf, size_t count);

37. 

38./*定义设备属性*/

39.static DEVICE_ATTR(val, S_IRUGO | S_IWUSR,hello_val_show, hello_val_store);

#include <linux/init.h>

#include <linux/module.h>

#include <linux/types.h>

#include <linux/fs.h>

#include <linux/proc_fs.h>

#include <linux/device.h>

#include <asm/uaccess.h>

 

#include "hello.h"

 

/*主设备和从设备号变量*/

static int hello_major = 0;

static int hello_minor = 0;

 

/*设备类别和设备变量*/

static struct class* hello_class = NULL;

static struct hello_android_dev* hello_dev = NULL;

 

/*传统的设备文件操作方法*/

static int hello_open(struct inode* inode, struct file* filp);

static int hello_release(struct inode* inode, struct file* filp);

static ssize_t hello_read(struct file* filp, char __user *buf, size_tcount, loff_t* f_pos);

static ssize_t hello_write(struct file* filp, const char __user *buf,size_t count, loff_t* f_pos);

 

/*设备文件操作方法表*/

static struct file_operations hello_fops = {

  .owner = THIS_MODULE,

  .open = hello_open,

  .release = hello_release,

  .read = hello_read,

  .write = hello_write,

};

 

/*访问设置属性方法*/

static ssize_t hello_val_show(struct device* dev, struct device_attribute*attr,  char* buf);

static ssize_t hello_val_store(struct device* dev, structdevice_attribute* attr, const char* buf, size_t count);

 

/*定义设备属性*/

static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, hello_val_show,hello_val_store);

定义传统的设备文件访问方法,主要是定义hello_open、hello_release、hello_read和hello_write这四个打开、释放、读和写设备文件的方法:

[cpp]viewplaincopyprint?

1. /*打开设备方法*/

2. staticint 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.staticint 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, constchar __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.}

/*打开设备方法*/

static int hello_open(struct inode* inode, struct file* filp) {

  struct hello_android_dev* dev;       

 

  /*将自定义设备结构体保存在文件指针的私有数据域中,以便访问设备时拿来用*/

  dev =container_of(inode->i_cdev, struct hello_android_dev, dev);

  filp->private_data = dev;

 

  return 0;

}

 

/*设备文件释放时调用,空实现*/

static int hello_release(struct inode* inode, struct file* filp) {

  return 0;

}

 

/*读取设备的寄存器val的值*/

static ssize_t hello_read(struct file* filp, char __user *buf, size_tcount, loff_t* f_pos) {

  ssize_t err = 0;

  struct hello_android_dev* dev =filp->private_data;       

 

  /*同步访问*/

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

   return -ERESTARTSYS;

  }

 

  if(count < sizeof(dev->val)){

   goto out;

  }       

 

  /*将寄存器val的值拷贝到用户提供的缓冲区*/

  if(copy_to_user(buf,&(dev->val), sizeof(dev->val))) {

   err = -EFAULT;

   goto out;

  }

 

  err = sizeof(dev->val);

 

out:

  up(&(dev->sem));

  return err;

}

 

/*写设备的寄存器值val*/

static ssize_t hello_write(struct file* filp, const char __user *buf,size_t count, loff_t* f_pos) {

  struct hello_android_dev* dev =filp->private_data;

  ssize_t err = 0;       

 

  /*同步访问*/

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

   return -ERESTARTSYS;       

  }       

 

  if(count != sizeof(dev->val)) {

   goto out;       

  }       

 

  /*将用户提供的缓冲区的值写到设备寄存器去*/

  if(copy_from_user(&(dev->val),buf, count)) {

   err = -EFAULT;

   goto out;

  }

 

  err = sizeof(dev->val);

 

out:

  up(&(dev->sem));

  return err;

}

定义通过devfs文件系统访问方法,这里把设备的寄存器val看成是设备的一个属性,通过读写这个属性来对设备进行访问,主要是实现hello_val_show和hello_val_store两个方法,同时定义了两个内部使用的访问val值的方法__hello_get_val和__hello_set_val:

[cpp]viewplaincopyprint?

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, constchar* 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 = (structhello_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, constchar* buf, size_t count) {

43.struct hello_android_dev* hdev = (structhello_android_dev*)dev_get_drvdata(dev);

44. 

45.return __hello_set_val(hdev, buf, count);

46.}

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

static ssize_t __hello_get_val(struct hello_android_dev* dev, char* buf) {

  int val = 0;       

 

  /*同步访问*/

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

   return -ERESTARTSYS;       

  }       

 

  val = dev->val;       

  up(&(dev->sem));       

 

  return snprintf(buf, PAGE_SIZE,"%d\n", val);

}

 

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

static ssize_t __hello_set_val(struct hello_android_dev* dev, const char*buf, size_t count) {

  int val = 0;       

 

  /*将字符串转换成数字*/       

  val = simple_strtol(buf, NULL,10);       

 

  /*同步访问*/       

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

   return -ERESTARTSYS;       

  }       

 

  dev->val = val;       

  up(&(dev->sem));

 

  return count;

}

 

/*读取设备属性val*/

static ssize_t hello_val_show(struct device* dev, struct device_attribute*attr, char* buf) {

  struct hello_android_dev* hdev =(struct hello_android_dev*)dev_get_drvdata(dev);       

 

  return __hello_get_val(hdev, buf);

}

 

/*写设备属性val*/

static ssize_t hello_val_store(struct device* dev, structdevice_attribute* attr, const char* buf, size_t count) {

  struct hello_android_dev* hdev =(struct hello_android_dev*)dev_get_drvdata(dev); 

 

  return __hello_set_val(hdev, buf,count);

}

定义通过proc文件系统访问方法,主要实现了hello_proc_read和hello_proc_write两个方法,同时定义了在proc文件系统创建和删除文件的方法hello_create_proc和hello_remove_proc:

[cpp]viewplaincopyprint?

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, constchar __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 fromuser.\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.staticvoid 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.staticvoid hello_remove_proc(void) {

55.remove_proc_entry(HELLO_DEVICE_PROC_NAME, NULL);

56.}

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

static ssize_t hello_proc_read(char* page, char** start, off_t off, intcount, int* eof, void* data) {

  if(off > 0) {

   *eof = 1;

   return 0;

  }

 

  return __hello_get_val(hello_dev,page);

}

 

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

static ssize_t hello_proc_write(struct file* filp, const char __user*buff, unsigned long len, void* data) {

  int err = 0;

  char* page = NULL;

 

  if(len > PAGE_SIZE) {

   printk(KERN_ALERT"The buff istoo large: %lu.\n", len);

   return -EFAULT;

  }

 

  page =(char*)__get_free_page(GFP_KERNEL);

  if(!page) {               

   printk(KERN_ALERT"Failed toalloc page.\n");

   return -ENOMEM;

  }       

 

  /*先把用户提供的缓冲区值拷贝到内核缓冲区中去*/

  if(copy_from_user(page, buff, len)){

   printk(KERN_ALERT"Failed tocopy buff from user.\n");               

   err = -EFAULT;

   goto out;

  }

 

  err = __hello_set_val(hello_dev,page, len);

 

out:

  free_page((unsigned long)page);

  return err;

}

 

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

static void hello_create_proc(void) {

  struct proc_dir_entry* entry;

 

  entry =create_proc_entry(HELLO_DEVICE_PROC_NAME, 0, NULL);

  if(entry) {

   entry->owner = THIS_MODULE;

   entry->read_proc =hello_proc_read;

   entry->write_proc =hello_proc_write;

  }

}

 

/*删除/proc/hello文件*/

static void hello_remove_proc(void) {

  remove_proc_entry(HELLO_DEVICE_PROC_NAME,NULL);

}

最后,定义模块加载和卸载方法,这里只要是执行设备注册和初始化操作:

[cpp]viewplaincopyprint?

1. /*初始化设备*/

2. staticint __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(structhello_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.staticint __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 devregion.\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 hellodevice.\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.  staticvoid __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);

/*初始化设备*/

static int  __hello_setup_dev(structhello_android_dev* dev) {

  int err;

  dev_t devno = MKDEV(hello_major,hello_minor);

 

  memset(dev, 0, sizeof(structhello_android_dev));

 

  cdev_init(&(dev->dev),&hello_fops);

  dev->dev.owner = THIS_MODULE;

  dev->dev.ops =&hello_fops;       

 

  /*注册字符设备*/

  err =cdev_add(&(dev->dev),devno, 1);

  if(err) {

   return err;

  }       

 

  /*初始化信号量和寄存器val的值*/

  init_MUTEX(&(dev->sem));

  dev->val = 0;

 

  return 0;

}

 

/*模块加载方法*/

static int __init hello_init(void){

  int err = -1;

  dev_t dev = 0;

  struct device* temp = NULL;

 

  printk(KERN_ALERT"Initializinghello device.\n");       

 

  /*动态分配主设备和从设备号*/

  err = alloc_chrdev_region(&dev,0, 1, HELLO_DEVICE_NODE_NAME);

  if(err < 0) {

   printk(KERN_ALERT"Failed toalloc char dev region.\n");

   goto fail;

  }

 

  hello_major = MAJOR(dev);

  hello_minor = MINOR(dev);       

 

  /*分配helo设备结构体变量*/

  hello_dev = kmalloc(sizeof(structhello_android_dev), GFP_KERNEL);

  if(!hello_dev) {

   err = -ENOMEM;

   printk(KERN_ALERT"Failed toalloc hello_dev.\n");

   goto unregister;

  }       

 

  /*初始化设备*/

  err = __hello_setup_dev(hello_dev);

  if(err) {

   printk(KERN_ALERT"Failed tosetup dev: %d.\n", err);

   goto cleanup;

  }       

 

  /*在/sys/class/目录下创建设备类别目录hello*/

  hello_class =class_create(THIS_MODULE, HELLO_DEVICE_CLASS_NAME);

  if(IS_ERR(hello_class)) {

   err = PTR_ERR(hello_class);

   printk(KERN_ALERT"Failed tocreate hello class.\n");

   goto destroy_cdev;

  }       

 

  /*在/dev/目录和/sys/class/hello目录下分别创建设备文件hello*/

  temp = device_create(hello_class,NULL, dev, "%s", HELLO_DEVICE_FILE_NAME);

  if(IS_ERR(temp)) {

   err = PTR_ERR(temp);

   printk(KERN_ALERT"Failed tocreate hello device.");

   goto destroy_class;

  }       

 

  /*在/sys/class/hello/hello目录下创建属性文件val*/

  err = device_create_file(temp,&dev_attr_val);

  if(err < 0) {

   printk(KERN_ALERT"Failed tocreate attribute val.");               

   goto destroy_device;

  }

 

  dev_set_drvdata(temp,hello_dev);       

 

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

  hello_create_proc();

 

  printk(KERN_ALERT"Succedded toinitialize hello device.\n");

  return 0;

 

destroy_device:

  device_destroy(hello_class, dev);

 

destroy_class:

  class_destroy(hello_class);

 

destroy_cdev:

  cdev_del(&(hello_dev->dev));

 

cleanup:

  kfree(hello_dev);

 

unregister:

  unregister_chrdev_region(MKDEV(hello_major,hello_minor), 1);

 

fail:

  return err;

}

 

/*模块卸载方法*/

static void __exit hello_exit(void) {

  dev_t devno = MKDEV(hello_major,hello_minor);

 

  printk(KERN_ALERT"Destroy hellodevice.\n");       

 

  /*删除/proc/hello文件*/

  hello_remove_proc();       

 

  /*销毁设备类别和设备*/

  if(hello_class) {

   device_destroy(hello_class,MKDEV(hello_major, hello_minor));

   class_destroy(hello_class);

  }       

 

  /*删除字符设备和释放设备内存*/

  if(hello_dev) {

   cdev_del(&(hello_dev->dev));

   kfree(hello_dev);

  }       

 

  /*释放设备号*/

  unregister_chrdev_region(devno, 1);

}

 

MODULE_LICENSE("GPL");

MODULE_DESCRIPTION("First Android Driver");

 

module_init(hello_init);

module_exit(hello_exit);

五.在hello目录中新增Kconfig和Makefile两个文件,其中Kconfig是在编译前执行配置命令make menuconfig时用到的,而Makefile是执行编译命令make是用到的:

Kconfig文件的内容

config HELLO

tristate"First Android Driver"

default n

help

This is thefirst 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/

八. 配置编译选项:

USER-NAME@MACHINE-NAME:~/Android/kernel/common$ makemenuconfig

找到"DeviceDrivers" => "First Android Drivers"选项,设置为y。

注意,如果内核不支持动态加载模块,这里不能选择m,虽然我们在Kconfig文件中配置了HELLO选项为tristate。要支持动态加载模块选项,必须要在配置菜单中选择Enable loadablemodule support选项;在支持动态卸载模块选项,必须要在Enable loadablemodule support菜单项中,选择Module unloading选项。

九. 编译:

USER-NAME@MACHINE-NAME:~/Android/kernel/common$ make

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

十. 参照在Ubuntu上下载、编译和安装Android最新内核源代码(Linux Kernel)一文所示,运行新编译的内核文件,验证hello驱动程序是否已经正常安装:

USER-NAME@MACHINE-NAME:~/Android$ emulator -kernel./kernel/common/arch/arm/boot/zImage &

USER-NAME@MACHINE-NAME:~/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驱动程序交互,敬请期待。

 

 

二:在Ubuntu上为Android系统内置C可执行程序测试Linux内核驱动程序

在前一篇文章中,我们介绍了如何在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目录:

USER-NAME@MACHINE-NAME:~/Android$ cdexternal

USER-NAME@MACHINE-NAME:~/Android/external$mkdir hello

三. 在hello目录中新建hello.c文件:

[cpp]viewplaincopyprint?

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.}

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#define DEVICE_NAME "/dev/hello"

int main(int argc, char** argv)

{

  int fd = -1;

  int val = 0;

  fd = open(DEVICE_NAME, O_RDWR);

  if(fd == -1) {

    printf("Failed to opendevice %s.\n", DEVICE_NAME);

    return -1;

  }

 

  printf("Read originalvalue:\n");

  read(fd, &val, sizeof(val));

  printf("%d.\n\n", val);

  val = 5;

  printf("Write value %d to%s.\n\n", val, DEVICE_NAME);

        write(fd, &val,sizeof(val));

 

  printf("Read the valueagain:\n");

        read(fd, &val,sizeof(val));

        printf("%d.\n\n",val);

  close(fd);

  return 0;

}

这个程序的作用中,打开/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 := $(callall-subdir-c-files)

include $(BUILD_EXECUTABLE)

注意,BUILD_EXECUTABLE表示我们要编译的是可执行程序。

五. 参照如何单独编译Android源代码中的模块一文,使用mmm命令进行编译:

USER-NAME@MACHINE-NAME:~/Android$ mmm./external/hello

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

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

USER-NAME@MACHINE-NAME:~/Android$ makesnod

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

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

USER-NAME@MACHINE-NAME:~/Android$emulator -kernel ./kernel/common/arch/arm/boot/zImage &

USER-NAME@MACHINE-NAME:~/Android$ adbshell

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的ApplicationFrameworks提供Java接口来访问Linux内核驱动程序呢?可以的,接下来的几篇文章中,我们将介绍如何在Android的ApplicationFrameworks中,增加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文件:

USER-NAME@MACHINE-NAME:~/Android$ cdhardware/libhardware/include/hardware

USER-NAME@MACHINE-NAME:~/Android/hardware/libhardware/include/hardware$vi hello.h

hello.h文件的内容如下:

[cpp]viewplaincopyprint?

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

#ifndef ANDROID_HELLO_INTERFACE_H

#define ANDROID_HELLO_INTERFACE_H

#include <hardware/hardware.h>

 

__BEGIN_DECLS

 

/*定义模块ID*/

#define HELLO_HARDWARE_MODULE_ID "hello"

 

/*硬件模块结构体*/

struct hello_module_t {

  struct hw_module_t common;

};

 

/*硬件接口结构体*/

struct hello_device_t {

  struct hw_device_t common;

  int fd;

  int (*set_val)(structhello_device_t* dev, int val);

  int (*get_val)(structhello_device_t* dev, int* val);

};

 

__END_DECLS

 

#endif

 

这里按照Android硬件抽象层规范的要求,分别定义模块ID、模块结构体以及硬件接口结构体。在硬件接口结构体中,fd表示设备文件描述符,对应我们将要处理的设备文件"/dev/hello",set_val和get_val为该HAL对上提供的函数接口。

三. 进入到hardware/libhardware/modules目录,新建hello目录,并添加hello.c文件。 hello.c的内容较多,我们分段来看。

首先是包含相关头文件和定义相关结构:

[cpp]viewplaincopyprint?

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.staticint hello_device_open(conststruct hw_module_t* module, constchar* name, struct hw_device_t** device);

16.staticint hello_device_close(struct hw_device_t* device);

17. 

18./*设备访问接口*/

19.staticint hello_set_val(struct hello_device_t* dev, int val);

20.staticint hello_get_val(struct hello_device_t* dev, int* val);

21. 

22./*模块方法表*/

23.staticstruct 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.};

#define LOG_TAG "HelloStub"

 

#include <hardware/hardware.h>

#include <hardware/hello.h>

#include <fcntl.h>

#include <errno.h>

#include <cutils/log.h>

#include <cutils/atomic.h>

 

#define DEVICE_NAME "/dev/hello"

#define MODULE_NAME "Hello"

#define MODULE_AUTHOR "shyluo@gmail.com"

 

/*设备打开和关闭接口*/

static int hello_device_open(const struct hw_module_t* module, const char*name, struct hw_device_t** device);

static int hello_device_close(struct hw_device_t* device);

 

/*设备访问接口*/

static int hello_set_val(struct hello_device_t* dev, int val);

static int hello_get_val(struct hello_device_t* dev, int* val);

 

/*模块方法表*/

static struct hw_module_methods_t hello_module_methods = {

  open: hello_device_open

};

 

/*模块实例变量*/

struct hello_module_t HAL_MODULE_INFO_SYM = {

  common: {

   tag: HARDWARE_MODULE_TAG,

   version_major: 1,

   version_minor: 0,

   id: HELLO_HARDWARE_MODULE_ID,

   name: MODULE_NAME,

   author: MODULE_AUTHOR,

   methods: &hello_module_methods,

  }

};

 

这里,实例变量名必须为HAL_MODULE_INFO_SYM,tag也必须为HARDWARE_MODULE_TAG,这是Android硬件抽象层规范规定的。

定义hello_device_open函数:

[cpp]viewplaincopyprint?

1. staticint hello_device_open(conststruct hw_module_t* module, constchar* name, struct hw_device_t** device) {

2. struct hello_device_t* dev;dev = (struct hello_device_t*)malloc(sizeof(struct hello_device_t));

3.  

4. if(!dev) {

5. LOGE("Hello Stub: failed to alloc space");

6. return -EFAULT;

7. }

8.  

9. memset(dev, 0, sizeof(structhello_device_t));

10.dev->common.tag = HARDWARE_DEVICE_TAG;

11.dev->common.version = 0;

12.dev->common.module = (hw_module_t*)module;

13.dev->common.close = hello_device_close;

14.dev->set_val = hello_set_val;dev->get_val = hello_get_val;

15. 

16.if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {

17.LOGE("Hello Stub: failed to open /dev/hello -- %s.", strerror(errno));free(dev);

18.return -EFAULT;

19.}

20. 

21.*device = &(dev->common);

22.LOGI("Hello Stub: open /dev/hello successfully.");

23. 

24.return 0;

25.}

static int hello_device_open(const struct hw_module_t* module, const char*name, struct hw_device_t** device) {

  struct hello_device_t* dev;dev =(struct hello_device_t*)malloc(sizeof(struct hello_device_t));

 

  if(!dev) {

   LOGE("Hello Stub: failed toalloc space");

   return -EFAULT;

  }

 

  memset(dev, 0, sizeof(structhello_device_t));

  dev->common.tag =HARDWARE_DEVICE_TAG;

  dev->common.version = 0;

  dev->common.module =(hw_module_t*)module;

  dev->common.close =hello_device_close;

  dev->set_val =hello_set_val;dev->get_val = hello_get_val;

 

  if((dev->fd = open(DEVICE_NAME,O_RDWR)) == -1) {

   LOGE("Hello Stub: failed to open/dev/hello -- %s.", strerror(errno));free(dev);

   return -EFAULT;

  }

 

  *device = &(dev->common);

  LOGI("Hello Stub: open/dev/hello successfully.");

 

  return 0;

}

 

DEVICE_NAME定义为"/dev/hello"。由于设备文件是在内核驱动里面通过device_create创建的,而device_create创建的设备文件默认只有root用户可读写,而hello_device_open一般是由上层APP来调用的,这些APP一般不具有root权限,这时候就导致打开设备文件失败:

Hello Stub: failed to open /dev/hello -- Permissiondenied.

解决办法是类似于Linux的udev规则,打开Android源代码工程目录下,进入到system/core/rootdir目录,里面有一个名为ueventd.rc文件,往里面添加一行:

/dev/hello 0666 root root

定义hello_device_close、hello_set_val和hello_get_val这三个函数:

[cpp]viewplaincopyprint?

1. staticint 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.staticint 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.staticint 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.}

static int hello_device_close(struct hw_device_t* device) {

  struct hello_device_t* hello_device= (struct hello_device_t*)device;

 

  if(hello_device) {

   close(hello_device->fd);

   free(hello_device);

  }

 

  return 0;

}

 

static int hello_set_val(struct hello_device_t* dev, int val) {

  LOGI("Hello Stub: set value %dto device.", val);

 

  write(dev->fd, &val,sizeof(val));

 

  return 0;

}

 

static int hello_get_val(struct hello_device_t* dev, int* val) {

  if(!val) {

   LOGE("Hello Stub: error valpointer");

   return -EFAULT;

  }

 

  read(dev->fd, val,sizeof(*val));

 

  LOGI("Hello Stub: get value %dfrom device", *val);

 

  return 0;

}

 

四. 继续在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$ mmmhardware/libhardware/modules/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的ApplicationFrameworks层增加API接口,才能让上层Application访问我们的硬件。在接下来的文章中,我们还将完成这一系统过程,使得我们能够在Java应用程序中访问我们自己定制的硬件。

 

 

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

    在上两篇文章中,我们介绍了如何为Android系统的硬件编写驱动程序,包括如何在Linux内核空间实现内核驱动程序和在用户空间实现硬件抽象层接口。实现这两者的目的是为了向更上一层提供硬件访问接口,即为Android的Application Frameworks层提供硬件服务。我们知道,Android系统的应用程序是用Java语言编写的,而硬件驱动程序是用C语言来实现的,那么,Java接口如何去访问C接口呢?众所周知,Java提供了JNI方法调用,同样,在Android系统中,Java应用程序通过JNI来调用硬件抽象层接口。在这一篇文章中,我们将介绍如何为Android硬件抽象层接口编写JNI方法,以便使得上层的Java应用程序能够使用下层提供的硬件服务。

一. 参照在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程序一文,准备好硬件抽象层模块,确保Android系统镜像文件system.img已经包含hello.default模块。

二. 进入到frameworks/base/services/jni目录,新建com_android_server_HelloService.cpp文件:

USER-NAME@MACHINE-NAME:~/Android$ cdframeworks/base/services/jni

USER-NAME@MACHINE-NAME:~/Android/frameworks/base/services/jni$vi com_android_server_HelloService.cpp

在com_android_server_HelloService.cpp文件中,实现JNI方法。注意文件的命令方法,com_android_server前缀表示的是包名,表示硬件服务HelloService是放在frameworks/base/services/java目录下的com/android/server目录的,即存在一个命令为com.android.server.HelloService的类。这里,我们暂时略去HelloService类的描述,在下一篇文章中,我们将回到HelloService类来。简单地说,HelloService是一个提供Java接口的硬件访问服务类。

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

[cpp]viewplaincopyprint?

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>

#define LOG_TAG "HelloService"

#include "jni.h"

#include "JNIHelp.h"

#include "android_runtime/AndroidRuntime.h"

#include <utils/misc.h>

#include <utils/Log.h>

#include <hardware/hardware.h>

#include <hardware/hello.h>

#include <stdio.h>

接着定义hello_init、hello_getVal和hello_setVal三个JNI方法:

[cpp]viewplaincopyprint?

1. namespace android

2. {

3. /*在硬件抽象层中定义的硬件访问结构体,参考<hardware/hello.h>*/

4. struct hello_device_t* hello_device = NULL;

5. /*通过硬件抽象层定义的硬件访问接口设置硬件寄存器val的值*/

6. staticvoid 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, jobjectclazz) {

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.staticinlineint 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, jclassclazz) {

35.hello_module_t* module;

36. 

37.LOGI("Hello JNI: initializing......");

38.if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (conststruct 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.staticconst 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.intregister_android_server_HelloService(JNIEnv *env) {

58.return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table));

59.}

60.};

namespace android

{

  /*在硬件抽象层中定义的硬件访问结构体,参考<hardware/hello.h>*/

        struct hello_device_t*hello_device = NULL;

  /*通过硬件抽象层定义的硬件访问接口设置硬件寄存器val的值*/

        static voidhello_setVal(JNIEnv* env, jobject clazz, jint value) {

    int val = value;

    LOGI("Hello JNI: set value%d to device.", val);

    if(!hello_device) {

     LOGI("Hello JNI: device isnot open.");

     return;

    }

   

    hello_device->set_val(hello_device,val);

  }

        /*通过硬件抽象层定义的硬件访问接口读取硬件寄存器val的值*/

  static jint hello_getVal(JNIEnv*env, jobject clazz) {

    int val = 0;

    if(!hello_device) {

     LOGI("Hello JNI: device isnot open.");

     return val;

    }

    hello_device->get_val(hello_device,&val);

   

    LOGI("Hello JNI: get value%d from device.", val);

 

    return val;

  }

        /*通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/

  static inline inthello_device_open(const hw_module_t* module, struct hello_device_t** device) {

    returnmodule->methods->open(module, HELLO_HARDWARE_MODULE_ID, (structhw_device_t**)device);

  }

        /*通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件*/

  static jboolean hello_init(JNIEnv*env, jclass clazz) {

    hello_module_t* module;

   

    LOGI("Hello JNI:initializing......");

    if(hw_get_module(HELLO_HARDWARE_MODULE_ID,(const struct hw_module_t**)&module) == 0) {

     LOGI("Hello JNI: hello Stubfound.");

     if(hello_device_open(&(module->common),&hello_device) == 0) {

       LOGI("Hello JNI: hellodevice is open.");

       return 0;

     }

     LOGE("Hello JNI: failed toopen hello device.");

     return -1;

    }

    LOGE("Hello JNI: failed toget hello stub module.");

    return -1;   

  }

        /*JNI方法表*/

  static const JNINativeMethodmethod_table[] = {

    {"init_native","()Z", (void*)hello_init},

    {"setVal_native","(I)V", (void*)hello_setVal},

    {"getVal_native","()I", (void*)hello_getVal},

  };

        /*注册JNI方法*/

  intregister_android_server_HelloService(JNIEnv *env) {

        returnjniRegisterNativeMethods(env, "com/android/server/HelloService",method_table, NELEM(method_table));

  }

};

注意,在hello_init函数中,通过Android硬件抽象层提供的hw_get_module方法来加载模块ID为HELLO_HARDWARE_MODULE_ID的硬件抽象层模块,其中,HELLO_HARDWARE_MODULE_ID是在<hardware/hello.h>中定义的。Android硬件抽象层会根据HELLO_HARDWARE_MODULE_ID的值在Android系统的/system/lib/hw目录中找到相应的模块,然后加载起来,并且返回hw_module_t接口给调用者使用。在jniRegisterNativeMethods函数中,第二个参数的值必须对应HelloService所在的包的路径,即com.android.server.HelloService。

三. 修改同目录下的onload.cpp文件,首先在namespace android增加register_android_server_HelloService函数声明:

namespaceandroid {

..............................................................................................

intregister_android_server_HelloService(JNIEnv *env);

};

在JNI_onLoad增加register_android_server_HelloService函数调用:

extern "C" jintJNI_onLoad(JavaVM* vm, void* reserved)

{

.................................................................................................

register_android_server_HelloService(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类的实现,在下一篇文章中,我们将描述如何实现硬件服务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接口定义文件:

USER-NAME@MACHINE-NAME:~/Android$ cdframeworks/base/core/java/android/os

USER-NAME@MACHINE-NAME:~/Android/frameworks/base/core/java/android/os$vi IHelloService.aidl

IHelloService.aidl定义了IHelloService接口:

[java]viewplaincopyprint?

1. package android.os;

2.  

3. interface IHelloService {

4. void setVal(int val);

5. int getVal();

6. }

package android.os;

 

interface IHelloService {

    void setVal(int val);

    int getVal();

}

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接口:

USER-NAME@MACHINE-NAME:~/Android$ mmm frameworks/base

这样,就会根据IHelloService.aidl生成相应的IHelloService.Stub接口。

五.进入到frameworks/base/services/java/com/android/server目录,新增HelloService.java文件:

[java]viewplaincopyprint?

1. package com.android.server;

2. import android.content.Context;

3. import android.os.IHelloService;

4. import android.util.Slog;

5. publicclass HelloService extends IHelloService.Stub {

6. privatestaticfinal String TAG = "HelloService";

7. HelloService() {

8. init_native();

9. }

10.publicvoid setVal(int val) {

11.setVal_native(val);

12.}

13.publicint getVal() {

14.return getVal_native();

15.}

16. 

17.privatestaticnativeboolean init_native();

18.privatestaticnativevoid setVal_native(int val);

19.privatestaticnativeint getVal_native();

20.};

package com.android.server;

import android.content.Context;

import android.os.IHelloService;

import android.util.Slog;

public class HelloService extends IHelloService.Stub {

private static final String TAG ="HelloService";

HelloService() {

  init_native();

}

public void setVal(int val) {

  setVal_native(val);

}

public int getVal() {

  return getVal_native();

}

private static native booleaninit_native();

    privatestatic native void setVal_native(int val);

private static native intgetVal_native();

};

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 HelloService", e);

}

......................................................................................

}

七. 编译HelloService和重新打包system.img:

USER-NAME@MACHINE-NAME:~/Android$ mmmframeworks/base/services/java

USER-NAME@MACHINE-NAME:~/Android$ makesnod

这样,重新打包后的system.img系统镜像文件就在ApplicationFrameworks层中包含了我们自定义的硬件服务HelloService了,并且会在系统启动的时候,自动加载HelloService。这时,应用程序就可以通过Java接口来访问Hello硬件服务了。我们将在下一篇文章中描述如何编写一个Java应用程序来调用这个HelloService接口来访问硬件,敬请期待。

 

六:在Ubuntu上为Android系统内置Java应用程序测试ApplicationFrameworks层的硬件服务

我们在Android系统增加硬件服务的目的是为了让应用层的APP能够通过Java接口来访问硬件服务。那么, APP如何通过Java接口来访问ApplicationFrameworks层提供的硬件服务呢?在这一篇文章中,我们将在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:

[java]viewplaincopyprint?

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.publicclass Hello extends Activity implements OnClickListener {

16.privatefinalstatic 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 firstcreated. */

26.@Override

27.publicvoid 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.publicvoid 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 fromdevice.");

55.}

56.}

57.elseif(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 todevice.");

64.}

65.}

66.elseif(v.equals(clearButton)) {

67.String text = "";

68.valueText.setText(text);

69.}

70.}

71.}

package shy.luo.hello;

 

import shy.luo.hello.R;

import android.app.Activity;

import android.os.ServiceManager;

import android.os.Bundle;

import android.os.IHelloService;

import android.os.RemoteException;

import android.util.Log;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.EditText;

 

public class Hello extends Activity implements OnClickListener {

  private final static String LOG_TAG= "shy.luo.renju.Hello";

 

  private IHelloService helloService= null;

 

  private EditText valueText = null;

  private Button readButton = null;

  private Button writeButton = null;

  private Button clearButton = null;

 

    /** Called when the activity isfirst created. */

    @Override

    public void onCreate(BundlesavedInstanceState) {

       super.onCreate(savedInstanceState);

       setContentView(R.layout.main);

 

  helloService = IHelloService.Stub.asInterface(

    ServiceManager.getService("hello"));

       

        valueText =(EditText)findViewById(R.id.edit_value);

        readButton =(Button)findViewById(R.id.button_read);

        writeButton =(Button)findViewById(R.id.button_write);

        clearButton =(Button)findViewById(R.id.button_clear);

 

  readButton.setOnClickListener(this);

  writeButton.setOnClickListener(this);

  clearButton.setOnClickListener(this);

       

        Log.i(LOG_TAG, "Hello ActivityCreated");

    }

   

    @Override

    public void onClick(View v) {

      if(v.equals(readButton)){

    try {

         intval = helloService.getVal();

         Stringtext = String.valueOf(val);

         valueText.setText(text);

    } catch (RemoteException e) {

     Log.e(LOG_TAG, "RemoteException while reading value from device.");

    }   

      }

      elseif(v.equals(writeButton)) {

    try {

         Stringtext = valueText.getText().toString();

         intval = Integer.parseInt(text);

     helloService.setVal(val);

    } catch (RemoteException e) {

     Log.e(LOG_TAG, "RemoteException while writing value to device.");

    }

      }

      elseif(v.equals(clearButton)) {

        Stringtext = "";

        valueText.setText(text);

      }

    }

}

程序通过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:

[html]viewplaincopyprint?

1. <?xmlversion="1.0"encoding="utf-8"?>

2. <LinearLayoutxmlns: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>

<?xml version="1.0" encoding="utf-8"?>

    <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

      android:orientation="vertical"

      android:layout_width="fill_parent"

      android:layout_height="fill_parent">

       <LinearLayout

         android:layout_width="fill_parent"

         android:layout_height="wrap_content"

         android:orientation="vertical"

         android:gravity="center">

          <TextView

            android:layout_width="wrap_content"

             android:layout_height="wrap_content"

            android:text="@string/value">

          </TextView>

          <EditText

            android:layout_width="fill_parent"

            android:layout_height="wrap_content"

            android:id="@+id/edit_value"

            android:hint="@string/hint">

          </EditText>

       </LinearLayout>

       <LinearLayout

         android:layout_width="fill_parent"

         android:layout_height="wrap_content"

          android:orientation="horizontal"

         android:gravity="center">

          <Button

            android:id="@+id/button_read"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

             android:text="@string/read">

          </Button>

          <Button

            android:id="@+id/button_write"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="@string/write">

          </Button>

          <Button

            android:id="@+id/button_clear"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="@string/clear">

          </Button>

       </LinearLayout>

    </LinearLayout>

字符串文件res/values/strings.xml:

[html]viewplaincopyprint?

1. <?xmlversion="1.0"encoding="utf-8"?>

2. <resources>

3. <stringname="app_name">Hello</string>

4. <stringname="value">Value</string>

5. <stringname="hint">Please input avalue...</string>

6. <stringname="read">Read</string>

7. <stringname="write">Write</string>

8. <stringname="clear">Clear</string>

9. </resources>

<?xml version="1.0" encoding="utf-8"?>

    <resources>

       <stringname="app_name">Hello</string>

       <stringname="value">Value</string>

       <stringname="hint">Please input a value...</string>

       <stringname="read">Read</string>

       <stringname="write">Write</string>

       <stringname="clear">Clear</string>

    </resources>

程序描述文件AndroidManifest.xml:

[html]viewplaincopyprint?

1. <?xmlversion="1.0"encoding="utf-8"?>

2. <manifestxmlns:android="http://schemas.android.com/apk/res/android"

3. package="shy.luo.hello"

4. android:versionCode="1"

5. android:versionName="1.0">

6. <applicationandroid:icon="@drawable/icon"android:label="@string/app_name">

7. <activityandroid:name=".Hello"

8. android:label="@string/app_name">

9. <intent-filter>

10.<actionandroid:name="android.intent.action.MAIN"/>

11.<categoryandroid:name="android.intent.category.LAUNCHER"/>

12.</intent-filter>

13.</activity>

14.</application>

15.</manifest>

<?xml version="1.0" encoding="utf-8"?>

    <manifestxmlns:android="http://schemas.android.com/apk/res/android"

     package="shy.luo.hello"

     android:versionCode="1"

     android:versionName="1.0">

      <applicationandroid:icon="@drawable/icon"android:label="@string/app_name">

        <activityandroid:name=".Hello"

                 android:label="@string/app_name">

            <intent-filter>

                <actionandroid:name="android.intent.action.MAIN" />

                <categoryandroid:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

      </application>

    </manifest>

三. 将Hello目录拷贝至packages/experimental目录,新增Android.mk文件:
USER-NAME@MACHINE-NAME:~/Android/packages/experimental$vi Android.mk

Android.mk的文件内容如下:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := $(callall-subdir-java-files)

LOCAL_PACKAGE_NAME := Hello

include $(BUILD_PACKAGE)

四. 编译:

USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Hello

编译成功后,便可以在out/target/product/generic/system/app目录下看到Hello.apk文件了。
五. 重新打包系统镜像文件system.img:

USER-NAME@MACHINE-NAME:~/Android$ makesnod
重新打包后的system.img文件就内置了Hello.apk文件了。

六. 运行Android模拟器:

USER-NAME@MACHINE-NAME:~/Android$ emulator -kernelkernel/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)概要介绍和学习计划。

 

Android 的定位分层架构 Android GPS 阅读详情

相关推荐

构建个人知识管理系统:从信息焦虑到高效输出的闭环设计

在信息爆炸的时代,知识管理已成为提升个人效率与核心竞争力的关键技术。其核心原理在于构建一个可持续的‘输入-处理-输出’闭环系统,通过系统化的方法将碎片化信息转化为可复用、可连接的知识资产。这一过程的技术价值在于对抗信息过载,实现知识的沉淀、内化与创造性应用,最终服务于决策、创作与问题解决等实际场景。本文以‘收集-处理-归档-应用’四步法为框架,深入探讨了如何利用标签化与MOC(地图笔记)等方法,打造一个轻量、跨平台且数据自主的个人知识循环系统。该系统尤其强调‘知识卡片’的创建与‘定期回顾’的习惯,旨在帮助知

weixin_30824599的博客 424

第一篇 Android 驱动开发之简单概述

1.Android设备设计之五层结构: 第一层:硬件; 第二层:linux内核层;或者说是:驱动层 主要包含:外设驱动;内存管理;进程管理;电源管理;其实这些都是linux kernel 具有的功能。 第三层:C、C++代码库;其实就是:HAL层; 主要包含linux的动态链接库*.so;以及嵌入到APK的NDK代码。 第四层:SDK层;或者说是:framework层 主要是直接面向

htob_workstation的博客 1万+

android底层HAL层深入了解

需要了解android底层开发,尤其是针对厂商接口对接,我们就需要针对android的HAL层做深入理解,在android的六层架构中,其中HAL层架构在linux驱动层之上,下面我们就来以相机为列做初步了解(官方输出),福利:基础应用开源框架 Android 的相机硬件抽象层 (HAL) 可将 Camera 2 中较高级别的相机框架 API 连接到底层的相机驱动程序和硬件。相机子系统包括相机管...

android应用开发详解 1万+

Android底层驱动开发(七)

设备树

Lzdnydppx的博客 703

T-Box功能详解及应用

T - BOX,其全称为 Telematics Box,作为远程 / 车载通信模块,是一款集车身网络与无线通讯功能于一体的智能终端设备。它充当了多媒体车机与 TSP(汽车远程服务提供商)以及互联网之间的无线路由器角色,为用户创造了远程访问的可能。用户借助T - BOX能够对车辆配备的各个 ECU(电子控制单元)进行远程操控,包括远程启动或停止发动机开启或关闭空调天窗后备箱门车门,以及实现闪灯鸣笛等功能。

wgl110110的博客 5640

Python广度优先搜索BFS - 蓝桥13届省赛污染海域

请问这片N * M海域中有几块是没被污染的独立海域(没被污染的独立海域是指该块海域上下左右被已污染的海域包围,且N * M以外的海域都为已被污染的海域)有一片海域划分为N * M个方格,其中有些海域已被污染(用0表示),有些海域没被污染(用1表示)。第二行开始输入N行,每行M个数字(数字只能为1或者0,1表示没被污染的海域,0表示已被污染的海域)这块4 * 5的海域,有3块海域(绿色)没被污染,因为每一块的上下左右都被污染的海域包围。输出一个整数,表示N * M的海域中有几块是没被污染的独立海域。

m0_69865359的博客 264

Android系统架构

文章以前写的文章中的引用来源当时没记,如有侵权或注明出处请联系。 Android官方6层架构 应用层 应用框架层 java代码层 Framework API+JNI方法 服务端、客户端、Linux驱动。 在这层里定义的 API 都是用 Java 语言编写。但是又因为它包含了 JNI 的方法,JNI (Java Native Interface)用 C/C++ 编写接口,根据函数表查询调用核心库层里的底层方法,最终访问到 Linux 内核。 系统运行库层 本地代码层C和C

0 572

用Cadence IC618仿真双平衡吉尔伯特混频器:从原理图到后仿的完整避坑指南

本文详细介绍了在Cadence IC618环境中设计双平衡吉尔伯特混频器的全流程,包括原理图设计、仿真优化和版图实现。通过实战案例分享,重点解析了设计中的关键技巧和常见问题解决方案,帮助工程师高效完成高性能混频器设计,特别适合射频前端开发人员参考。

weixin_42656416的博客 161

Sqribble文档自动化流水线:模板驱动的PDF生成系统

文档自动化是现代内容生产的关键基础设施,其核心在于将重复性排版任务转化为可复用、可验证、可协作的确定性流程。基于模板驱动与规则约束的文档流水线技术,通过结构化模板定义视觉语法与内容容器,结合语义归一化、空间分配规则和行为触发机制,显著压缩从内容到交付PDF的周期。该技术广泛应用于营销电子书、客户手册、知识付费资料等需高频、合规、多端适配的场景,尤其适合内容生产者与中小团队提升交付稳定性与品牌一致性。Sqribble正是这一范式的典型实践。

355

密码学小白必看:三大顶级会议论文下载与阅读指南(附完整资源链接)

本文为密码学初学者提供了系统获取和阅读三大顶级会议(Crypto、Eurocrypt、Asiacrypt)论文的实战指南。详细介绍了通过IACR官方渠道、ePrint预印本等权威资源免费下载论文的方法,并分享了高效筛选、精读核心章节及构建个人知识体系的具体策略,帮助读者快速切入密码学研究前沿。

weixin_28363123的博客 389

Stata面板数据OLS回归分析实战指南

OLS回归作为计量经济学的核心分析方法,通过最小化残差平方和实现参数估计,广泛应用于经济学实证研究。其技术价值在于模型简洁、结果易于解释,特别适合处理面板数据中的复杂关系。在实际应用中,配合robust标准误能有效解决异方差问题,而固定效应模型则可控制个体特异性。本文以Stata软件为例,详细演示从数据导入、描述性统计到混合OLS和固定效应模型构建的全流程,重点解析面板数据特有的格式设定、模型选择标准以及结果呈现技巧,为经济学研究者提供可直接复用的分析框架。

weixin_29051811的博客 268

STM32C5与CubeMX2实战评测:定位、迁移与避坑指南

在嵌入式开发中,MCU选型与工具链效率直接影响项目成本与安全合规。以Arm Cortex-M33内核和TrustZone安全技术为基础,STM32C5系列通过硬件加密加速与安全启动能力,将原本高端的安全特性下放至中低端市场。配合CubeMX2的代码生成优化,可显著降低Flash占用并提升配置效率。该组合适用于智能门锁、物联网终端等对安全有硬性要求且预算敏感的场景。本文基于实测,解析STM32C5的定位逻辑与CubeMX2的迁移实践,帮助开发者规避常见陷阱。

weixin_33842304的博客 410

RK3576 USB3.0 DWC3控制器调试:PHY初始化失败排查指南

DWC3 是 Synopsys 公司推出的 USB3.0/3.1 控制器 IP,支持双角色(DRD)模式,可配置为 Host、Device 或 OTG。

小灰灰的博客 1174

android 的四个层次开发

android是一个开放的系统,这个系统体积非常庞大,不同的开发者在开发过程中并不需要掌握整个android   系统,只要进行其中某一个部分的开发   android 的开发分四个层次   1. Linux操作系统及驱动,该层由C语言实现   2. 本地代码框架,该层次由C,C++实现   3. java框架,该层主要由java实现   4. java应用程序,该层主要由java实现

道心惟纯 2588

芒果YOLO11改进23:主干Backbone篇GhostNet:原创改进高效 GhostNet 网络,华为出品,打造全新检测器

主干Backbone篇GhostNet:原创改进高效 GhostNet 网络,华为出品,打造全新检测器

包括YOLOv5、YOLOv7、YOLOv8等模型改进 404

基于YOLOv11与相机标定的齿轮尺寸测量系统

本文详细介绍了一种基于深度学习的目标检测算法YOLOv11与相机标定技术相结合的齿轮尺寸自动测量系统。该系统能够实时检测图像中的齿轮目标,并精确测量其关键尺寸参数,包括齿顶圆直径、齿根圆直径、模数等。我们构建了完整的深度学习解决方案,包含数据采集与标注、模型训练、相机标定、尺寸测量算法以及用户友好的UI界面。本文提供了完整的代码实现,并推荐了相关的参考数据集,为工业视觉检测领域提供了实用的技术方案。关键词:YOLOv11;齿轮检测;尺寸测量;相机标定;深度学习;工业视觉python。

YOLO 411

Android HAL与Android Framwork层的开发与优化概述

HAL层是Android系统中至关重要的一部分,它使得Framework层能够以一种抽象的方式与硬件设备进行交互。HAL层的开发涉及到硬件特性的抽象和标准化,以便Android系统能够与各种硬件设备兼容。HAL模块的架构通常包括以下几个关键部分:下面是一个简化的示例,展示了如何为一个假设的LED硬件设备实现一个HAL模块。1. 定义硬件接口 (): 2. 实现HAL模块 (): 3. 编译HAL库:在Android系统的构建系统中,HAL模块会被编译成库文件。在设备的BoardConfig.mk文件中,可以

jakelihua 3593

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Android开发基础知识

Android开发基础知识

Chai_Zihao的博客 3306

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Android系统四层体系结构详解

开门见山,先上图 很明显四层:接下来一一讲解: Java应用程序层: Android会同一系列核心应用程序包一起发布,该应用程序包包括客户端,SMS短消息程序,日历,地图,浏览器,联系人管理程序等。所有的应用程序都是使用JAVA语言编写的。 什么?这你已经知道了。那么你知道这些应用程序都是调用应用程序框架层的接口写的吗。那又怎样?显然你自己也可以使用Java通过Java原生接口JNI(J

雨日微风 3万+

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Android 架构层的理解

1. 了解Java虚拟机 Java究竟为什么需要JVM(Java虚拟机)?       图1 JVM虚拟机结构 上面这篇文章说的很好,总结来说就是为了跨平台(操作系统):Windows,Linux,Mac OS. 为什么不在执行的时候,把Java编译成每个系统适配的可执行代码呢?还需要费事情安装虚拟机? 目的就是,虽然虚拟机安装的时候费时间,但是可以保证...

weixin_30786657的博客 510

Android开发的技术层次

android 技术层次 装B 扯蛋 菜鸟 大哥 大侠 大牛 大师 博客 抽象 论文

理论之树 2万+

Android系统五层架构

2022-8-10 初学安卓

TinyHorse的博客 2万+

Android开发如何定制framework层服务

刚刚跨完年,新年第一篇文章,那么今天将对Android开发framework中间层的服务定制使用作个总结。首先我们先导入Android平台源码framework层的代码到开发工具eclipse中,代码工程目录如下: 当然了除了用eclipse之外也可以用其它软件进行开发使用,比如用Source Insight 3方式,效果图如下: 那么喜欢哪种就用哪种吧。我这边这部分代码是经过ATC公司

啊sir的专栏 2万+

Android底层开发

学习书籍:Android底层开发实践 使用代码:AOSP源码 带@的行表示关键点; 橙色背景的文字表示:整编log分析的关键字 绿色“待补充”文字表示:有待补充内容 -----第三章 Android系统开发环境搭建 3.1 Android上的开发工作 3.1.1 Android的移植开发 1. Linux驱动 @p47 @工作在系统内核空间 2. Android系统硬件抽象层 @p47 @工作在用户控件 ...

DreamWendy 7575

【Android开发】一文全面解析Framework层

上一篇文章从Native角度讲解了Android进程管理的相关概念,本文将继续从上层的Framework中的进程启动、销毁场景和优先级处理、以及它们与四大组件的种种关联,来逐步解析Android进程管理的其他关键要素。Android Framework层是运行在ART虚拟机之上的,对于一般的Java进程是不具备主动创建进程的能力的。我们都知道Android中存在四大组件:Activity、Service、BroadcastReceiver和ContentProvider,那么为什么这样划分呢,一个主要的原因

bugyinyin的博客 5804
上一篇: Linux-ASoC驱动归纳总结:
倚天神剑
博客等级 码龄17年 23粉丝 5原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值