android 自定义驱动(第三篇:HIDL服务端)

简介

在android 8.0之前,HAL是一个个的.so库,通过dlpen来打开,库和framework位于同一个进程中;
android系统进入8.0时代之后,framework和hal运行于不同的进程,所有的HAL采用HIDL技术来完成。运行Android8.0的设备必须支持绑定式和直通式HAL:

当前的类型为:
Java -> Jni -> Binder 客户端 ====== Binder 通信 ======> Binder 服务端 -> Hal -> Kernel

这一篇文章则是HIDL中的Binder服务端;
HIDL制作主要流程如下:
1.定义接口文件;
2.使用工具,根据接口文件生成代码;
3.完善接口函数
4.编译

一、HIDL接口文件定义

进入hardware/interfaces/目录下建立新的接口文件.
首先建立对应的文件夹:

mkdir -p hardware/interfaces/hello/1.0/defaul

并在hardware/interfaces/hello/1.0/defaul下创建接口描述文件IHello.hal:

package android.hardware.hello@1.0;
interface IHello{
    open();
    close();
    read() generates (int32_t val);
    write(int32_t val);
};

hidl-gen工具
Google提供了一些工具来帮助制作HIDL的框架:
在aosp根目录下执行

make hidl-gen

源码中编译生成hidl-gen.注意:编译前需要执行全编译的环境变量加载
使用hidl-gen工具生成代码

$ PACKAGE=android.hardware.hello@1.0
$ LOC=hardware/interfaces/hello/1.0/default/
$ hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
$ hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE

执行完后,hardware/interfaces/hello/1.0/default/会生成三个文件Android.dpHello.cppHello.h

)Z{KLQZ64)GQHWZ_1R(WRKK.png

接着使用脚本来更新Makefile:

./hardware/interfaces/update-makefiles.sh

执行完后,hardware/interfaces/hello/1.0会生成Android.bpAndroid.mk

上面生成的Hello.cppHello.h是实现接口的关键文件;

二、 硬件访问实现

Hello.h修改为直通式
hardware/interfaces/hello/1.0/default/Hello.h头文件

#ifndef ANDROID_HARDWARE_HELLO_V1_0_HELLO_H
#define ANDROID_HARDWARE_HELLO_V1_0_HELLO_H

#include <android/hardware/hello/1.0/IHello.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>

namespace android {
namespace hardware {
namespace hello {
namespace V1_0 {
namespace implementation {

using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;

struct Hello : public IHello {
    // Methods from IHello follow.
    Return<void> open() override;
    Return<void> close() override;
    Return<int32_t> read() override;
    Return<void> write(int32_t val) override;

    // Methods from ::android::hidl::base::V1_0::IBase follow.

};

// FIXME: most likely delete, this is only for passthrough implementations
 extern "C" IHello* HIDL_FETCH_IHello(const char* name);

}  // namespace implementation
}  // namespace V1_0
}  // namespace hello
}  // namespace hardware
}  // namespace android

#endif  // ANDROID_HARDWARE_HELLO_V1_0_HELLO_H

去掉注释.采用直通模式.

//  extern "C" IHello* HIDL_FETCH_IHello(const char* name);

Hello.cpp调用硬件抽象层
hardware/interfaces/hello/1.0/default/Hello.cpp源文件

#define LOG_TAG "HelloService HIDL"
#include "Hello.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/hello.h>
#include <stdio.h>


namespace android {
namespace hardware {
namespace hello {
namespace V1_0 {
namespace implementation {

/* 通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/
static inline int hello_device_open(const struct hw_module_t* module, struct hello_device_t** device) {
    return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
}

/*在硬件抽象层中定义的硬件访问结构体,参考<hardware/hello.h> */
struct hello_device_t* hello_device = NULL;

// Methods from IHello follow.
Return<void> Hello::open() {
    hello_module_t* module;
    if(hello_device) {
        ALOGI("Hello JNI: hello_init, device is open, no need to init.");
        return Void();
    }
        
    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 Void();
        }
        ALOGE("Hello JNI: failed to open hello device.");
        return Void();
    }
    ALOGE("Hello JNI: failed to get hello stub module.");
    return Void();
}

Return<void> Hello::close() {
    // TODO implement
    return Void();
}

Return<int32_t> Hello::read() {
     int32_t val = 0;
    if(!hello_device) {
         ALOGI("Hello JNI: device is not open.");
         return val;
    }
    ALOGI("Hello JNI:get value from device start.");
    hello_device->get_val(hello_device, &val);
    ALOGI("Hello JNI:get value %d from device.", val);
    return val;
}

Return<void> Hello::write(int32_t value) {
    int32_t val = value;
        ALOGI("Hello JNI: set value %d to device.", val);
        if(!hello_device) {
            ALOGI("Hello JNI: device is not open.");
            return Void();
        }
    hello_device->set_val(hello_device, val);

    return Void();
}


// Methods from ::android::hidl::base::V1_0::IBase follow.

IHello* HIDL_FETCH_IHello(const char* /* name */) {
    return new Hello();
}

}  // namespace implementation
}  // namespace V1_0
}  // namespace hello
}  // namespace hardware
}  // namespace android

