Android HAL 层框架分析以及代码示例

本文拜读参考自罗升杨老师的 《Android系统源代码情景分析》

本文代码实验平台为 Android7.1

一 硬件抽象层概述
二 开发Android硬件驱动程序
三 开发Android硬件抽象层模块
  • 3.1 硬件抽象层模块文件 命名规范
  • 3.2 硬件抽象层模块结构体 以及 硬件抽象层设备结构体 定义规范
  • 3.3 编写硬件抽象层模块接口
为Android硬件抽象层编写JNI方法供硬件服务程序调用
  • 4.1 JNI实现
  • 4.2 声明JNI注册方法
  • 4.3 添加JNI方法代码
五 开发Android硬件访问服务
  • 5.1 定义硬件访问服务接口
  • 5.2 实现硬件访问服务 最后 启动硬件访问服务
六 开发Android应用程序来使用硬件访问服务

一 硬件抽象层概述

Android系统的硬件抽象层(Hardware Abstract Layer, HAL)运行在用户空间中,它向下屏蔽硬件驱动模块的实现细节,向上提供硬件访问服务。通过硬件抽象层,Android系统分为两层来支持硬件设备,其中一层实现在用户空间(User Space),另外一层是现在内核空间(Kernel Space)。传统的Linux系统把对硬件的支持完全是现在在内核空间,即把对硬件的支持完全实现在硬件驱动模块中。

问题:Android系统为什么要把对硬件的支持划分为两层来实现呢?把硬件抽象层和内核驱动整合在一起放在内核空间不可行吗?从技术实现的角度来看,是可以的,然而从商业的角度来看,把对硬件的支持逻辑都放在内核空间,可能会损害厂家的利益。我们知道Linux内核源代码是遵循GPL协议的,如果我们在Android系统所使用的Linux内核中添加或者修改了代码,那么就必须将其公开,所以,如果Android系统想Linux系统一样,把对硬件的支持完全完全是现在linux硬件驱动中,那么就是说这些实现是开源的,相当于暴露的硬件的实现细节和参数,损伤了厂商的利益。因此,Android才会想到把对硬件的支持分成硬件抽象层和内核驱动层,内核驱动层只提供简单的访问硬件逻辑,例如读写硬件寄存器的通道,至于从硬件中读到了什么值或者写了什么值到硬件中的逻辑,都放在硬件抽象层中去了,这样就可以把商业秘密隐藏起来了。也正是由于这个分层的原因,Android被踢出了Linux内核主线代码树中。

先来一张代码概览图镇楼:

Android HAL 层框架分析以及代码示例_第1张图片

Android HAL 层框架分析以及代码示例_第2张图片

二 开发Android硬件驱动程序

为了方便描述,我们将为一个虚拟的字符硬件设备开发去驱动程序,这个虚拟的字符硬件设备只有一个寄存器,它的大小为4个字节,可读可写,由于这个字符设备是虚拟的,而且只有一个寄存器,因此我们将它命名为 fake register,并且将其对应的驱动名称命名为 freg。

具体驱动程序 :

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int CDRIVER_MAJOR=0;
int CDRIVER_MINOR=0;
static struct class *freg_class = NULL;

static ssize_t freg_read(struct file *filp, char *buffer, size_t count, loff_t *ppos);
static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos);
static int freg_open(struct inode *inode,struct file *filp);
static int freg_release(struct inode* inode, struct file* filp);


static ssize_t freg_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
static ssize_t freg_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n);


struct Freg_struct{
	//struct semaphore sem;//信号量
	struct mutex mutex;//互斥锁
	struct cdev freg_cdev;
	char val;
};
struct Freg_struct *freg_struct = NULL; /*设备结构体指针*/

static struct kobject *freg_kobj;

struct  Freg_Attribute0 {
	struct attribute	attr;
	ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
			char *buf);
	ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
			const char *buf, size_t n);
};

static struct  Freg_Attribute0 freg_attribute[] = {
  //__ATTR(device name,	S_IRUGO | S_IWUSR,	hdmiin_test_show,	hdmiin_test_store),
	__ATTR(freg_dev, 0660,	freg_show, freg_store),
};


static ssize_t freg_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
	int val = 0;
	struct Freg_struct dev;
	
	mutex_lock(&dev.mutex);
	val = dev.val;
	mutex_unlock(&dev.mutex);
	return snprintf(buf, PAGE_SIZE, "%d\n", val);
}

static ssize_t freg_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
{
	struct Freg_struct dev;
	
	mutex_lock(&dev.mutex);
	dev.val = *buf;  
	mutex_unlock(&dev.mutex);
	
	return 0;
}



