天天看点

AOSP添加新硬件设备开发-HIDL层

目录

一 ,准备

二,添加hal文件

三,实现hal文件

四,添加service

五,启动rc文件

六,生成Android.bp文件

七,SELinux权限

八,编译

android设备添加一个外围设备,不像嵌入式那么简单,系统越复杂加入的方式越杂,但是万物都有规矩可寻,有方法可走。这篇文章就说说HAL和用户之间的接口HIDL。

Google:https://source.android.google.cn/devices/architecture/hidl

google 官方这样解释 :HAL 接口定义语言(简称 HIDL,发音为“hide-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。

HIDL 允许指定类型和方法调用(会汇集到接口和软件包中)。从更广泛的意义上来说,HIDL 是用于在可以独立编译的代码库之间进行通信的系统。

HIDL 旨在用于进程间通信 (IPC)。进程之间的通信采用 Binder 机制。对于必须与进程相关联的代码库,还可以使用直通模式(在 Java 中不受支持)。

HIDL 可指定数据结构和方法签名,这些内容会整理归类到接口(与类相似)中,而接口会汇集到软件包中。尽管 HIDL 具有一系列不同的关键字,但 C++ 和 Java 程序员对 HIDL 的语法并不陌生。此外,HIDL 还使用 Java 样式的注释。

在Android8.0开始,google引入了Treble机制,为了方便Android系统的快速移植、升级、稳定,Android引入了HAL Binder机制,把framework和HAL进行隔离,减少了之间的耦合性,使得framework可以被覆盖升级,不用对HAL编译升级。

用图片展示一下两者的差别:

AOSP添加新硬件设备开发-HIDL层

Android8.0之前,HAL的调用方式为Legacy HAL, HAL都是编译成so,然后动态链接到各个frameworks service中去,到了8.0,google为了给厂商一些修改时间,保持了Android的向前兼容性,另外设计了一套直通式(Passthrough)的HAL,保留动态库的HAL调用方式,

binderservice链接动态库Hal的实现。

8.0之后,google规定厂商新增的HAL都需要切换到绑定式HAL(Binderized)。

在从 

libhardware

 HAL 转换为 HIDL HAL 的过程中,HAL 实现成为服务器,而调用 HAL 的进程则成为客户端。默认实现可提供直通和 Binder 化 HAL,并可能会随着时间而发生变化:

AOSP添加新硬件设备开发-HIDL层

                                                 Hal的发展历程

Legacy Hal:Android 8.0 之前版本的 HAL 都是编译成 so,然后动态链接到各个 frameworks service 中去。

Passthrough Hal:该模式是为了兼容旧版的 HAL,旧版 HAL 实现仍以动态库的方式提供,只是 binder service 链接了动态库 HAL 实现,即 binder service 通过 hw_get_module 链接了旧版的 hal 实现,而用户端通过与 binder service IPC 通信,间接实现了与旧版 HAL 的交互。

Binderized HAL:HAL 与 用户调用在不同的进程中,HAL 被写成 binder service,而用户接口如 frameworks 作为 binder client 通过 IPC 机制实现跨进程接口调用。

概念介绍到这里,了解更清楚的话,可以去google官网阅读相关内容。

实现一个回调案例

代码结构

├── bearpi

│   └── 1.0

│       ├── Android.bp

│       ├── current.txt

│       ├── default

│       │   ├── Android.bp

│       │   ├── [email protected]

│       │   ├── BearPi.cpp

│       │   ├── BearPi.h

│       │   └── service.cpp

│       ├── IBearPiClientCallback.hal

│       ├── IBearPi.hal

│       ├── test

│       │   ├── Android.bp

│       │   └── BearPiTest.cpp

│       └── types.hal

一 ,准备

我们需要用的hidl-gen 工具去生成文件,这个工具可以通过编译得到

source build/envsetup.sh

lunch xxx

make

先整编一下需要添加的aosp。

二,添加hal文件

在hardware/interfaces目录下新建bearpi,我们从1.0版本开始,default目录为实现hal的存放路径

mkdir -p bearpi/1.0/default

很多现有的HAL实现会与硬件异步通信,需要以异步的方式通知客户端发生的事件,比如,指令下发后返回执行状态。HIDL接口可以用作异步回调。

在bearpi/1.0路径下创建两个hal文件,

IBearPi.hal

package [email protected];   ----------------------------------------------------------------------------------------------->软件包名

import IBearPiClientCallback; --------------------------------------------------------------------------------------------------------------->

import

 语句是用于访问其他软件包中的软件包接口和类型的 HIDL 机制

interface IBearPi{

    setNotify(IBearPiClientCallback callback)  generates (uint64_t bearpiId); --------------------------------------------------->设置回调

    test() generates (uint64_t testresult);

};

IBearPiClientCallback.hal

package [email protected];

interface IBearPiClientCallback {

    oneway onResult(uint64_t deviceId, BearPiAcquiredInfo acquiredInfo, uint32_t vendorCode);------------------------>回调函数

};

types.hal --------------------------------------------------------------------------------------------------------------------------------------------->

types.hal

 文件并不定义接口,而是定义软件包中每个接口可以访问的数据类型

package [email protected];

enum BearPiAcquiredInfo : int32_t {

  ACQUIRED_GOOD = 0,

  ACQUIRED_PARTIAL = 1,

  ACQUIRED_INSUFFICIENT = 2,

  ACQUIRED_IMAGER_DIRTY = 3,

  ACQUIRED_TOO_SLOW = 4,

  ACQUIRED_TOO_FAST = 5,

  ACQUIRED_VENDOR = 6

};

注意软件包前缀对应的位置,android.hardware.*  -----hardware/interfaces) {---------------------------------------------->打开就是直通模式,注释使用绑定式

    //return new BearPi();

//}

//

}  // namespace implementation

}  // namespace V1_0

}  // namespace bearpi

}  // namespace hardware

}  // namespace android

