程序員自我修養(yǎng)之HAL層與應(yīng)用框架通信之HIDL

一、HIDL

1、什么是HIDL

HIDL(HAL Interface Definition Language)是用于指定HAL和其用戶之間的接口的一種接口描述語(yǔ)言(IDL),AIDL是架構(gòu)在Android binder之上,用來(lái)定義Android基于Binder通信的Client與Service之間的接口;而HIDL定義的則是Android Framework與Android HAL實(shí)現(xiàn)之間的接口。

2、HIDL底層實(shí)現(xiàn)原理

使用 HDL 創(chuàng)建的 HAL 稱為綁定式 HAL,使用 Binder 進(jìn)程間通信 (IPC) 調(diào)用與其他架構(gòu)層進(jìn)行通信

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

這個(gè)是我們最熟悉的Binder,App開(kāi)發(fā)中,ActivityManagerService用的都是這個(gè),Java繼承Binder,C++中繼承Bbbinder,然后通過(guò)servicemanager進(jìn)程注冊(cè)實(shí)名Binder,然后通過(guò)已經(jīng)創(chuàng)建好的Binder接口傳遞匿名Binder對(duì)象,拿到BinderProxy或者BpBinder以后,就可以Binder通信了。

  • 3.2 dev/vndbinder

其實(shí)dev/vndbinde和dev/binder使用方式基本一樣而且是共用一套Binder SDK,也是Java繼承Binder,C++中繼承Bbbinder,但是通過(guò)vndservicemanager進(jìn)程注冊(cè)實(shí)名Binder,然后通過(guò)已經(jīng)創(chuàng)建好的Binder接口傳遞匿名Binder對(duì)象,拿到BinderProxy或者BpBinder以后,就可以Binder通信了。如何在使用同一套Binder SDK的代碼,最后訪問(wèn)的設(shè)備節(jié)點(diǎn)變成dev/vndbinder,servicemanager變成vndservicemanager。

dev/binder和dev/vndbinder無(wú)法在一個(gè)進(jìn)程中同時(shí)使用,原因兩類進(jìn)程的任意一個(gè)進(jìn)程無(wú)法同時(shí)使用dev/binder和dev/vndbinder,這一點(diǎn)不單是android官方約定,也是目前android binder sdk的限制,因?yàn)閮烧叨际枪灿肂inder SDK,所以只能指定一個(gè)設(shè)備節(jié)點(diǎn),要么dev/binder,要么dev/vndbinder
  • 3.3 、dev/hwbinder

解決無(wú)法可以在一個(gè)進(jìn)程同時(shí)使用,強(qiáng)制由所有供應(yīng)商按照android官方定義的hal接口來(lái)實(shí)現(xiàn),由HIDL方案去實(shí)現(xiàn)

4、舉例說(shuō)明三種binder

假如手機(jī)中有如下3類進(jìn)程

4.1、應(yīng)用進(jìn)程:

Camera APP
手電筒 APP

4.2、框架進(jìn)程:

System Server進(jìn)程

4.3、供應(yīng)商進(jìn)程:

Camera HAL進(jìn)程
Light HAL進(jìn)程

這些進(jìn)程之間需要使用Binder機(jī)制跨進(jìn)程通信,Android提供了三個(gè)Binder設(shè)備節(jié)點(diǎn)dev/binder,dev/hwbinder,dev/vndbinder,也就是Android系統(tǒng)同時(shí)提供了三個(gè)獨(dú)立運(yùn)行的Binder通信模塊,Android團(tuán)隊(duì)規(guī)定每類進(jìn)程使用的這三個(gè)Binder通信模塊的規(guī)則如下圖:


image.png

5、aidl使用和hidl使用范疇

5.1、aidl使用范疇

  • 適用于應(yīng)用層與應(yīng)用層之間調(diào)用
  • 適用于應(yīng)用層與java framework之間調(diào)用
  • 適用于應(yīng)用層與native framework(僅限于system/bin)之間調(diào)用
    .......

5.2、hidl使用范疇

  • 適用于native framework之間調(diào)用
  • 適用于應(yīng)用層與native framework(僅限于vendor/bin)之間調(diào)用
    ......

注意:主要是強(qiáng)調(diào)應(yīng)用層與vendor/bin和system/bin之間區(qū)別,這個(gè)坑對(duì)于我這小白掉進(jìn)去還砸了一個(gè)大坑

二、HAL服務(wù)創(chuàng)建與HIDL的調(diào)用

1、hal層服務(wù)啟動(dòng)

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

本次案例是 :app調(diào)用/vendor/bin/hw 的service

1.1、service的編寫