static ssize_t freg_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
    int ret;
	struct Freg_struct* freg_str = filp->private_data;

	mutex_lock(&freg_str->mutex);
 
    ret=copy_to_user(buffer, (char *)&freg_str->val, sizeof(freg_str->val)); 
    if(ret<0)
    {
        printk("ret =%d \n",ret);
        return ret;
    }
	
	mutex_unlock(&freg_str->mutex);
	
   return 0;
}

static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos)
{
	struct Freg_struct* freg_str = filp->private_data;
	int err;
	
	mutex_lock(&freg_str->mutex);
	
	err=copy_from_user(&(freg_str->val), buf, count);
    if(err<0)
    {
        printk("ret =%d \n",err);
        return err;
    }
	
	mutex_unlock(&freg_str->mutex);
	return 0;
}
 
static int freg_open(struct inode *inode,struct file *filp)
{
	struct Freg_struct* freg_str;
	
	//对于一个字符设备文件, 其inode->i_cdev 指向字符驱动对象cdev,已知 inode->i_cdev(freg_cdev)字符设备地址,以及名称freg_cdev,返回 类型为 Freg_struct 的结构的首地址
	// 需要注意的是 container_of 第三个参数是一个普通变量,并非指针。所以本程序中的字符设备的 struct cdev 定义的是一个普通的 cdev变量,
	 //  而初始化cdev 的方式也必须是静态方法,因为动态方式使用的是 cdev 的指针变量。
	freg_str = container_of(inode->i_cdev, struct Freg_struct, freg_cdev);
	
	//将自定义设备结构体保存在文件指针的私有数据中,以便后面读写的时候用
	filp->private_data = freg_str;
	
    return 0;
}

static int freg_release(struct inode* inode, struct file* filp)
{
	return 0;
}

 
struct file_operations simple_fops={
	
 .owner=THIS_MODULE,
 .open=freg_open,
 .read=freg_read,
 .write=freg_write,
 .release = freg_release,
}; 

static int hello_init(void)
{
    int ret;
	dev_t freg_dev_t; 
	

printk("start freg_init2\n" );

	if(CDRIVER_MAJOR!=0){
		//静态申请设备编号
		freg_dev_t = MKDEV(CDRIVER_MAJOR,CDRIVER_MINOR);
		ret = register_chrdev_region(freg_dev_t,1,"freg_chrdev");
    }else{
        //动态分配设备号
        ret = alloc_chrdev_region(&freg_dev_t,0,1,"freg_chrdev");
        CDRIVER_MAJOR = MAJOR(freg_dev_t);
    }
	
	if(ret < 0){
		printk(KERN_ERR"cannot get major %d \n",CDRIVER_MAJOR);
		printk(KERN_INFO "cannot get major1\n");
		return ret;
	}
 

	//申请设备结构体内存
	freg_struct = kmalloc(sizeof(struct Freg_struct), GFP_KERNEL);
	if(!freg_struct){
		printk(KERN_INFO "freg_struct kmalloc failed \n");
	}
	
	//赋值申请内存空间全部为0
	memset(freg_struct, 0, sizeof(struct Freg_struct));
 
	/*
	//动态申请、初始化 cdev 
	 freg_struct->freg_cdev = cdev_alloc();
	  freg_struct->freg_cdev->ops=&simple_fops;
      freg_struct->freg_cdev->owner=THIS_MODULE;
	*/

printk(KERN_INFO "init cdev start\n");	
	/*初始化 注册 cdev*/
	cdev_init(&freg_struct->freg_cdev, &simple_fops);//静态申请cdev
    freg_struct->freg_cdev.owner = THIS_MODULE;
	freg_struct->freg_cdev.ops = &simple_fops;
	ret = cdev_add(&freg_struct->freg_cdev,freg_dev_t,1);
    if(ret)
    {
        printk(KERN_INFO "cdev_add err!\n");
    }
    
	// 在/sys/class/目录下创建设备类别目录 freg
    freg_class = class_create(THIS_MODULE, "freg");

	// 在/dev/目录和/sys/class/freg 目录下分别创建设备文件 freg
    device_create(freg_class, NULL, freg_dev_t,NULL,"freg");
	
	//freg_kobj = kobject_create_and_add("freg_kobj", NULL); //在 sys下生成 freg_kobj属性目录
	freg_kobj = kobject_create_and_add("freg_kobj", kernel_kobj);//在 /sys/kernel下生成  freg_kobj属性目录
	ret = sysfs_create_file(freg_kobj, &freg_attribute[0].attr);		
	
	mutex_init(&freg_struct->mutex);
	 return 0;
}