四,添加service

在default路径下创建service.cpp

#define LOG_TAG "fht"

#include <android/log.h>

#include <hidl/HidlSupport.h>

#include <hidl/HidlTransportSupport.h>

#include <android/hardware/bearpi/1.0/IBearPi.h>

#include <android/hardware/bearpi/1.0/types.h>

#include "BearPi.h"

using android::hardware::bearpi::V1_0::IBearPi;

using android::hardware::bearpi::V1_0::implementation::BearPi;

using android::hardware::configureRpcThreadpool;

using android::hardware::joinRpcThreadpool;

using android::sp;

int main(){

    ALOGD("Bearpi service start ");

    android::sp<IBearPi> bear = BearPi::getInstance();

//绑定式服务

// This function must be called before you join to ensure the proper
// number of threads are created. The threadpool will never exceed
// size one because of this call.
           

 //如果“callerWillJoin”为true,则表示调用方将加入线程池,因此内核需要少创建一个线程。

//如果“callerWillJoin”为false,我们仍然会在本地生成一个线程,我们还应该告诉内核创建的线程比这里请求的少一个。

    configureRpcThreadpool(1, true ); //  用于设置 当前进程用于hwbinder通信的最大线程数

    if (bear != nullptr) {

        if (::android::OK != bear->registerAsService()) { //注册服务

            return 1;

        }

    } else {

        ALOGE("Can't create instance of bearpi, nullptr");

    }

    ALOGD("Bearpi service registerAsService ");

// Adds this thread to the threadpool, resulting in one total
 // thread in the threadpool. We could also do other things, but
 // would have to specify 'false' to willJoin in configureRpcThreadpool
           

    joinRpcThreadpool();

    return 0; // should never get here

//直通式服务,

defaultPassthroughServiceImplementation

 将对提供的 

-impl

 库执行 

dlopen()

 操作,并将其作为 Binder 化服务提供

//return defaultPassthroughServiceImplementation<IBearPi>("BearPi");

}

五,启动rc文件

在default路径下新建 [email protected]

service vendor.bear_hal /vendor/bin/hw/[email protected]

    # "class hal" causes a race condition on some devices due to files created

    # in /data. As a workaround, postpone startup until later in boot once

    # /data is mounted.

    class hal

    user system

    group system system

六,生成Android.bp文件

文件创建成功后,需要加入编译,下面我们使用hidl-gen生成对应的Android.bp文件

生成1.0框架接口库

hidl-gen -Landroidbp -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport [email protected]

生成impl 库的 Android.bp

hidl-gen -Landroidbp-impl -o /home/ubuntu/data/LG/hardware/interfaces/bearpi/1.0/default/  -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport [email protected]

