一、驱动篇
1、内核源码目录/drivers/下,新建hello目录
2、在hello目录下创建hello.h文件:
#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; // 代表寄存器,类型为int
struct semaphore sem; // 信号量,用于同步操作
struct cdev dev; // 内嵌的字符设备,这个Linux驱动程序自定义字符设备结构体的标准方法
};
#endif
3、在hello目录下创建hello.c文件
#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 <linux/uaccess.h>
#include <linux/slab.h>
#include "hello.h"
/*
在hello目录中增加hello.c文件,这是驱动程序的实现部分。驱动程序的功能主要是向上层提供访问设备的寄存器的值,包括读和写。
这里,提供了三种访问设备寄存器的方法:
一是通过proc文件系统来访问,
二是通过传统的设备文件的方法来访问,
三是通过devfs文件系统来访问。
下面分段描述该驱动程序的实现。
————————————————
版权声明:本文为CSDN博主「罗升阳」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/luoshengyang/java/article/details/6568411
*/
/* 主设备和从设备号变量 */
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_t cout, 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, struct device_attribute* attr, const char* buf, size_t count);
/* 定义设备属性*/
static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, hello_val_show, hello_val_store);
/* 打开设备方法*/
static int hello_open(struct inode* inode, struct file* filp) {
struct hello_android_dev* dev;
/* 将自定义设备结构体保存在文件指针的私有数据域中,以便访问设备时拿来用*/
dev = (struct hello_android_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_t count, 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;
}
/*读取寄存器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, struct device_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);
}
/*读取设备寄存器val的值,保存在page缓存区中*/
static ssize_t hello_proc_read(char* page, char** start, off_t off, int count, 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 is too large:%lu.\n", len);
return -EFAULT;
}
page = (char*)__get_free_page(GFP_KERNEL);
if(!page) {
printk(KERN_ALERT"Failed to alloc page.\n");
return -ENOMEM;
}
/*先把用户提供的缓冲区值拷贝到内核缓冲区去*/
if(copy_from_user(page, buff, len)) {
printk(KERN_ALERT"Failed to copy 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 = proc_create(HELLO_DEVICE_PROC_NAME, 0644, 0, &hello_fops);
/*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);
}
/* 最后,定义模块加载和卸载方法,这里只要执行设备注册和初始化操作 */
/*初始化设备*/
static int __hello_setup_dev(struct hello_android_dev* dev) {
int err;
dev_t devno = MKDEV(hello_major, hello_minor);
memset(dev, 0, sizeof(struct hello_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的值*/
sema_init(&(dev->sem), 1);
//init_MUTEX(&(dev->sem)); 2.6.25及以后的linux内核版本废除了init_MUTEX函数
dev->val = 0;
return 0;
}
/* 模块加载方法*/
static int __init hello_init(void) {
//宏定义__init,用于告诉编译器相关函数或变量仅用于初始化。
//编译器将标__init的所有代码存在特殊的内存段中,初始化结束后就释放这段内存。
int err = -1;
dev_t dev = 0;
struct device* temp = NULL;
printk(KERN_ALERT"Initializeing hello device.\n");
/* 动态分配主设备和从设备号*/
err = alloc_chrdev_region(&dev, 0, 1, HELLO_DEVICE_NODE_NAME);
if(err < 0) {
printk(KERN_ALERT"Failed to alloc char dev region.\n");
goto fail;
}
hello_major = MAJOR(dev);
hello_minor = MINOR(dev);
/*分配hello设备结构体变量*/
hello_dev = kmalloc(sizeof(struct hello_android_dev), GFP_KERNEL);
if(!hello_dev) {
err = -ENOMEM;
printk(KERN_ALERT"Failed to alloc hello_dev.\n");
goto unregister;
}
/*初始化设备*/
err = __hello_setup_dev(hello_dev);
if(err) {
printk(KERN_ALERT"Failed to setup 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 to create 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 to create 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 to create attribute val.");
goto destroy_device;
}
dev_set_drvdata(temp, hello_dev);
/*创建/proc/hello文件*/
hello_create_proc();
printk(KERN_ALERT"Succedded to initialize 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) {
//__exit修饰词标记函数只在模块卸载时使用。如果模块被直接编进内核则该函数就不会被调用。
//如果内核编译时没有包含该模块,则此标记的函数将被简单地丢弃。
dev_t devno = MKDEV(hello_major, hello_minor);
printk(KERN_ALERT"Destory hello device.\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);
4、在hello目录下创建Kconfig和Makefile两个文件:
Makefile文件:
obj-$(CONFIG_HELLO) += hello.o
Kconfig文件:
config HELLO
tristate "First Android Driver"
default n
help
This is the first android driver.
说明:在Kconfig配置文件中,tristate表示编译选项HELLO支持在编译内核时,hello模块支持以模块、内建和不编译三种编译方法,默认编译为模块,因此,在编译内核前,我们需要使用make menuconfig命令配置编译选项,使得hello可以以模块或者内建的方法进行编译。 在Makefile文件中,根据选择HELLO的值,执行不同的编译方法。
5、添加hello模块项目到驱动程序中
修改drivers/Kconfig 在menu "Device Drivers"后添加一行:
menu "Device Drivers"
**************************
source "drivers/mux/Kconfig"
source "drivers/hello/Kconfig" //添加此行
endmenu
将我们自定义的hello模块引入,修改drivers/Makefile,添加hello编译模块在里面添加,在最前面一行添加:
obj-$(CONFIG_HELLO) += hello/
在内核目录/arch/x86/configs/x86_64_ranchu_defconfig文件中添加一行:(因为我源码用的是goldfish-refs_heads_android-goldfish-4.14-gchips,而且编译用的是aosp_x86_64-eng模式,如果刷机用的是实际设备,且编译模式用的是aosp_ranchu-userdebug,就需选择内核目录/arch/arm64/configs/arm64_ranchu_defconfig)
CONFIG_HELLO=y //在最后一行添加
这里m表示编译成module,y表示编译进kernel只可以看到.o不会生成.ko 这样,执行make menuconfig时,就可以配置hello模块的编译选项了,配置为编译成模块。.
6、配置编译选择
步骤如下:
a、内核目录下执行make menuconfig,弹窗选择框,进入Device Drivers-->选择First Android Driver 按y键是选中,n是不选中;
b、注意: 如果内核不支持动态加载模块,这里不能选择m,虽然我们在Kconfig文件中配置了HELLO选项为tristate。要支持动态加载模块选项,必须要在配置菜单中选择Enable loadable module support选项;在支持动态卸载模块选项,必须要在Enable loadable module support菜单项中,选择Module unloading选项。 我们这里是支持动态加载的,因此选择m,方便加载调试。
7、编译内核
make kernel
二、Android自定义驱动HAL层
完成内核驱动程序后,便可以在Android系统中得到三个文件/dev/hello、/sys/class/hello/hello/val和/proc/hello。我们将通过/dev/hello来连接硬件抽象层模块(HAL)和Linux内核驱动程序模块。
1、定义HAL抽象层hello.h头文件
进入到hardware/libhardware/include/hardware目录,新建hello.h文件
#ifndef ANDROID_HELLO_INTERFACE_H
#define ANDROID_HELLO_INTERFACE_H
#include <hardware/hardware.h>
//__BEGIN_DECLS
#if defined(__cplusplus)
extern "C" {
#endif
/* define module ID */
#define HELLO_HARDWARE_MODULE_ID "hello"
/* hardware module struct */
struct hello_module_t {
struct hw_module_t common;
};
/* hardware interface struct */
struct hello_device_t {
struct hw_device_t common;
int fd; // 设备文件描述符,对应我们将要处理的设备文件"/dev/hello"
int (*set_val)(struct hello_device_t* dev, int val); // set_val 为HAL对上提供的函数接口
int (*get_val)(struct hello_device_t* dev, int* val); // // get_val 为HAL对上提供的函数接口
};
#if defined(__cplusplus)
}
#endif
//__END_DECLS
#endif
这里按照标准Android硬件标准抽象层规范要求,分别定义模块ID、模块结构体以及硬件接口结构体。在硬件接口结构体中,fd表示设备文件描述符,对应我们将要处理的设备/dev/hello,set_val和get_val为该HAL对上提供的函数接口。
2、实现硬件访问hello.c
进入到hardware/libhardware/modules目录,新建hello目录,并添加hello.c文件。
#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>
#include <string.h> /* memset */
#include <unistd.h> /* close */
#include <stdio.h>
#include <stdlib.h>
#define DEVICE_NAME "/dev/hello"
#define MODULE_NAME "Hello"
#define MODULE_AUTHOR "momxmo"
/*设备打开和关闭接口*/
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 = {
// 这里,实例变量名必须为HAL_MODULE_INFO_SYM, tag也必须为HARDWARE_MODULE_TAG
// 这是android硬件抽象层规范规定的。
.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,
}
};
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));
ALOGD("Hello Stub: hello_device_open_name=%s",name);
if(!dev) {
ALOGE("Hello Stub: failed to alloc space");
return -EFAULT;
}
// memset是计算机中C/C++语言初始化函数。作用是将某一块内存中的内容全部设置为指定的值,
//这个函数通常为新申请的内存做初始化工作
memset(dev, 0, sizeof(struct hello_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;
// open是UNIX系统(包括LINUX、Mac等)的系统调用函数,区别于C语言库函数fopen
// https://baike.baidu.com/item/open/13009226#1_1
if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
ALOGE("Hello Stub:failed to open /dev/hello --%s.", strerror(errno));
free(dev);
return -EFAULT;
}
*device = &(dev->common);
ALOGI("Hello Stub: open /dev/hello successfully.");
return 0;
}
static int hello_device_close(struct hw_device_t* device) {
//强转类型
struct hello_device_t* hello_device = (struct hello_device_t*)device;
ALOGD("Hello Stub: hello_device_close");
if(hello_device) {
close(hello_device->fd);
ALOGI("Hello Stub:hello_device_close hello_device->fd:%d", hello_device->fd);
free(hello_device);
}
return 0;
}
static int hello_set_val(struct hello_device_t * dev, int val) {
ALOGI("Hello Stub: hello_set_val set value %d to device.", val);
// write()会把参数buf所指的内存写入count个字节到参数fd所指的文件内。 fd,是open时打开的
// 例: int fp = open("/home/test.txt", O_RDWR|O_CREAT);
int result = write(dev->fd, &val, sizeof(val));
ALOGI("Hello Stub:hello_set_val result:%d", result);
return 0;
}
static int hello_get_val(struct hello_device_t* dev, int* val) {
ALOGD("Hello Stub: hello_get_val");
if(!val) {
ALOGE("Hello Stub: error val pointer");
return -EFAULT;
}
int result = read(dev->fd, val, sizeof(*val));
ALOGI("Hello Stub:hello_get_val result:%d", result);
ALOGI("Hello Stub: get value %d from device.", *val);
return 0;
}
3、节点权限问题:
DEVICE_NAME定义为"/dev/hello"。由于设备文件是在内核驱动里面通过device_create创建的,而device_create创建的设备文件默认只有root用户可读写,而hello_device_open一般是由上层APP来调用的,这些APP一般不具有root权限,这时候就导致打开设备文件失败: Hello Stub: failed to open /dev/hello -- Permission denied. 解决办法: 类似于Linux的udev规则,打开Android源代码工程目录下,修改system/core/rootdir/ueventd.rc文件,往里面添加一行:
/dev/vndbinder 0666 root root
/dev/hello 0666 root root //添加此行
4、在hardware/libhardware/modules/hello目录中,新建Android.bp文件:
cc_library_shared {
name: "hello.default",
relative_install_path: "hw",
proprietary: true,
srcs: ["hello.c"],
cflags: ["-Wall", "-Werror"],
header_libs: ["libhardware_headers"],
shared_libs: [
"libcutils",
"liblog",
"libutils",
],
}
注意: LOCAL_MODULE的定义规则,hello后面跟有default, hello.default能够保证我们的模块总能被硬象抽象层加载到
5、将hello模块添加到系统
这里的目的是保证在整编译系统的时候,才会将hello模块打包到vendor.img镜像中; 需要在/hardware/libhardware/modules/Android.mk中添加hello模块:
hardware_modules := \
camera \
gralloc \
sensors \
hello // 添加此行
include $(call all-named-subdir-makefiles,$(hardware_modules))
6、编译,执行命令
mmm hardware/libhardware/modules/hello
编译成功后,就可以在Android/out/target/product/angler/vendor/lib64/hw目录下看到hello.default.so文件了。
7、so库导入系统中
如果不想重新打包镜像系统,这里我们可以通过adb push的方式将hello.default.so文件push到手机系统/vendor/lib64/hw/目录下,并添加777权限;可以通过adb shell命令,进入到此文件夹观察hello.default.so文件是否存在此目录下.
8、这样hello.default.so文件已经存在系统的vendor/lib64/hw文件夹中,但添加app后打开HAL层后仍报错:log显示 : Access denied finding property "ro.hardware.hello" 很明显是权限不允许。然后从Hello JNI: failed to get hello stub hw_module.的打印可以知道是 hw_get_module 的时候没有找到我们新添加的HAL层。 其实后面还有一句比较长的log
system_server: type=1400 audit(0.0:111): avc: denied { read } for name="u:object_r:vendor_default_prop:s0" dev="tmpfs" ino=292 scontext=u:r:system_server:s0 tcontext=u:object_r:vendor_default_prop:s0 tclass=file permissive=0
通过前的log我们知道,这个是selinux权限的问题,因此这里需要配置selinux的权限
修改两个文件:/system/sepolicy/prebuilts/api/29.0/public/property_contexts和/system/sepolicy/public/property_contexts:
*****************************************************************************
ro.hardware.fingerprint u:object_r:exported_default_prop:s0 exact string
ro.hardware.hello u:object_r:exported_default_prop:s0 exact string //添加这一行
ro.hardware.flp u:object_r:exported_default_prop:s0 exact string
*****************************************************************************
之后还是报错:
avc: denied { read } for name="hello.default.so" dev="dm-1" ino=596 scontext=u:r:system_server:s0 tcontext=u:object_r:vendor_file:s0 tclass=file permissive=0
还需要修改 /system/sepolicy/vendor/file_contexts:
# Same process HALs installed by platform into /vendor
/(vendor|system/vendor)/lib(64)?/hw/android\.hardware\.graphics\.mapper@2\.0-impl\.so u:object_r:same_process_hal_file:s0
/(vendor|system/vendor)/lib(64)?/hw/android\.hardware\.graphics\.mapper@3\.0-impl\.so u:object_r:same_process_hal_file:s0
/(vendor|system/vendor)/lib(64)?/hw/android\.hardware\.renderscript@1\.0-impl\.so u:object_r:same_process_hal_file:s0
/(vendor|system/vendor)/lib(64)?/hw/gralloc\.default\.so u:object_r:same_process_hal_file:s0
/(vendor|system/vendor)/lib(64)?/hw/hello\.default\.so u:object_r:same_process_hal_file:s0 //添加这一行
编译完后又发现有报错:
system_server: type=1400 audit(0.0:114): avc: denied { read write } for name="hello" dev="tmpfs" ino=7375 scontext=u:r:system_server:s0 tcontext=u:object_r:device:s0 tclass=chr_file permissive=0
还需要配置selinux权限,分别修改/system/sepolicy/private/和/system/sepolicy/prebuilts/api/29.0/private/文件夹下的file_contexts和system_server.te文件,/system/sepolicy/public/和/system/sepolicy/prebuilts/api/29.0/public/文件夹下的device.te文件:
file_contexts:
************************************************************
/dev/zero u:object_r:zero_device:s0
/dev/hello u:object_r:hello_device:s0 //添加这一行
/dev/__properties__ u:object_r:properties_device:s0
/dev/__properties__/property_info u:object_r:property_info:s0
system_server.te:
************************************************************
allow system_server adbd_socket:sock_file rw_file_perms;
allow system_server rtc_device:chr_file rw_file_perms;
allow system_server audio_device:dir r_dir_perms;
allow system_server hello_device:chr_file rw_file_perms; //添加这一行
device.te:
***********************************************************
type tty_device, dev_type;
type video_device, dev_type;
type zero_device, dev_type, mlstrustedobject;
type hello_device, dev_type, mlstrustedobject; //添加这一行
type fuse_device, dev_type, mlstrustedobject;
**********************************************************
之后重新编译完成就可以了,但是直接编译m可能会出错,也是由于权限策略的问题,可以使用命令:
make SELINUX_IGNORE_NEVERALLOWS=true
正常完成编译并打开系统应用后,初始化可以得到以下日志:
/system_process D/HelloManagerService: HelloService init
/system_process I/HelloService: Hello JNI: initializing......
/system_process I/HelloService: Hello JNI: hello Stub found.
/system_process D/HelloStub: Hello Stub: hello_device_open_name=hello
/system_process I/HelloStub: Hello Stub: open /dev/hello successfully.
/system_process I/HelloService: Hello JNI: hello device is open.
可以发现已经找到驱动并已经打开,说明HAL层添加成功。
三、自定义驱动Framework JNI调用
因为android10之后已不再支持HDIL调用,故仅支持此jni调用。
1、实现JNI
在frameworks/base/services/core/jni/
目录新建com_android_server_HelloService.cpp
文件: 实现JNI方法。
#define LOG_TAG "HelloService"
#include "jni.h"
#include <nativehelper/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方法:
namespace android
{
/*在硬件抽象层中定义的硬件访问结构体,参考<hardware/hello.h>*/
struct hello_device_t* hello_device = NULL;
/*通过硬件抽象层定义的硬件访问接口设置硬件寄存器val的值*/
static void hello_setVal(JNIEnv* env, jobject clazz, jint value) {
int val = value;
ALOGI("Hello JNI: set value %d to device.", val);
if(!hello_device) {
ALOGI("Hello JNI: device is not open.");
return;
}
hello_device->set_val(hello_device, val);
}
/*通过硬件抽象层定义的硬件访问接口读取硬件寄存器val的值*/
static jint hello_getVal(JNIEnv* env, jobject clazz) {
int val = 0;
if(!hello_device) {
ALOGI("Hello JNI: device is not open.");
return val;
}
hello_device->get_val(hello_device, &val);
ALOGI("Hello JNI: get value %d from device.", val);
return val;
}
/*通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/
static inline int hello_device_open(const hw_module_t* module, struct hello_device_t** device) {
return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
}
/*通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件*/
static jboolean hello_init(JNIEnv* env, jclass clazz) {
hello_module_t* module;
ALOGI("Hello JNI: initializing......");
if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {
ALOGI("Hello JNI: hello Stub found.");
if(hello_device_open(&(module->common), &hello_device) == 0) {
ALOGI("Hello JNI: hello device is open.");
return 0;
}
ALOGE("Hello JNI: failed to open hello device.");
return -1;
}
ALOGE("Hello JNI: failed to get hello stub module.");
return -1;
}
/*JNI 方法表*/
// java和native的方法对应表,第一个是java中的方法,
//第二个是参数(括号中为参数)和返回值,含义为:[B -- byte数组,Z -- bool, I -- int, V -- void,
// Ljava/lang/String; -- String对象, [Ljava/lang/String; -- String对象数组
//第三个对应的是native中的函数
static const JNINativeMethod method_table[] = {
{"init_native", "()Z", (void*)hello_init},
{"setVal_native","(I)V", (void*)hello_setVal},
{"getVal_native","()I",(void*)hello_getVal},
};
/*注册JNI方法*/
// 第二个参数的值必须对应HelloService所在的包的路径,即com.android.server.HelloService
int register_android_server_HelloService(JNIEnv* env) {
return jniRegisterNativeMethods(env, "com/android/server/hello/HelloManagerService", method_table, NELEM(method_table));
}
}
2、同时在此文件夹下修改onload.cpp文件
namespace android {
.........添加一
int register_android_server_HelloService(JNIEnv *env);
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
............添加二
register_android_server_HelloService(env); // 这样,在Android系统初始化时,就会自动加载该JNI方法调用表
return JNI_VERSION_1_4;
}
3、修改同目录下的Android.bp文件
srcs: [
**********************
"com_android_server_HelloService.cpp", //添加这一行
"onload.cpp",
":lib_networkStatsFactory_native",
],
4、编译,单模块编译如下:
root@ubuntu:frameworks/base/services/core/jni$ mm -j16
四、自定义驱动Framework Java服务
1、定义AIDL接口
在frameworks/base/core/java/android/app目录下创建IHelloManager.aidl文件:
package android.app;
/** {@hide} */
interface IHelloManager {
void init();
//@UnsupportedAppUsage
void setVal(int val);
//@UnsupportedAppUsage
int getVal();
}
2、在frameworks/base/core/java/android/app目录下创建服务HelloManager.java
package android.app;
import android.annotation.SystemService;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import android.annotation.Nullable;
import android.os.ServiceManager;
import android.util.Singleton;
@SystemService(Context.Hello_SERVICE)
public class HelloManager {
private Context mContext;
/**
* @hide
*/
public HelloManager() {
}
/**
* @hide
*/
public static IHelloManager getService() {
return IHelloManagerSingleton.get();
}
@UnsupportedAppUsage
private static final Singleton<IHelloManager> IHelloManagerSingleton =
new Singleton<IHelloManager>() {
protected IHelloManager create() {
final IBinder b = ServiceManager.getService(Context.Hello_SERVICE);
final IHelloManager im = IHelloManager.Stub.asInterface(b);
return im;
}
};
@Nullable
public void init(){
try{
getService().init();
}catch(RemoteException e) {
e.rethrowFromSystemServer();
}
}
@Nullable
public void setVal(int val){
try{
getService().setVal(val);
}catch(RemoteException e) {
e.rethrowFromSystemServer();
}
}
@Nullable
public int getVal() {
try {
return getService().getVal();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
return -1;
}
}
3、在frameworks/base目录下修改Android.bp文件
srcs: [
**********************************************
"core/java/android/app/IHelloManager.aidl", //添加这一行
":apex-properties",
":platform-properties",
":framework-statslog-gen",
],
4、创建服务HelloManagerService.java
在frameworks/base/service/core/java/com/android/server下新建hello文件夹,并在此文件夹下新建HelloManagerService.java文件
package com.android.server.hello;
import android.app.IHelloManager;
import android.util.Log;
public class HelloManagerService extends IHelloManager.Stub {
private static final String TAG = "HelloManagerService";
public HelloManagerService() {
Log.d(TAG, "HelloService init_native");
init_native();
}
public void init(){
Log.d(TAG, "HelloService init");
init_native();;
}
public void setVal(int val) {
Log.d(TAG, "HelloService setVal val:" + val);
setVal_native(val);
}
public int getVal() {
Log.d(TAG, "HelloService getVal val");
return getVal_native();
}
private static native boolean init_native();
private static native void setVal_native(int val);
private static native int getVal_native();
};
5、修改Context,在frameworks/base/core/java/android/content/Context.java中加入常量:
/** @hide */
@StringDef(suffix = { "_SERVICE" }, value = {
***************
Hello_SERVICE, //添加自定義服務名
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
public static final String Hello_SERVICE="hello"; //添加常量
6、在frameworks/base/services/java/com/android/server/SystemServer.java中 注册系统服务
在系統服務中startOtherService()方法中注冊自定義服務。
********************************
import com.android.server.hello.HelloManagerService;
********************************
startOtherService(){
***************************************************
//HelloManagerService
traceBeginAndSlog("StartHelloManagerService");
ServiceManager.addService(Context.Hello_SERVICE,new HelloManagerService());
traceEnd();
*******************************
}
7、在frameworks/base/core/java/android/app/SystemServiceRegistry.java注册服务获取器:
************************************
import android.app.HelloManager;
import android.app.IHelloManager;
*************************************
static {
//CHECKSTYLE:OFF IndentationCheck
registerService(Context.Hello_SERVICE, HelloManager.class,
new CachedServiceFetcher<HelloManager>() {
@Override
public HelloManager createService(ContextImpl ctx) throws
ServiceNotFoundException {
return new HelloManager();
}});
*******************************************************
}
8、配置SeLinux權限
在 system/sepolicy/prebuilts/api/29.0/private/ 与 system/sepolicy/private/ 目录下,分别修改:
注意:兩個目錄下文件 需要一致,即使空格和標點符號也要一致
service_contexts: 在中間插入:
**************************************************************
activity u:object_r:activity_service:s0
#配置自定义服务selinux角色
hello u:object_r:hello_service:s0
****************************************************************
service.te 在末尾添加:
****************************************************
type uce_service, service_manager_type;
#配置自定义服务类型
type hello_service, app_api_service, ephemeral_app_api_service, system_server_service,service_manager_type; //添加这一行
untrusted_app_all.te 在末尾添加:
#允许所有app使用自定义服务
allow untrusted_app_all hello_service:service_manager find;
9、更新api
make update-api
10、編譯
m
11、修改SDK
a、在SDK/platforms目錄下復制一份android-29(android10),並改名爲:android-311,刪除裏面的android.jar文件,並將源碼中out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes-header.jar文件復制到文件夾,其將其改名爲android.jar。
注意:我之前在编译android12源码时在上面这个文件夹下拿的classes-header.jar文件有57M,可以确定这个jar包应该包含了sdk包中android12平台下android.jar包的全部内容,并且还包含了我们自定义服务的HelloManager.class(在此jar包的/android/app/文件夹下),但android10源码编译平台编译后在此文件夹下生成的classes-header.jar包只有6.1M,虽然含有我们的HelloManager.class,但肯定不能完全包含sdk包中的android10平台下的android.jar包全部内容,故我是把android10平台编译生成的classes-header.jar包中的HelloManager.class文件拷贝到sdk中的android10平台中的android.jar中的。如果谁知道android10平台编译的完整的classes-header.jar文件是在哪个文件夹下,也可以回复我,这个完整的jar包确实我是找了好久没找到,因为我最开始是编译android12源码的,只知道android12源码编译完整的jar包是在这个文件夹下。
b、修改SDK/platforms/android-311文件夾中的source.properties、package.xml文件.
source.properties文件:
#指定自定义平台标识为311(可以是任意数字,但为了与原生标识区分,请使用三位数)
#修改:
Pkg.Desc=Android SDK Platform 311
Pkg.UserSrc=false
#修改:
Platform.Version=311
Platform.CodeName=
Pkg.Revision=1
Layoutlib.Api=15
AndroidVersion.ApiLevel=311
#修改:
Layoutlib.Revision=1
Platform.MinToolsRev=22
package.xml文件 :
<localPackage path="platforms;android-311" obsolete="false">
<type-details xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="ns5:platformDetailsType">
<!-- 修改 -->
<api-level>311</api-level>
<codename></codename>
<layoutlib api="15"/></type-details>
<revision>
<major>1</major>
</revision>
<!-- 修改 -->
<display-name>Android SDK Platform 311</display-name>
<uses-license ref="android-sdk-license"/>
</localPackage>
c、復制SDK/source/android-31文件夾並更名爲android-311
修改source.properties文件:
Pkg.UserSrc=false
Pkg.Revision=1
AndroidVersion.ApiLevel=311
修改package.xml文件:
<localPackage path="platforms;android-311" obsolete="false">
<type-details xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="ns5:platformDetailsType">
<!-- 修改 -->
<api-level>311</api-level>
<codename></codename>
<layoutlib api="15"/></type-details>
<revision>
<major>1</major>
</revision>
<!-- 修改 -->
<display-name>Android SDK Platform 311</display-name>
<uses-license ref="android-sdk-license"/>
</localPackage>
12、在Android Studio中配置build.gradle:
android {
compileSdk 311
....
defaultConfig {
targetSdk 311
}
}
13、新建工程調用LanceManager自定義系統服務
HelloManager helloManager = (HelloManager)getSystemService("hello");
接下来即可使用已注册的api:
helloManager.init();
helloManager.getVal();
helloManager.setVal(int val);