static void freg_exit(void)
{
	dev_t freg_dev_t;
	freg_dev_t=MKDEV(CDRIVER_MAJOR,0);
	
    printk( KERN_DEBUG "Module skeleton exit\n" );
    device_destroy(freg_class, freg_dev_t);
    class_destroy(freg_class);
 
    cdev_del(&freg_struct->freg_cdev);
    unregister_chrdev_region(MKDEV(CDRIVER_MAJOR,0),1);
}
 
module_init(hello_init);
module_exit(freg_exit);

MODULE_LICENSE("GPL");

测试程序:

external\freg\freg.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define DEVICE_NAME "/dev/freg" 
 
int main()
{
  int val = 0;
  int fd = -1;
  
  fd = open(DEVICE_NAME, O_RDWR);
  if (fd == -1)
    {
		printf("Failed to open device %s.\n", DEVICE_NAME);
		return -1;
    }
  
  printf("Read original value:\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 value again:\n");
  read(fd, &val, sizeof(val));
  printf("%d.\n\n", val);
  
  close(fd);
  return 0;
}

external\freg\Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
LOCAL_MODULE_TAGS := debug
LOCAL_SRC_FILES := freg.c
LOCAL_MODULE := freg
LOCAL_SYSTEM_SHARED_LIBRARIES := libc
include $(BUILD_EXECUTABLE)

三 开发Android硬件抽象层模块

Android系统为硬件抽象层中的模块接口定义了编写规范,Android系统的硬件抽象层以模块的形式来管理各个硬件访问接口,每一个硬件模块都对应有一个动态链接库文件。这些动态链接库文件的命名符合一定的命名规范,同时在系统内部,每个硬件抽象层模块都使用结构体 hw_module_t 来描述,而硬件设备则结构体 hw_device_t 来描述。下面分别介绍硬件抽象层模块文件的命名规范以及结构体 hw_module_t 和 hw_device_t的定义。

在上面我们完成了内核的驱动程序,我们可以通过内核生成的 /dev 目录下的设备节点来连接 Android系统硬件抽象层和Linux底层驱动层。

3.1 硬件抽象层模块文件 命名规范

硬件抽象层模块文件 命名规范在 hardware/libhardware/hardware.c文件中

hardware/libhardware/hardware.c

/**
 * There are a set of variant filename for modules. The form of the filename
 * is ".variant.so" so for the led module the Dream variants 
 * of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:
 *
 * led.trout.so
 * led.msm7k.so
 * led.ARMV6.so
 * led.default.so
 */

static const char *variant_keys[] = {
    "ro.hardware",  /* This goes first so that it can pick up a different
                       file on the emulator. */
    "ro.product.board",
    "ro.board.platform",
    "ro.arch"
};

硬件抽象层模块文件的命名规范为 “.variant.so”,其中 MODULE_ID 表示模块的ID,variant表示系统的四个属性,ro.hardware, ro.product.board, ro.board.platform,ro.arch 之一。系统在加载硬件抽象层模块时,依次按照 ro.hardware、ro.product.board、ro.board.platform、ro.arch的顺序来取他们的属性值。如果其中一个属性值存在,那么就把它的值作为variant值,然后再检查对应的文件是否存在,如果存在,那么就找到要加载的硬件抽象层模块了,否则就继续查找下一个系统属性,如果这四个系统属性都不存在,或者对应于这四个系统属性硬件抽象层模块文件都不存在,那么就是用 .default.so来作为要加载的硬件抽象层模块文件的名称。

注意:
系统属性 ro.hardware : 表示在系统启动时,由init进程负责设置的。它首先会读取 /proc/cmdline文件,检查里面有没有一个名称为 androidboot.hardware的属性,如果有,就把他作为属性 ro.hardware的值,否则,就将 /proc/cpuinfo文件的内容读取出来,并且将里面的硬件信息解析出来,,即将 Hardware字段的内容作为属性 ro.hardware的值。

系统属性 ro.product.board、ro.board.platform、ro.arch 是从 /system/build.prop文件读取出来的

3.2 硬件抽象层模块结构体 以及 硬件抽象层设备结构体 定义规范

结构体 hw_module_t 和 hw_device_t 以及相关的其他结构体定义在文件 hardware/libhardware/include/hardware/hardware.h中。

#define HARDWARE_MODULE_TAG MAKE_TAG_CONSTANT('H', 'W', 'M', 'T')
#define HARDWARE_DEVICE_TAG MAKE_TAG_CONSTANT('H', 'W', 'D', 'T')

struct hw_module_t;
struct hw_module_methods_t;
struct hw_device_t;