修改default下面Android.bp,生成service bin

cc_binary {

    name: "[email protected]",

    defaults: ["hidl_defaults"],

    init_rc: ["[email protected]"],

    vendor: true,

    relative_install_path: "hw",

    srcs: [

        "BearPi.cpp",

        "service.cpp",

    ],

    shared_libs: [

        "libcutils",

        "liblog",

        "libhidlbase",

        "libhardware",

        "libutils",

  1.         "[email protected]",

    ],

}

七,SELinux权限

以mtk平台为例,添加bearpi相应权限

路径:device/mediatek/sepolicy/basic/non_plat

1,新建bearpi.te

type bearpi, domain;

type bearpi_exec, exec_type, vendor_file_type, file_type;

hwbinder_use(bearpi);

init_daemon_domain(bearpi)

add_hwservice(bearpi, hal_bearpi_hwservice)

allow bearpi hwservicemanager_prop:file { read open getattr map };

2,file_contexts中添加

/(vendor|system/vendor)/bin/hw/android\.hardware\[email protected]\.0-service     u:object_r:bearpi_exec:s0

3,hwservice.te中定义hwservice manager类型

type hal_bearpi_hwservice, hwservice_manager_type;

4,hwservice_contexts中添加接口访问权限

android.hardware.bearpi::IBearPi                            u:object_r:hal_bearpi_hwservice:s0

八,编译

1,添加系统编译,使系统整编后能主动编译,在device/xx/device.mk中添加包编译

PRODUCT_PACKAGES += \

    [email protected] \

   [email protected] \

   test_bearpi \

   bearpi.default

DEVICE_MANIFEST_FILE += device/xiaomi/lancelot/manifest_bearpi.xml

2,在项目地下新建manifest_bearpi.xml接口注册文件,如果没有注册文件,客户端和服务端将无法通信,getService()将返回null。

<manifest version="1.0" type="device">

    <hal format="hidl">

        <name>android.hardware.bearpi</name>

        <transport>hwbinder</transport>

        <version>1.0</version>

        <interface>

            <name>IBearPi</name>

            <instance>default</instance>

        </interface>

    </hal>

</manifest>

3,添加启动服务,在init.project.rc内添加bearpi 的rc文件,保证开机能够启动服务

import /vendor/etc/init/[email protected]

----------------------------------------------------------我------------是-------------分-----------割------------线---------------------------------------------------------------

如上服务端写好后,我们写客户端测试下

在1.0路径下新建test目录

创建BearPiTest.cpp

//

// Created by ubuntu on 2020/12/25.

//

#define LOG_TAG "fht"

#define LOG_VERBOSE "fht"

#include <android/hardware/bearpi/1.0/IBearPi.h>

#include <hidl/Status.h>

#include <log/log.h>

#include <android/log.h>

using android::hardware::bearpi::V1_0::BearPiAcquiredInfo;

using android::hardware::bearpi::V1_0::IBearPi;

using android::hardware::bearpi::V1_0::IBearPiClientCallback;

using android::sp;

using android::hardware::hidl_string;

using android::hardware::Return; // 命名空间

using android::hardware::Void;  // Void函数

//实现回调

class BearPiClientCallback : public IBearPiClientCallback { 

public:

      BearPiClientCallback() {}

      ~BearPiClientCallback() {}

      Return<void> onResult(uint64_t deviceId, const ::android::hardware::bearpi::V1_0::BearPiAcquiredInfo acquiredInfo,uint32_t vendorCode) override {

          ALOGD("onResult deviceid =%lu, vendorCode = %d, info = %d", deviceId, vendorCode, acquiredInfo);

          return Void();

      };

};

int main() {

//获得服务

    sp <IBearPi> bear = IBearPi::getService();

    if (bear == nullptr) {

        ALOGE("Can't find IBearPi service ...");

        return -1;

    }

    ALOGD("IBearPi ON");

    sp <BearPiClientCallback> callback = new BearPiClientCallback();

//注册回调

    bear->setNotify(callback);

    bear->test();

    return 0;

}

在test目录下新建Android.bp

cc_binary {

     name: "test_bearpi",

     srcs: [

     "BearPiTest.cpp",

     ],

     shared_libs: [

          "libcutils",

         "liblog",

         "libhidlbase",

         "libhardware",

         "libutils",

         "[email protected]",

     ],

}