这篇文章接上一篇文章是第二篇,主要介绍如果给Android添加hal层,并解决添加过程中所遇见的selinux问题
AndroidQ 从app到驱动 第一章 编写Linux内核驱动程序
AndroidQ 从app到驱动 第二章 添加HAL层,先打通JNI层到驱动的访问
AndroidQ 从app到驱动 第三章 SystemServer服务中添加 HelloService
AndroidQ 从app到驱动 第四章 编写app验证新添加的helloservice是否正常
AndroidQ 从app到驱动 第五章 编写JNI层完成HelloService与Hal层的对接
AndroidQ 从app到驱动 第六章 从app到驱动的所有的代码整理
这篇文章从三个方面来介绍HAL层的添加过程
1:添加HAL层所需要修改以及添加的文件
2:JNI层访问HAL层的selinux权限问题解决
3:HAL层访问驱动的selinux权限问题解决
一,添加HAL层所需要修改以及添加的文件
首先看截图:截图接上一篇文章中的驱动修改,因此这里只关心红色的部分,绿色部分不关注。
从截图中可以看到,添加HAL层比较简单,修改的文件也比较少,其中com_android_server_AlarmManagerService.cpp的修改时用来验证hal层的,仅做测试使用,后续会删掉
下面展示下涉及到的各个文件的修改内容
首先我看看下hardware/libhardware/include/hardware/hello.h的内容
#ifndef ANDROID_INCLUDE_HARDWARE_HELLO_H
#define ANDROID_INCLUDE_HARDWARE_HELLO_H
#include
#include
#include
#include
#include
#include
#define HELLO_HARDWARE_MODULE_ID "hello"
typedef struct hello_module {
struct hw_module_t common;
}hello_module_t;
typedef struct hello_device {
struct hw_device_t common;
int fd;
int (*write_string)(struct hello_device* dev, const char *str);
int (*read_string)(struct hello_device* dev, char* str);
}hello_device_t;
#endif // ANDROID_INCLUDE_HARDWARE_HELLO_H
这里有比较重要的三个变量 HELLO_HARDWARE_MODULE_ID 标志当前hal模块的ID,每个hal模块都有,jni层查找hal模块的时候就是通过这个id来查找的, hello_module 以及hello_device这两个结构体是HAL层规范中所要求的。下面这个文章中有比较详细的介绍,
Android应用程序访问linux驱动第二步:实现并测试hardware层
然后我们在看下 hardware/libhardware/modules/hello/ 目录下面的文件列表,以及文件的内容
hello.c的文件内容 这个代码也是从Android应用程序访问linux驱动第二步:实现并测试hardware层复制过来的,基本上没有修改
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "Legacy HelloHAL"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "/dev/hello"
#define MODULE_NAME "Default Hello HAL"
#define MODULE_AUTHOR "The Android Open Source Project"
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_write_string(struct hello_device* dev, const char * str);
static int hello_read_string(struct hello_device* dev, char* str);
static struct hw_module_methods_t hello_module_methods = {
.open = hello_device_open,
};
hello_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = 1,
.hal_api_version = 1,
.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) {
hello_device_t *dev = malloc(sizeof(hello_device_t));
memset(dev, 0, sizeof(hello_device_t));
ALOGE("Hello: hello_device_open name = %s",name);
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (hw_module_t*)module;
dev->common.close = hello_device_close;
dev->write_string = hello_write_string;
dev->read_string = hello_read_string;
if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
ALOGE("Hello: open /dev/hello fail-- %s.", strerror(errno));free(dev);
return -EFAULT;
}
*device = &(dev->common);
ALOGE("Hello: open /dev/hello successfully.");
return 0;
}
static int hello_device_close(struct hw_device_t* device) {
struct hello_device* hello_device = (struct hello_device*)device;
if(hello_device) {
close(hello_device->fd);
free(hello_device);
}
return 0;
}
static int hello_write_string(struct hello_device* dev,const char * str) {
ALOGE("Hello:write string: %s", str);
write(dev->fd, str, sizeof(str));
return 0;
}
static int hello_read_string(struct hello_device* dev, char* str) {
ALOGE("Hello:read hello_read_string");
read(dev->fd,str, sizeof(str));
return 0;
}
Android.bp的内容 这里要注意name的内容。后面的.default是默认的
// Copyright (C) 2011 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
cc_library_shared {
name: "hello.default",
relative_install_path: "hw",
proprietary: true,
srcs: ["hello.c"],
cflags: ["-Wall", "-Werror"],
header_libs: ["libhardware_headers"],
shared_libs: [
"liblog",
"libcutils",
"libutils",
],
}
build/make/target/product/full_base.mk的修改 这个文件的修改主要是要将我们添加的HAL层给编译进系统,要不然发现我们自己添加的HAL层无法编译进系统。
走到这里HAL层所需要的代码就已经添加完毕了,后面我这边在com_android_server_AlarmManagerService.cpp添加了一部分内容,用于新添加的HAL层到驱动的调试,确保JNI层能够正在访问到HAL层,以及新添加的HAL层能够正常访问上一篇文章中添加的驱动。
com_android_server_AlarmManagerService.cpp 的具体修改如下:
核心代码如下: 这里的修改比较简单,就是在Alarm的JNI层初始化的时候,通过hw_get_module查询我们新添加的HAL层,验证添加的HAL模块能够正常找到,然后通过调用新添加的Hal层的open函数,验证JNI层到HAL的调用是否正常,以及HAL层能否正常打开驱动文件。
struct hello_device* hello_device = NULL;
static inline int hello_device_open(const hw_module_t* module, struct hello_device** device) {
return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
}
static jint HelloServiceInit() {
ALOGE("HelloServiceInit HelloServiceInit");
const hw_module_t *hw_module = NULL;
ALOGE("Hello JNI: initializing......");
if(hw_get_module(HELLO_HARDWARE_MODULE_ID, &hw_module) == 0) {
ALOGE("Hello JNI: hello Stub found.");
if(hello_device_open(hw_module, &hello_device) == 0) {
ALOGE("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 hw_module.");
return -1;
}
然后就可以全编验证了。然后就是漫长的等待了。
二,JNI层访问HAL层的selinux权限问题解决
首先在编译完毕的out目录下使用 find 命令查找与hello相关的编译结果。下图红框中的结果显示,我们添加的HAL层已经正常编译进了系统了。
烧录验证了。烧录完毕,adb shell 进入手机后台,确认 hello.default.so已经正常生成,下面就是看log中的打印,查看hal层的加载情况了。
然后一切总是没有自己想的那么顺利,编译完成,分析log发现了如下问题:
18:58:27.780796 761 761 E AlarmManagerService: HelloServiceInit HelloServiceInit
18:58:27.780872 761 761 E AlarmManagerService: Hello JNI: initializing......
18:58:27.781541 761 761 E libc : Access denied finding property "ro.hardware.hello"
18:58:27.782138 761 761 E AlarmManagerService: Hello JNI: failed to get hello stub hw_module.
18:58:27.782462 761 761 D AlarmManagerService: Kernel timezone updated to -480 minutes west of GMT
18:58:27.787587 416 426 D ccci_mdinit: (1):save_timezone++
18:58:27.788485 409 466 W HWComposer: Ignoring duplicate VSYNC event from HWC (t=0)
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的权限
这里参考文章Android P property属性权限添加通过这篇文章有个大概的了解,然后通过在/system/sepolicy/ 目录下面通过grep -rn "ro.hardware" ./ 查找其他相关属性的设置,然后就知道了具体要如何修改了,
这里我们需要修改下面两个文件,具体修改如下,如果仅仅修改一个的话,编译不过,会提示这两个文件不一样而报错
/system/sepolicy//prebuilts/api/28.0/public/property_contexts
/system/sepolicy//public/property_contexts
这里就是照猫画虎弄出来的,然后再次编译验证。
验证log显示还是无法访问。但是log已经发生了变化,说明前面的修改有效。
19:26:07.814621 833 833 E AlarmManagerService: HelloServiceInit HelloServiceInit
19:26:07.814755 833 833 E AlarmManagerService: Hello JNI: initializing......
19:26:07.815466 833 833 E AlarmManagerService: Hello JNI: failed to get hello stub hw_module.
19:26:07.815834 833 833 D AlarmManagerService: Kernel timezone updated to -480 minutes west of GMT
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
报错log入上面,很明显还是selinux的问题。
这里我们还需要修改 /system/sepolicy/vendor/file_contexts
需要在这个文件中添加一行 这一行就是给我们新添加的HAL的so指定类型,使得JNI层能够正常访问。
/(vendor|system/vendor)/lib(64)?/hw/hello\.default\.so u:object_r:same_process_hal_file:s0
这个问题可以参考下面的文章了解
https://source.android.google.cn/devices/architecture/vndk/dir-rules-sepolicy
然后再次编译验证:
19:50:18.407752 772 772 E AlarmManagerService: HelloServiceInit HelloServiceInit
19:50:18.407925 772 772 E AlarmManagerService: Hello JNI: initializing......
19:50:18.411655 772 772 E AlarmManagerService: Hello JNI: hello Stub found.
19:50:18.411738 475 954 I OMXClient: IOmx service obtained
19:50:18.411762 772 772 E Legacy HelloHAL: Hello: hello_device_open name = hello
19:50:18.411958 772 772 E Legacy HelloHAL: Hello: open /dev/hello fail-- Permission denied.
19:50:18.411998 772 772 E AlarmManagerService: Hello JNI: failed to open hello device.
19:50:18.412325 772 772 D AlarmManagerService: Kernel timezone updated to -480 minutes west of GMT
这一次结果很欣慰,JNI层能够正常访问我们添加的HAL层了,HAL层的log也打印出来了,但是结果还是有问题。
log显示,HAL层打开驱动文件的时候,权限不允许。
Legacy HelloHAL: Hello: open /dev/hello fail-- Permission denied.
下面我们进入第三步,解决HAL层无法访问驱动的问题。
三,HAL层访问驱动的selinux权限问题解决
这里需要两步,
1)配置linux系统本身的文件系统权限,
因为默认情况下/dev/hello的权限是不允许其他组访问的,因此我们需要先在
system/core/rootdir/ueventd.rc文件中修改 、/dev/hello 的权限。
在这个文件中添加了一行 /dev/hello 0666 root root 来配置/dev/hello 的权限
diff --git a/alps_release_p0_mp1_default/system/core/rootdir/ueventd.rc b/alps_release_p0_mp1_default/system/core/rootdir/ueventd.rc
old mode 100644
new mode 100755
index b03d83b..682425e
--- a/alps_release_p0_mp1_default/system/core/rootdir/ueventd.rc
+++ b/alps_release_p0_mp1_default/system/core/rootdir/ueventd.rc
@@ -56,6 +56,8 @@ subsystem sound
/dev/pmsg0 0222 root log
+/dev/hello 0666 root root
+
# the msm hw3d client device node is world writable/readable.
/dev/msm_hw3dc 0666 root root
修改完ueventd.rc之后基本编译验证,发现如下错误:这里与前面不一样的是多了一个avc的报错,很显然,是selinu的问题。
20:25:57.899830 663 663 E AlarmManagerService: HelloServiceInit HelloServiceInit
20:25:57.899932 663 663 E AlarmManagerService: Hello JNI: initializing......
20:25:57.904053 663 663 E AlarmManagerService: Hello JNI: hello Stub found.
20:25:57.904158 663 663 E Legacy HelloHAL: Hello: hello_device_open name = hello
20:25:57.904341 663 663 E Legacy HelloHAL: Hello: open /dev/hello fail-- Permission denied.
20:25:57.904384 663 663 E AlarmManagerService: Hello JNI: failed to open hello device.
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
2:)配置selinux权限。
这个驱动的selinux权限的配置,我也是参考dev目录下面其他驱动的配置方式来配置的,具体做法就是在/system/sepolicy/ 目录下面通过grep -rn "/dev" ./ 或者看手机的dev目录下有哪些比较特殊的驱动文件,然后在/system/sepolicy/目录下使用grep 命令查看这个驱动的配置,然后照着写就可以了。
selinux的权限配置,修改的文件比较多,
system/sepolicy/prebuilts/api/26.0/private/system_server.te
system/sepolicy/prebuilts/api/26.0/public/device.te
system/sepolicy/prebuilts/api/27.0/private/file_contexts
system/sepolicy/prebuilts/api/27.0/private/system_server.te
system/sepolicy/prebuilts/api/27.0/public/device.te
system/sepolicy/prebuilts/api/28.0/private/file_contexts
system/sepolicy/prebuilts/api/28.0/private/system_server.te
system/sepolicy/prebuilts/api/28.0/public/device.te
system/sepolicy/private/file_contexts
system/sepolicy/private/system_server.te
system/sepolicy/public/device.te
但是这些修改都是一样的,只需要修改 file_contexts system_server.te device.te,文件,然后将修改同步到system/sepolicy/prebuilts/api/目录下对应的文件即可。
文件的具体修改如下:
然后编译验证。
21:05:34.170118 655 655 E AlarmManagerService: HelloServiceInit HelloServiceInit
21:05:34.170242 655 655 E AlarmManagerService: Hello JNI: initializing......
21:05:34.173964 655 655 E AlarmManagerService: Hello JNI: hello Stub found.
21:05:34.174069 655 655 E Legacy HelloHAL: Hello: hello_device_open name = hello
21:05:34.174815 655 655 E Legacy HelloHAL: Hello: open /dev/hello successfully.
21:05:34.174899 655 655 E AlarmManagerService: Hello JNI: hello device is open.
结果非常满意,提示驱动打开成功。到此HAL层添加成功。后续就需要添加systemService进行framework层的调试了,敬请期待。