typedef struct hw_module_t {

   /** tag must be initialized to HARDWARE_MODULE_TAG */
    uint32_t tag;
	
    uint16_t module_api_version;
#define version_major module_api_version
    uint16_t hal_api_version;
#define version_minor hal_api_version

    /** Identifier of module */
    const char *id;

    /** Name of this module */
    const char *name;

    /** Author/owner/implementor of the module */
    const char *author;

    /** Modules methods */
    struct hw_module_methods_t* methods;

    /** module's dso */
    void* dso;

#ifdef __LP64__
    uint64_t reserved[32-7];
#else
    /** padding to 128 bytes, reserved for future use */
    uint32_t reserved[32-7];
#endif

} hw_module_t;

typedef struct hw_module_methods_t {
    /** Open a specific device */
    int (*open)(const struct hw_module_t* module, const char* id,
            struct hw_device_t** device);

} hw_module_methods_t;


typedef struct hw_device_t {
    /** tag must be initialized to HARDWARE_DEVICE_TAG */
    uint32_t tag;

    uint32_t version;

    /** reference to the module this device belongs to */
    struct hw_module_t* module;

    /** padding reserved for future use */
#ifdef __LP64__
    uint64_t reserved[12];
#else
    uint32_t reserved[12];
#endif

    /** Close this device */
    int (*close)(struct hw_device_t* device);

} hw_device_t;

#define HAL_MODULE_INFO_SYM         HMI

struct hw_module_t 注意:

  • 1.硬件抽象层中的中的每一个模块都必须自定义一个硬件抽象层模块结构体,而且它的第一个成员变量的类型必须是 hw_module_t。

  • 2.硬件抽象层中的每一个模块都必须存在一个导出符号 HAL_MODULE_INFO_SYM ,即"HMI",他指向一个自定义的硬件抽象层模块结构体。

  • 3.结构体 hw_module_t的成员变量tag的值必须设置为 HARDWARE_MODULE_TAG ,用来标志这是一个硬件抽象层模块结构体。

  • 4.结构体 hw_module_t的成员变量dso用来保存加载硬件抽象层模块后得到的句柄值。因为每一个硬件抽象层模块都对应有一个动态链接库文件,加载硬件抽象层模块的过程实际就是加载与其对应的动态链接库文件的过程。在调用 dlclose 函数来卸载这个硬件抽象层模块时,需要用到这个句柄值。

  • 5.结构体 hw_module_t的成员变量 methods定义了一个硬件抽象层模块的操作方法列表,类型为 hw_module_methods_t。

struct hw_module_methods_t 注意:

*1. 结构体 hw_module_methods_t 只有一个成员变量,一个函数指针,用来打来硬件抽象层模块中的硬件设备,参数module表示要打开的硬件设备所在的模块,id表示要打开的硬件设备的id,参数device是一个输出参数,用来描述一个已经打开的硬件设备。由于一个硬件抽象层模块可能包含多个硬件设备,因此,在调用 hw_module_methods_t的成员变量open来打开一个硬件设备时,我们需要指定一个id,硬件抽象层模块中的硬件设备使用结构体 he_device_t来描述。

struct hw_device_t 注意:

  • 1.硬件抽象层模块中的每一个硬件设备都必须自定义一个硬件设备结构体,而且他的第一个成员变量的类型必须是 hw_device_t。

  • 2.结构体 hw_device_t的成员变量 tag的值必须设置为 HARDWARE_DEVICE_TAG,用来标志这是一个硬件抽象层中的硬件设备结构体

  • 3.成员变量 close 函数指针用来关闭一个硬件设备

  • 4.硬件抽象层中的硬件设备是由其所在的模块提供的接口来打开的,而关闭则是由硬件设备本身提供接口来完成。

3.3 编写硬件抽象层模块接口

每一个硬件抽象层模块在内核中都有一个对应的驱动程序,硬件抽象层模块就是通过这些驱动程序来访问硬件设备的。硬件抽象层中模块接口源文件一般保存在 hardware/libhardware目录中,我们将虚拟硬件设备freg在硬件抽象层中的模块名称定义为 freg,目录结构如下:

hardware/libhardware
--------include
------------hardware
----------------freg.h


hardware/libhardware/Modules
--------freg
---------------freg.cpp
---------------Android.mk

它由三个文件组成,其中freg.h 和 freg.cpp 是源代码文件,而Android.mk是模块的编译脚本文件,三个文件内容如下:

hardware/libhardware/include/hardware/freg.h
freg.h 内容如下

#ifndef ANDROID_FREF_INTERFACE_H
#define ANDROID_FREF_INTERFACE_H

#include 

__BEGIN_DECLS

//定义模块ID
#define FREG_HARDWARE_MODULE_ID "freg"

//定义设备ID
#define FREG_HARDWARE_DEVICE_ID "freg"

//自定义模块结构体
struct freg_module_t{
	struct hw_module_t common;
};