cc_binary {
name: "vendor.melrose.hardware.led@1.0-service", //啟動(dòng)service的名字
proprietary: true,  (安裝在/vendor/bin)
relative_install_path: "hw",   //最終安裝在放在/vendor/bin/hw目錄下
init_rc: ["vendor.melrose.hardware.led@1.0-service.rc"],  //啟動(dòng)文件配置
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、配置啟動(dòng)service文件.rc

手機(jī)開(kāi)機(jī)會(huì)通過(guò)init.cpp調(diào)用LoadBootScripts方法去啟動(dòng)對(duì)應(yīng)的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目錄下即可查看你寫的啟動(dòng)文件
  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 (啟動(dòng)時(shí)候會(huì)把hal所有相關(guān)拉起)
user system
group system

1.3、配置selinux權(quán)限

注意:selinux每個(gè)放的位置不同,但原理是相通的
  • 在system/sepolicy/vendor下創(chuàng)建hal_led_default.te
 //定義一個(gè)hal_led_default 進(jìn)程
type hal_led_default, domain; 
//hal_led_default_exec 是可執(zhí)行文件、vendor文件、文件三種類型
type hal_led_default_exec, exec_type, vendor_file_type, file_type; 
//初始化hal_led_default 進(jìn)程
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
這可確保為該可執(zhí)行文件添加適當(dāng)?shù)臉?biāo)簽,以便 SELinux在適當(dāng)?shù)挠蛑羞\(yùn)行相應(yīng)服務(wù)。在添加完這些文件重新編譯后,在開(kāi)機(jī)時(shí)就可以啟動(dòng)該服務(wù),但是不會(huì)注冊(cè)成功,還需要配置hal。

1.4、配置hal服務(wù)

  • 在system/sepolicy/public/attributes文件中,需要定義HAL的名稱:
hal_attribute(led);
  • 定義詳細(xì)規(guī)則在system/sepolicy/public目錄下創(chuàng)建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
//這個(gè)是aidl文件包名+類名,這里不能寫錯(cuò)
vendor.melrose.hardware.led : 包名
ILed : 類名

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

1.5、添加seliunx權(quán)限

  • 在system/sepolicy/public/service.te末尾添加
//給service_context中的led_service標(biāo)簽定義類型
type led_service, system_api_service, system_server_service, service_manager_type;
  • 在system/sepolicy/private/system_server.te添加權(quán)限
hal_client_domain(system_server, hal_led)

2、aidl文件生成

2.1、bp文件編寫

//注意:native 側(cè)調(diào)用一定要使用ndk生成的庫(kù),千萬(wàn)不能用cpp生成的庫(kù)。
//原因是兩個(gè)是不同的binder,無(wú)法通信
aidl_interface {
    name: "vendorabcd.hardware.sensorscalibrate",
    vendor_available: true, //這里設(shè)置為true,因?yàn)槭莢endor目錄下的模塊
    owner: "vendorabcd", //作為vendor模塊,需要設(shè)定owner才可以通過(guò)編譯
    srcs: ["vendorabcd/hardware/sensorscalibrate/*.aidl"],
    stability: "vintf", //標(biāo)示此接口為Stable AIDL,必需
    backend: {
        cpp: {
            enabled: false, //產(chǎn)生一個(gè)cpp backend,.cpp文件(unstable版)
        },
        java: {
            sdk_version: "module_current", //產(chǎn)生一個(gè)java backend,
                           //默認(rèn)enabled,用來(lái)在framework層使用此接口
        },
        ndk: {
            enabled: true, //產(chǎn)生一個(gè)ndk backend,.cpp文件(stable版)
                           //要使用aidl通信,需要ndk后端
                           //不允許使用vndk,因?yàn)樗皇莝table的
        },
    },
}

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>
        //生成的版本號(hào)
        <version>1.0</version>
        <interface>
            //aidl類名
            <name>ISensorsCalibrate</name>
            //默認(rèn)這樣寫
            <instance>default</instance>
        </interface>
   </hal>

作用: java client可以調(diào)用到

2.3、 配置使AIDL編譯

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

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

三、案例調(diào)用

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名字,這個(gè)一定要與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注冊(cè)字符串);

四、流程梳理

每個(gè)公司流程不同,但是大致流程相似,我在這里整體梳理流程

1、定義一個(gè)hal服務(wù),通過(guò)init.cpp加載來(lái)啟動(dòng)

      - 定義rc文件,在bp中編譯

2、配置hal服務(wù)的selinux相關(guān)

      - 訪問(wèn)權(quán)限
      - 添加標(biāo)簽為標(biāo)簽定義類型
      - 配置hal

3、編譯aidl文件

      - 在framework_compatibility_matrix配置客戶端
      - 配置service.xml文件,在bp中編譯

4、調(diào)用方式

file_context和service_context的作用
關(guān)聯(lián)了android系統(tǒng)的服務(wù)與type, 文件的內(nèi)容由service manager在運(yùn)行時(shí)通過(guò)selinux的接口動(dòng)態(tài)檢查權(quán)限

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容