android自定义驱动编译+Framework层调用

一、驱动篇

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);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值