//自定义设备结构体
struct freg_device_t{
	struct hw_device_t common;
	int fd;
	int(*set_val)(struct freg_device_t* dev, int val);
	int(*get_val)(struct freg_device_t* dev, int* val);
};

__END_DECLS
#endif

这个文件中的常量和结构体都是按照硬件抽象层模块编写规范来定义的,宏 FREG_HARDWARE_MODULE_ID 和 FREG_HARDWARE_DEVICE_ID 分别代表模块ID和设备ID,结构体freg_module_t用来描述自定义的模块结构体,他的第一个成员变量的类型是hw_module_t。 结构体freg_device_t是自定义的设备结构体,他的第一个成员变量的类型是hw_device_t 用来描述我们的虚拟硬件设备,其中的fd代表文件描述符,表示需要打开的/dev/xxx 设备结点。其余两个函数指针用来读写 /dev/xxx 设备。

hardware/libhardware/Modules/freg/freg.cpp

freg.cpp 内容如下

#define LOG_TAG "FregHALStub"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define DEVICE_NAME "/dev/freg"
#define MODULE_NAME "Freg"
#define MODULE_AUTHOR "MHR"

//设备打开和关闭接口
static int freg_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);
static int freg_device_close(struct hw_device_t* device);

//设备读写接口
static int freg_set_val(struct freg_device_t* dev, int val);
static int freg_get_val(struct freg_device_t* dev, int* val);

//定义模块操作方式结构体变量
static struct hw_module_methods_t freg_module_methods = {
open: freg_device_open
};

//定义模块结构体变量
struct freg_module_t HAL_MODULE_INFO_SYM = {
	common: {
		tag: HARDWARE_MODULE_TAG,
		version_major: 1,
		version_minor: 0,
		id: FREG_HARDWARE_MODULE_ID,
		name: MODULE_NAME,
		author: MODULE_AUTHOR,
		methods: &freg_module_methods,
	}

};

//虚拟硬件设备freg的打开和关闭分别由  freg_device_open、freg_device_close来实现
static int freg_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device)
{	
	
	struct freg_device_t* dev;
	dev = (struct freg_device_t*)malloc(sizeof(struct freg_device_t));
	
	if(!dev) {
		ALOGI("%s Freg Stub: failed to alloc space", __func__);
		return -EFAULT;
	}
	
	memset(dev, 0, sizeof(struct freg_device_t));
	dev->common.tag = HARDWARE_DEVICE_TAG;
	dev->common.version = 0;
	dev->common.module = (hw_module_t*)module;
	dev->common.close = freg_device_close;
	dev->set_val = freg_set_val;
	dev->get_val = freg_get_val;
	
	if((dev->fd = open(DEVICE_NAME,  O_RDWR)) == -1){
			ALOGI("%s Freg Stub: open /dev/freg Failed.", __func__);
			free(dev);
		}
	
	*device = &(dev->common);
	ALOGI("%s Freg Stub: open /dev/freg successfully.", __func__);
	return 0;

}

static int freg_device_close(struct hw_device_t* device)
{
	struct freg_device_t* freg_device = (struct freg_device_t*)device;
 
	if(freg_device) {
		close(freg_device->fd);
		free(freg_device);
	}
	
	return 0;
}

//虚拟硬件设备 freg的读写函数
static int freg_set_val(struct freg_device_t* dev, int val) {
 
	write(dev->fd, &val, sizeof(val));
	return 0;
}

static int freg_get_val(struct freg_device_t* dev, int* val) {
 
	read(dev->fd, val, sizeof(*val));
	return 0;
}

freg.cpp 代码分析:

//定义模块结构体变量
struct freg_modules_t HAL_MODULE_INFO_SYM = {
	common: {
		tag: HARDWARE_MODULE_TAG,
		version_major: 1,
		version_minor: 0,
		id: FREG_HARDWARE_MODULE_ID,
		name: MODULE_NAME,
		author: MODULE_AUTHOR,
		methods: &freg_module_methods,
	}

};

这段代码中, HAL_MODULE_INFO_SYM 是模块变量,按照硬件抽象层的编写规范,每一个硬件抽象层模块必须导出一个名称为 HAL_MODULE_INFO_SYM 的符号,它指向一个自定义的硬件抽象层模块结构体,而且它的第一个类型为 hw_module_t 的成员变量tag值必须设置为 HARDWARE_MODULE_TAG . 除此之外,还初始化了这个硬件抽象层模块结构体的版本号, ID,名称,作者,操作放大列表等。所以,这里,实例变量名必须为 HAL_MODULE_INFO_SYM ,tag也必须为 HARDWARE_MODULE_TAG ,这是Android硬件抽象层规范规定的。

static int freg_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device)
static int freg_device_close(struct hw_device_t* device)