这里引用了硬件抽象层的include <hardware/hardware.h>include <hardware/hello.h>,需要在hardware/interfaces/hello/1.0/default/Android.dp引入共享库libhardwareandroid.hardware.hello@1.0这个是上一篇文章定义的;
编译

$ mmm hardware/interfaces/hello/1.0/default/

以上已经实现了访问硬件抽象层的访问,下面我们需要创建HIDL service服务端,提供给上层访问;

三、 构建HIDL service服务

虽然上一步已经能访问硬件抽象层,有了库,我们还需要构建HDIL服务端供上层使用;

  • 1.创建服务入口hardware/interfaces/hello/1.0/default/service.cpp文件,代码如下:
#define LOG_TAG "android.hardware.hello@1.0-service"
#include <android/hardware/hello/1.0/IHello.h>
#include <hidl/LegacySupport.h>

using android::hardware::hello::V1_0::IHello;
using android::hardware::defaultPassthroughServiceImplementation;
int main() {
    return defaultPassthroughServiceImplementation<IHello>();
}
  • 2.修改编译脚本,添加对服务的编译
    hardware/interfaces/hello/1.0/default/Android.bp
    这里主要目的将当前hardware/interfaces/hello/1.0/default项目定义为cc_binary可直接可执行的项目,因为service.cpp是包含main主函数入口的;
cc_library_shared {
    name: "android.hardware.hello@1.0-impl",
    relative_install_path: "hw",
    proprietary: true,
    srcs: [
        "Hello.cpp",
    ],
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libutils",
         "liblog",
            "libhardware",
        "android.hardware.hello@1.0",
    ],
}

cc_binary {
    name: "android.hardware.hello@1.0-service",
    defaults: ["hidl_defaults"],
    relative_install_path: "hw",
    proprietary: true,
    srcs: ["service.cpp"],
    init_rc: ["android.hardware.hello@1.0-service.rc"],
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "liblog",
    "libhardware",
        "android.hardware.hello@1.0",
        "android.hardware.hello@1.0-impl",
    ],
}

编译成功可执行文件android.hardware.hello@1.0-service放在/vendor/bin/hw/目录中;

  • 3.创建启动rc脚本
    创建rc文件!(rc文件是被init进程访问的)
    hardware/interfaces/hello/1.0/default/android.hardware.hello@1.0-service.rc
service hello_service /vendor/bin/hw/android.hardware.hello@1.0-service
    class hal
    user system
    group system

注意:这里定义了hello_service服务进程所属用户和用户组都是system,而且启动执行文件位于/vendor/bin/hw/android.hardware.hello@1.0-service
最后HIDL service服务会随着开机一起启动
最后目录结构如下:

image.png

、 构建HIDL

  • 4.提供外部访问配置修改
    为了让服务器被客户端访问到,还需要在device/huawei/angler/manifest.xml(不同厂商路径不同)添加如下:
    <hal format="hidl">
        <name>android.hardware.hello</name>
        <transport>hwbinder</transport>
        <impl level="generic"></impl>
        <version>1.0<version>
        <interface>
            <name>IHello</name>
            <instance>default</instance>
        </interface>
    </hal>

四、编译

$ mmm hardware/interfaces/hello/1.0/default/

最后会生成以下文件:

/vendor/lib64/hw/android.hardware.hello@1.0-impl.so
/vendor/etc/init/android.hardware.hello@1.0-service.rc 
/vendor/bin/hw/android.hardware.hello@1.0-service
/system/lib64/android.hardware.hello@1.0.so  

注意:在编译system.img和vendor.img镜像的过程中,如何以上文件没有合入,可以使用adb push的方式验证

问题点:

1.system_servre进程访问HDIL 服务进程 的selinux权限问题

SELinux: avc:  denied  { find } for interface=android.hardware.hello::IHello pid=865 scontext=u:r:system_server:s0 tcontext=u:object_r:default_android_hwservice:s0 tclass=hwservice_manager permissive=0

我们上层framework的system_server进程没有权限访问default_android_hwservice服务,可以看这篇文章解决
第一步:打开device/huawei/angler/sepolicy/system_server.te文件,添加权限

allow system_server default_android_hwservice:hwservice_manager{ find add read}

第二步:打开system/sepolicy/public/domain.te文件,找到

注释掉这一行
#neverallow * default_android_hwservice:hwservice_manager{add find}

或者可以修改为

neverallow { domain –system_server} default_android_hwservice:hwservice_manager {add find};

参考:https://www.freesion.com/article/945159695/

遗留问题

服务端main没有启动执行

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352