AndroidQ 从app到驱动 第二章 添加HAL层,先打通JNI层到驱动的访问

这篇文章接上一篇文章是第二篇,主要介绍如果给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层所需要修改以及添加的文件

首先看截图:截图接上一篇文章中的驱动修改,因此这里只关心红色的部分,绿色部分不关注。

AndroidQ 从app到驱动 第二章 添加HAL层,先打通JNI层到驱动的访问_第1张图片

 从截图中可以看到,添加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层无法编译进系统。

AndroidQ 从app到驱动 第二章 添加HAL层,先打通JNI层到驱动的访问_第2张图片

 

走到这里HAL层所需要的代码就已经添加完毕了,后面我这边在com_android_server_AlarmManagerService.cpp添加了一部分内容,用于新添加的HAL层到驱动的调试,确保JNI层能够正在访问到HAL层,以及新添加的HAL层能够正常访问上一篇文章中添加的驱动。

com_android_server_AlarmManagerService.cpp 的具体修改如下:

AndroidQ 从app到驱动 第二章 添加HAL层,先打通JNI层到驱动的访问_第3张图片

核心代码如下: 这里的修改比较简单,就是在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层已经正常编译进了系统了。

AndroidQ 从app到驱动 第二章 添加HAL层,先打通JNI层到驱动的访问_第4张图片

烧录验证了。烧录完毕,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

AndroidQ 从app到驱动 第二章 添加HAL层,先打通JNI层到驱动的访问_第5张图片

这里就是照猫画虎弄出来的,然后再次编译验证。

验证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/目录下对应的文件即可。

文件的具体修改如下:

AndroidQ 从app到驱动 第二章 添加HAL层,先打通JNI层到驱动的访问_第6张图片

AndroidQ 从app到驱动 第二章 添加HAL层,先打通JNI层到驱动的访问_第7张图片

AndroidQ 从app到驱动 第二章 添加HAL层,先打通JNI层到驱动的访问_第8张图片

然后编译验证。

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层的调试了,敬请期待。

最后在来张截图,展示下本次修改中所涉及到的所有的文件AndroidQ 从app到驱动 第二章 添加HAL层,先打通JNI层到驱动的访问_第9张图片

 

你可能感兴趣的:(Android,Linux)