前面提到,一个硬件抽象层模块可能包含多个硬件设备,而这些硬件设备的打开操作都是由函freg_device_open来完成的,因此,函数freg_device_open会根据传递进来的参数id来判断要打开哪一个硬件设备。在本利硬件抽象层模块freg中,只有一个虚拟硬件设备freg,它使用 freg_device_t来描述,因此,函数 freg_device_open 发现参数id与虚拟硬件设备freg的ID值匹配以后,就会分配一个 freg_device_t结构体,并对其成员变量进行初始化,按照硬件抽象层模块的编写规范,硬件抽象层中的硬件设备标签 (dev->common.tag)必须设置为HARDWARE_DEVICE_TAG,除此之外,我们还将虚拟硬件设备freg的关闭函数设置为 freg_device_close。

hardware/libhardware/Modules/freg/Android.mk

Android.mk 内容如下

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_PRELINK_MODULE := false
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_SRC_FILES := freg.cpp
LOCAL_MODULE := freg.default
include $(BUILD_SHARED_LIBRARY)

这个是硬件抽象层模块freg的编译脚本文件,include $(BUILD_SHARED_LIBRARY) 表示要将该硬件模块抽象层编译成一个动态链接库文件, 名称为 freg.default,并且保存在 $(TARGET_OUT_SHARED_LIBRARIES)/hw (out/target/product/generic/system/lib/hw )目录下。LOCAL_MODULE的定义规则,freg后面跟有default,freg.default能够保证我们的模块总能被硬象抽象层加载到。

mmm hardware/libhardware/modules/

生成 out/target/product/m282a/obj/lib/freg.default.so

注意:
我们将硬件抽象层模块 freg 对应的文件名称定义为 freg.default,编译成功后,系统会自动在后面加后缀.so, 于是就得到了一个 freg.default.so文件。根据硬件抽象层模块文件的命名规范,当我们要加载硬件抽象层模块freg时,只需要指定它的ID值,即 “freg”,系统会根据一定的规则成功的找到要加载啊 freg.default.so文件。硬件抽象层模块 freg 都准备好后,就可以编译打包了,最终会在 out/target/product/generic/system/lib/hw 下得到 freg.default.so文件。硬件抽象层模块文件实际上是一个动态链接库文件,即so文件。另外为了能直接编译,需要在 build\target\product\embedded.mk 中添加 : freg.default
才能编译我们所添加的 hardware模块。最后在 out/target/product/generic/system/lib/hw 下生成 freg.default.so
Android HAL 层框架分析以及代码示例_第3张图片
至此, 虽然我们在Android系统为我们自己的硬件增加了一个硬件抽象层模块,但是现在Java应用程序还不能访问到我们的硬件。我们还必须编写JNI方法和在Android的Application Frameworks层增加API接口,才能让上层Application访问我们的硬件。

四 为Android硬件抽象层编写JNI方法供硬件服务程序调用
4.1 JNI实现

在Android系统中,通常把硬件访问服务的JNI方法实现在frameworks\base\services\core\jni 目录中,因此,我们把实现了硬件访问服务FregService的JNI方法 com_android_service_FregService.cpp 放在该目录下,com_android_server前缀表示的是包名 , 表示硬件服务FregService是放在frameworks/base/services/java目录下的com/android/server目录的, 即存在一个命令为com.android.server.FregService的类 内如如下:

#define LOG_TAG "FregServiceJNI"

#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include 
#include 
#include 
#include 
#include 

