美文网首页Android高级开发
HAL层与应用框架通信之HIDL

HAL层与应用框架通信之HIDL

作者: 巴菲伟 | 来源:发表于2023-02-04 17:51 被阅读0次

一、HIDL

1、什么是HIDL

HIDL(HAL Interface Definition Language)是用于指定HAL和其用户之间的接口的一种接口描述语言(IDL),AIDL是架构在Android binder之上,用来定义Android基于Binder通信的Client与Service之间的接口;而HIDL定义的则是Android Framework与Android HAL实现之间的接口。

2、HIDL底层实现原理

使用 HDL 创建的 HAL 称为绑定式 HAL,使用 Binder 进程间通信 (IPC) 调用与其他架构层进行通信

3、binder的种类

参考链接:
https://source.android.google.cn/docs/core/architecture/hidl/binder-ipc?hl=zh-cn

671be4cc585db93a51ba0c811f8aaa4.png

3.1、binder种类介绍

  • 3.1 dev/binder

这个是我们最熟悉的Binder,App开发中,ActivityManagerService用的都是这个,Java继承Binder,C++中继承Bbbinder,然后通过servicemanager进程注册实名Binder,然后通过已经创建好的Binder接口传递匿名Binder对象,拿到BinderProxy或者BpBinder以后,就可以Binder通信了。

  • 3.2 dev/vndbinder

其实dev/vndbinde和dev/binder使用方式基本一样而且是共用一套Binder SDK,也是Java继承Binder,C++中继承Bbbinder,但是通过vndservicemanager进程注册实名Binder,然后通过已经创建好的Binder接口传递匿名Binder对象,拿到BinderProxy或者BpBinder以后,就可以Binder通信了。如何在使用同一套Binder SDK的代码,最后访问的设备节点变成dev/vndbinder,servicemanager变成vndservicemanager。

dev/binder和dev/vndbinder无法在一个进程中同时使用,原因两类进程的任意一个进程无法同时使用dev/binder和dev/vndbinder,这一点不单是android官方约定,也是目前android binder sdk的限制,因为两者都是共用Binder SDK,所以只能指定一个设备节点,要么dev/binder,要么dev/vndbinder
  • 3.3 、dev/hwbinder

解决无法可以在一个进程同时使用,强制由所有供应商按照android官方定义的hal接口来实现,由HIDL方案去实现

4、举例说明三种binder

假如手机中有如下3类进程

4.1、应用进程:

Camera APP
手电筒 APP

4.2、框架进程:

System Server进程

4.3、供应商进程:

Camera HAL进程
Light HAL进程

这些进程之间需要使用Binder机制跨进程通信,Android提供了三个Binder设备节点dev/binder,dev/hwbinder,dev/vndbinder,也就是Android系统同时提供了三个独立运行的Binder通信模块,Android团队规定每类进程使用的这三个Binder通信模块的规则如下图:


image.png

5、aidl使用和hidl使用范畴

5.1、aidl使用范畴

  • 适用于应用层与应用层之间调用
  • 适用于应用层与java framework之间调用
  • 适用于应用层与native framework(仅限于system/bin)之间调用
    .......

5.2、hidl使用范畴

  • 适用于native framework之间调用
  • 适用于应用层与native framework(仅限于vendor/bin)之间调用
    ......

注意:主要是强调应用层与vendor/bin和system/bin之间区别,这个坑对于我这小白掉进去还砸了一个大坑

二、HAL服务创建与HIDL的调用

1、hal层服务启动

参考链接: https://android.googlesource.com/platform/system/core/+/master/init/README.md

本次案例是 :app调用/vendor/bin/hw 的service

1.1、service的编写