namespace android
{
//硬件抽象层中定义的 硬件结构体
	struct freg_device_t* freg_device = NULL;

//通过硬件抽象层定义的硬件访问接口设置硬件寄存器val的值
static void freg_setVal(JNIEnv* env, jobject clazz, jint value) {
		int val = value;
		
		LOGI("Freg JNI: set value %d to device.", val);
		
		if(!freg_device){
			LOGI("Freg JNI: device is not open.");
			return;
		}

		freg_device->set_val(freg_device, val);
	}

//通过硬件抽象层定义的硬件访问接口读取硬件寄存器val的值
	static jint freg_getVal(JNIEnv* env, jobject clazz){
		
		int val = 0;
		
		if(!freg_device){
			LOGE("Device freg is not oprn.");
			return 0;
		}
		freg_device->get_val(freg_device,&val);
		LOGE("Freg JNI: Get value %d from device freg."val);
		return val;
	}

//通过硬件抽象层定义的硬件模块打开接口打开硬件设备
	static inline int freg_device_open(const hw_module_t* module, struct freg_device_t** device)
	{
		return module->methods->open(module, FREG_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
	}	
		
//通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件
	static jint freg_init(JNIEnv* env, jclass clazz){
		freg_module_t* module;
		
		LOGI(Initializing HAL stub freg......);
		
		//加载硬件抽象层模块 freg
		if(hw_get_module(FREG_HARDWARE_MODULE_ID,(const struct hw_module_t**)&module) == 0) {
			LOGI("Freg JNI : freg Stub found.");
			
			//打开虚拟硬件设备freg
			if(freg_device_open(&(module->common),&freg_device) ==0) {
				LOGI("Freg JNI :freg Device found");
				return 0;
			}
			LOGI("Failed to open device freg");
			return -1;
		}
		LOGI("Failed to get HAL stub freg");
		return -1;
	}
	
	//Java 本地接口方法表
	static const JNINativeMethod method_table[] = {
		{"init_native", "()Z", (void*)freg_init},
		{"setVal_native", "(I)V", (void*)freg_setVal},
		{"getVal_native", "()I", (void*)freg_getVal},
	};
	
	//注册java本地接口方法
	int register_android_server_FregService(JNIEnv *env) {
    		return jniRegisterNativeMethods(env, "com/android/server/FregService", method_table, NELEM(method_table));
	}

};

在函数 freg_init 中,首先通过Android硬件抽象层提供的 hw_module_get 函数来加载模块ID为 FREG_HARDWARE_MODULE_ID 的硬件抽象层模块,其中, FREG_HARDWARE_MODULE_ID 是在中定义的。Android硬件抽象层会根据 FREG_HARDWARE_MODULE_ID 的值在Android系统的/system/lib/hw目录中找到相应的模块,然后加载起来,并且返回hw_module_t接口给调用者使用。

需要注意到是,在调用 freg_setVal 和 freg_getVal这两个JNI方法之前,调用者首先要调用JNI方法 freg_init 打开虚拟硬件设备,以便可以获得一个 freg_device_t接口。文件中定义的 JNI方法表 method_table,分别将函数 freg_init、freg_setVal、freg_getVal的JNI方法注册为 init_native setVal_native getVal_native。文件最后调用了 jniRegisterNativeMethods()函数把JNI方法表注册到JAVA虚拟机中,在 jniRegisterNativeMethods 函数中,第二个参数的值必须对应FregService所在的包的路径,即 com.android.server.FregService。以便提供给硬件访问服务 FregService 使用。

4.2 声明JNI注册方法

增加 register_android_server_FregService()函数的声明和调用。

frameworks\base\services\core\jni\onload.cpp

namespace android {
...
...
int register_android_server_FregService(JNIEnv *env);
};

using namespace android;
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */){

	...
	register_android_server_FregService(env);
}
4.3 添加JNI方法代码

最后到 frameworks\base\services\core\jni 目录中,打开里面的Android.mK文件,修改变量LOCALSRC_FILS的值。

frameworks\base\services\core\jni\Android.mK

LOCAL_SRC_FILES += \
......
com_android_server_FregService.cpp \
onload.cpp

最后执行

mmm frameworks/base/services/core/jni 

最后得到的 libandroid_service.so文件就包含 init_native、setVal_native、getVal_native 这三个JNI方法了。至此 硬件访问服务FregService的实现就完成了。下面介绍如何在系统进程System中启动它。

五 开发Android硬件访问服务

在前面,我们介绍了如何为Android系统的硬件编写驱动程序,包括如何在Linux内核空间实现内核驱动程序和在用户空间实现硬件抽象层接口。实现这两者的目的是为了向更上一层提供硬件访问接口,即为Android的Application Frameworks层提供硬件服务。那么到此为止,在开发好硬件抽象层模块之后,我们还需要在应用程序框架层中实现一个硬件访问服务,硬件访问服务通过硬件抽象层模块来为应用程序提供硬件读写操作接口,由于硬件抽象层模块是使用 C++/C 语言开发的,而应用程序框架中的硬件访问服务是使用JAVA语言开发的,那么,Java接口如何去访问C接口呢?众所周知,Java提供了JNI方法调用,因此,硬件访问服务必须通过JAVA本地接口(Java Native Interface JNI)来调用硬件抽象层模块的接口。Android系统的硬件访问服务通常运行在系统进程System中,而使用这些硬件访问服务的应用程序运行在其他的进程中,即应用程序需要通过进程间通信机制来访问这些硬件访问服务,Android系统提供了一种高效的进程间通信机制–Binder进程间通信机制,应用程序就是通过它来访问运行在系统进程System中的硬件访问服务的。Binder进程间通信机制要求要求提供服务的一方必须实现一个具有跨进程访问能力的服务接口,以便使用服务的一方可以通过这个服务接口来访问它。因此,在实现硬件访问服务之前,我们首先要定义它的服务接口。

所以综上所述,我们在此处要实现以下2个任务

1.定义硬件访问服务接口 
2.实现硬件访问服务 最后 启动硬件访问服务
5.1 定义硬件访问服务接口

Android 系统提供了一种描述语言来定义具有跨进程访问能力的服务接口,这种描述语言称为 Android 接口描述语言(Android Interface Definition Language, AIDL)。以AIDL定义的服务接口文件是以aidl为后缀的,在编译时,编译系统会将他们转换成Java文件,然后对他们进行编译,此处我们使用AIDL来定义硬件访问服务接口 IFregService。在Android系统中,通常把硬件访问服务接口定义在 framework/base/core/java/android/os 目录中,因此,我们把定义了硬件访问服务接口 IFregService 的文件 IFregService.aidl也保存在这个目录中,内容如下:

framework/base/core/java/android/os/IFregService.aidl

package android.os;

//注: 注释中需要添加{@hide},不然会出现编译问题 
/** @hide */
interface IFregService {
	void setVal(int val);
	int getVal();
}

IFregService服务接口之定义了两个成员函数,setVal用来往虚拟硬件设备freg写,另一个是读。

由于服务器接口 IFregService 是使用AIDL语言描述的,因此,我们需要将其添加到编译脚本文件中,这样编译系统才能将其转换为Java文件,然后再对它进行编译,进入framework/base目录中,打开里面的 Andriod.mk文件,修改 LOCAL_SRC_FILES 变量的值,增加IFregService.aidl源文件

frameworks/base\ Andriod.mk

LOCAL_SRC_FILES += \
.....
voip/java/android/net/sip/ISipService.aidl \
...
core/java/android/os/IFregService.aidl

修改这个编译脚本之后,可以使用mmm命令对硬件访问服务接口IFregService进行编译了,

mmm frameworks/base 

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

5.2 实现硬件访问服务 最后 启动硬件访问服务

在Android系统中,通常把硬件访问服务实现在 frameworks/base/services/java/com/android/server 中,因此,我们把实现了硬件访问服务 FregService的FregService.java文件也保存在这个目录中,内容如下:

frameworks/base/services/java/com/android/server/FregService.java

package com.android.server;
import android.content.Context;
import android.os.IFregService;
import android.util.Slog;

/**
 * @hide
 */

// 定义类FregService 继承自  IFregService.Stub
public class FregService extends IFregService.Stub{
	
private static final String TAG = "FregService";//final关键字定义常量,就使得他在被定义后无法再对此进行赋值

FregService(){
	init_native();
}

public void setVal(int val){
	setVal_native(val);
}

public int getVal(){
	return getVal_native();
}

private static native int init_native();
private static native void setVal_native(int val);
private static native int getVal_native();

};

硬件访问服务FregService继承了IFregService.Stub类,并且实现了IFregService接口的成员函数setVal和getVal,其中,成员函数setVal通过调用JNI方法来写虚拟硬件设备freg,而成员函数getVal调用JNI方法getVal_native来读虚拟硬件设备。

前面提到,Android系统的硬件访问服务通常是在系统进程System中启动的,而系统进程System是在开机时自动启动,因此,将硬件访问服务运行在系统进程System中,就可以实现开机时自动启动,此处我们将硬件访问服务 FregService运行在系统进程System中,因此,进入到 frameworks/base/service/java/com/android/server目录下,打开里面的 SystemService.java 文件,修改ServiceThread类的成员函数 run的实现

         public void run() {
            Slog.i(TAG, "Making services ready");
			......
			
			try {
			Slog.i(TAG,"Freg Service");
			ServiceManager.addService("freg", new FregService());
			} catch(Throwable e) {
			Slog.i(TAG,"Failure starting Freg Service", e);
			}

        }

系统进程System在启动时,会创建一个 ServiceThread 线程来启动系统中的关键服务,其中就包括一些硬件访问服务,在ServiceThread类的成员函数run中,首先创建一个 FregService实例,然后把他注册到 Service Manager中,Service Manager是Android系统的Binder进程间通信机制的一个重要角色,他负责管理系统中的服务对象,注册到 Service Manager 中的服务对象都有一个对应的名称,使用这些服务的Client进程就是通过这些名称来向 Service Manager 请求他们的Binder代理对象接口的,以便可以访问他们所提供的服务,硬件访问服务 FregService 注册到 Service Manager之后,他的启动过程就完成了。

最后执行

mmm frameworks/base/services/java/ 

编译后得到的 service.jar 文件就包含有硬件访问服务FregService,并且在系统启动时,将他运行在系统进程System中。

五 开发Android应用程序来使用硬件访问服务

。。。。。。待续。。。。。。

你可能感兴趣的:(Android系统分析篇)