cc_binary {
name: "vendor.melrose.hardware.led@1.0-service", //启动service的名字
proprietary: true,  (安装在/vendor/bin)
relative_install_path: "hw",   //最终安装在放在/vendor/bin/hw目录下
init_rc: ["vendor.melrose.hardware.led@1.0-service.rc"],  //启动文件配置
vintf_fragments: ["vendor.melrose.hardware.led.xml"],  //编译HIDL文件
srcs: [
"Led.cpp",
"service.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"libbase",
"libbinder_ndk",
"libcutils",
"liblog",
"libmwnode",
"libmwinit",
"libmwtime",
"libmwcommon",
"liblog",
"vendor.melrose.hardware.led@1.0",
],
}

1.2、配置启动service文件.rc

手机开机会通过init.cpp调用LoadBootScripts方法去启动对应的native service
static void LoadBootScripts(ActionManager&action_manager, ServiceList&service_list) {
        Parser parser = CreateParser(action_manager, service_list);
        std::string bootscript = GetProperty("ro.boot.init_rc", "");
        if (bootscript.empty()) {
            parser.ParseConfig("/system/etc/init/hw/init.rc");
            if (!parser.ParseConfig("/system/etc/init")) {
                late_import_paths.emplace_back("/system/etc/init");
            }
            parser.ParseConfig("/system_ext/etc/init");
            if (!parser.ParseConfig("/vendor/etc/init")) {
                late_import_paths.emplace_back("/vendor/etc/init");
            }
            if (!parser.ParseConfig("/odm/etc/init")) {
                late_import_paths.emplace_back("/odm/etc/init");
            }
            if (!parser.ParseConfig("/product/etc/init")) {
                late_import_paths.emplace_back("/product/etc/init");
            }
        } else {
            parser.ParseConfig(bootscript);
        }
    }
  查看方式 : 
  adb shell
  cd  system|vendor|odm|product/etc/init目录下即可查看你写的启动文件
  cd  system|vendor|odm|product/bin或者system|vendor|odm|product/bin/hw目录即可查看你写的service
service led(名字随意) /vendor/bin/hw/vendor.melrose.hardware.led@1.0-service (service的名字)
class hal (启动时候会把hal所有相关拉起)
user system
group system

1.3、配置selinux权限

注意:selinux每个放的位置不同,但原理是相通的
  • 在system/sepolicy/vendor下创建hal_led_default.te
 //定义一个hal_led_default 进程
type hal_led_default, domain; 
//hal_led_default_exec 是可执行文件、vendor文件、文件三种类型
type hal_led_default_exec, exec_type, vendor_file_type, file_type; 
//初始化hal_led_default 进程
init_daemon_domain(hal_led_default)
  • 在下system/sepolicy/vendor/file_contexts添加:
/(vendor|system/vendor)/bin/hw/vendor\.melrose\.hardware\.led@1\.0-service u:object_r:hal_led_default_exec:s0

如何查看该配置是否正确:
adb shell
cd vendor/bin
ls -AZ | grep -i "whoami"   whoami: 替换为你要查看的service的名字
a7a0b0b00e43fbc97e1ff687ff40126.png
这可确保为该可执行文件添加适当的标签,以便 SELinux在适当的域中运行相应服务。在添加完这些文件重新编译后,在开机时就可以启动该服务,但是不会注册成功,还需要配置hal。

1.4、配置hal服务

  • 在system/sepolicy/public/attributes文件中,需要定义HAL的名称:
hal_attribute(led);
  • 定义详细规则在system/sepolicy/public目录下创建hal_led.te:
# HwBinder IPC from client to server, and callbacks
binder_call(hal_led_client, hal_led_server)
binder_call(hal_led_server, hal_led_client)
add_hwservice(hal_led_server, hal_led_hwservice)
allow hal_led_client hal_led_hwservice:hwservice_manager find;
  • 在system/sepolicy/private/service_contexts文件中添加:
vendor.melrose.hardware.led.ILed u:object_r:hal_led_hwservice:s0
//这个是aidl文件包名+类名,这里不能写错
vendor.melrose.hardware.led : 包名
ILed : 类名

如何查看配置是否正确
adb shell ps -Z | grep i "com.xiaomi.marekt"
52a82b0d1d249b1f721b9a54b2f0996.png

1.5、添加seliunx权限

  • 在system/sepolicy/public/service.te末尾添加
//给service_context中的led_service标签定义类型
type led_service, system_api_service, system_server_service, service_manager_type;
  • 在system/sepolicy/private/system_server.te添加权限
hal_client_domain(system_server, hal_led)

2、aidl文件生成

2.1、bp文件编写

//注意:native 侧调用一定要使用ndk生成的库,千万不能用cpp生成的库。
//原因是两个是不同的binder,无法通信
aidl_interface {
    name: "vendorabcd.hardware.sensorscalibrate",
    vendor_available: true, //这里设置为true,因为是vendor目录下的模块
    owner: "vendorabcd", //作为vendor模块,需要设定owner才可以通过编译
    srcs: ["vendorabcd/hardware/sensorscalibrate/*.aidl"],
    stability: "vintf", //标示此接口为Stable AIDL,必需
    backend: {
        cpp: {
            enabled: false, //产生一个cpp backend,.cpp文件(unstable版)
        },
        java: {
            sdk_version: "module_current", //产生一个java backend,
                           //默认enabled,用来在framework层使用此接口
        },
        ndk: {
            enabled: true, //产生一个ndk backend,.cpp文件(stable版)
                           //要使用aidl通信,需要ndk后端
                           //不允许使用vndk,因为它不是stable的
        },
    },
}

2.2、配置 Framework Compatibility Matrix

在vendor/device/framework_compatibility_matrix.xml添加adil文件

 <hal format="hidl" optional="true">
        //vendorabcd.hardware.sensorscalibrate : adil包名
        <name>vendorabcd.hardware.sensorscalibrate</name>
        //生成的版本号
        <version>1.0</version>
        <interface>
            //aidl类名
            <name>ISensorsCalibrate</name>
            //默认这样写
            <instance>default</instance>
        </interface>
   </hal>

作用: java client可以调用到

2.3、 配置使AIDL编译

<manifest version="1.0" type="device">
    <hal format="hidl" override="true">
        //aidl的包名
        <name>vendorabcd.hardware.sensorscalibrate</name>
        //ISensorsCalibrate/default 在client中调用使用
        <fqname>ISensorsCalibrate/default</fqname>
    </hal>
</manifest>

//vendorabcd.hardware.sensorscalibrate.xml文件位置:vendor/etc/vintf/manifest/目录下可找到

三、案例调用

1、service.cpp文件

#include <string>
#include <thread>
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
 #include "Service.h"
int main() {
    ABinderProcess_startThreadPool();
    std::shared_ptr<Service> securityCa = ::ndk::SharedRefBase::make<Service>();
    const std::string instance = std::string() + Service::descriptor + "/default";
//在sp中添加service名字,这个一定要与client端相同
    binder_status_t status = AServiceManager_addService(securityCa->asBinder().get(),
        instance.c_str());
    CHECK(status == STATUS_OK);
    ALOGD("Start jsse service.");
    ABinderProcess_joinThreadPool();
    return EXIT_FAILURE;  // should never be reached
}

2、client.java

Service service = Service.Stub.asInterface(ServiceManager.checkService(service注册字符串);

四、流程梳理

每个公司流程不同,但是大致流程相似,我在这里整体梳理流程

1、定义一个hal服务,通过init.cpp加载来启动

      - 定义rc文件,在bp中编译

2、配置hal服务的selinux相关

      - 访问权限
      - 添加标签为标签定义类型
      - 配置hal

3、编译aidl文件

      - 在framework_compatibility_matrix配置客户端
      - 配置service.xml文件,在bp中编译

4、调用方式

相关文章

网友评论

    本文标题:HAL层与应用框架通信之HIDL

    本文链接:https://www.haomeiwen.com/subject/zfmohdtx.html