hidl实现hal

一、前言

正如 Android HIDL 概述 一文中简单的对 HIDL 的演进和新架构下 Framework 与 Hal 层之间的通信做了介绍。但是笔者的目的是想完整的实现从上层 APP 到 hal 之间通信过程,由此可以更加深刻的理解这种机制。

 
 

二、Binderized Mode (绑定式)简介

从上文介绍,我们知道 绑定模式 是 google 为了向前兼容而定义的一种类型,且 Android 8.0 及后续版本的设备都必须只支持这种模式。这种模式下 Framework 与 Hal 分别位于不同的进程中,其实从具体实现来讲这种模式也更应该被称为 Binder 化的直通式。本文将通过这种方式实现一个 HIDL 服务。

三、环境/工具准备

  • Ubuntu 20.04 TLS
  • Android 源码:Android 9.0,编译烧录详见 Android源码编译烧录
  • hidl-gen 工具:Android 系统自带,需要配置一下环境变量

四、HIDL 实现

本文目的是实现一个具有 加减乘除 运算的 HIDL 服务,命名为 银河一号(GalaxyOne)。HIDL用起来非常简单,在系统源码中的 hardware/interfaces 目录下有很多的 HIDL,我们仿照其他 HIDL 来创建自己的目录:hardware/interfaces/galaxy_one/1.0

4.1 创建 IGalaxyOne.hal 文件

hardware/interfaces/galaxy_one/1.0/IGalaxyOne.hal

这里定义了四种基本的运算:加、减、乘、除,这是上层调用 HAL 的入口,内容如下:

package android.hardware.galaxy_one@1.0;

interface IGalaxyOne{

    //加法
    add(uint32_t a,uint32_t b) generates (uint32_t result);
    //减法
    sub(uint32_t a,uint32_t b) generates (uint32_t result);
    //乘法
    mul(uint32_t a,uint32_t b) generates (uint32_t result);
    //除法
    div(uint32_t a,uint32_t b) generates (uint32_t result);
    
};

4.2 hidl-gen 生成 HIDL 框架

在使用 hidl-gen 之前需要先做两件事:
1、hidl-gen 由 Android 提供,使用之前需要先配置一下系统路径,如我这里所做的:

# vim ~/.bashrc
export PATH=/home/zsk/AOSP/out/soong/host/linux-x86/bin:$PATH

2、Ubuntu 新的终端窗口必须先设定一些 Android 环境变量:

source build/envsetup.sh
lunch aosp_sailfish-userdebug  // lunch mode 根据需求修改
make hidl-gen

配置完成之后在 源码根目录 下执行如下命令:

  PACKAGE=android.hardware.galaxy_one@1.0
  LOC=hardware/interfaces/galaxy_one/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/galaxy_one/1.0 目录下多了一个 default 目录,进入之后发现有如下文件:

 

之后执行 update-makefiles.sh 脚本来为 HIDL 生成对应的 Android.bp 文件,此脚本位于 hardware/interfaces 目录下,同样可在源码根目录下执行:

./hardware/interfaces/update-makefiles.sh
 

 

接下来我们需要添加两个空文件:

touch hardware/interfaces/galaxy_one/1.0/default/android.hardware.galaxy_one@1.0-service.rc
touch hardware/interfaces/galaxy_one/1.0/default/service.cpp

完成之后,整个工程结构如下所示:

 

4.3 调用流程

上述过程已经将 HIDL 服务所需要的全本文件配置完成,虽然其中很多文件是空的,或者没有具体实现,我们现在先放在一边,先来对整体的调用流程及各个文件的功效略作说明。

 

 

Binder 化直通式
  • Application:指上层应用
  • JNI:指 framework 层,getService 获取 hal 层 service
  • android.hardware.galaxy_one@1.0.so:由 IGalaxyOne.hal 生成的接口库,由 hardware/interfaces/galaxy_one/1.0/Android.bp 通过 IGalaxyOne.hal 生成,这样只要这个接口库不变,那么 framework 的更新和 hal 层就隔绝开了
  • android.hardware.galaxy_one@1.0-service.rc:设备开机时通过 rc 文件启动此服务
  • galaxy_hal_service:service的名,可通过国 start galaxy_hal_service 启动服务
  • android.hardware.galaxy_one@1.0-impl.so:实现库,上层应用的最终调用

关于 Application、JNI 这两层内容会在稍后用两个篇幅去分析,此处暂不理会。现在我们就着这个调用过程将需要的内容补充完成。

4.3.1 接口库生成

android.hardware.galaxy_one@1.0.so,由 hardware/interfaces/galaxy_one/1.0/Android.bp 通过 IGalaxyOne.hal 生成,Android.bp 文件是在上面一些列命令执行之后生成,而接口库是当我们最终执行编译模块时生成,可以说这个过程不需要我们手动参与,Android.bp 内容如下:

// This file is autogenerated by hidl-gen -Landroidbp.

hidl_interface {
    name: "android.hardware.galaxy_one@1.0",   //此处设置接口库的名字
    root: "android.hardware",
    vndk: {
        enabled: true,
    },
    srcs: [
        "IGalaxyOne.hal",
    ],
    interfaces: [
        "android.hidl.base@1.0",
    ],
    gen_java: true,
}

4.3.2 实现库生成

android.hardware.galaxy_one@1.0-impl.so,由 hardware/interfaces/galaxy_one/1.0/default/Android.bp 通过 GalaxyOne.cpp 生成,注意这个 Android.bp 文件是位于 default 目录下,同样的在最后模块编译时生成,原始内容如下:

cc_library_shared {
    name: "android.hardware.galaxy_one@1.0-impl",
    relative_install_path: "hw",
    proprietary: true,
    srcs: [
        "GalaxyOne.cpp",
    ],
    shared_libs: [    //这里可以添加我们需要的库
        "liblog",     
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
        "libutils",
        "android.hardware.galaxy_one@1.0",
    ],
}
4.3.3 GalaxyOne.cpp 实现

4.3.2 中,实现库是由 GalaxyOne.cpp 编译而成,现在我们来将此文件补充完成:

GalaxyOne.h:
Binder化直通式,同样需要将 HIDL_FETCH_XXX 打开,至于原因我们在后面会提及

#ifndef ANDROID_HARDWARE_GALAXY_ONE_V1_0_GALAXYONE_H
#define ANDROID_HARDWARE_GALAXY_ONE_V1_0_GALAXYONE_H

#include <android/hardware/galaxy_one/1.0/IGalaxyOne.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <log/log.h>

namespace android {
namespace hardware {
namespace galaxy_one {
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 GalaxyOne : public IGalaxyOne {
    // Methods from ::android::hardware::galaxy_one::V1_0::IGalaxyOne follow.
    Return<uint32_t> add(uint32_t a, uint32_t b) override;
    Return<uint32_t> sub(uint32_t a, uint32_t b) override;
    Return<uint32_t> mul(uint32_t a, uint32_t b) override;
    Return<uint32_t> div(uint32_t a, uint32_t b) override;

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

};

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

}  // namespace implementation
}  // namespace V1_0
}  // namespace galaxy_one
}  // namespace hardware
}  // namespace android

#endif  // ANDROID_HARDWARE_GALAXY_ONE_V1_0_GALAXYONE_H

GalaxyOne.cpp:

#include "GalaxyOne.h"

namespace android {
namespace hardware {
namespace galaxy_one {
namespace V1_0 {
namespace implementation {

// Methods from ::android::hardware::galaxy_one::V1_0::IGalaxyOne follow.
Return<uint32_t> GalaxyOne::add(uint32_t a, uint32_t b) {
    uint32_t result = a + b;
    ALOGE("GalaxyOne::add  a = %d,b = %d,result = %d",a,b,result);
    return result;
}

Return<uint32_t> GalaxyOne::sub(uint32_t a, uint32_t b) {
    uint32_t result = a - b;
    ALOGE("GalaxyOne::sub  a = %d,b = %d,result = %d",a,b,result);
    return result;
}

Return<uint32_t> GalaxyOne::mul(uint32_t a, uint32_t b) {
    uint32_t result = a * b;
    ALOGE("GalaxyOne::mul  a = %d,b = %d,result = %d",a,b,result);
    return result;
}

Return<uint32_t> GalaxyOne::div(uint32_t a, uint32_t b) {
    uint32_t result = a / b;
    ALOGE("GalaxyOne::div  a = %d,b = %d,result = %d",a,b,result);
    return result;
}

// Methods from ::android::hidl::base::V1_0::IBase follow.
IGalaxyOne* HIDL_FETCH_IGalaxyOne(const char* /* name */) {
    ALOG("galaxy_one service init success....");
    return new GalaxyOne();
}

}  // namespace implementation
}  // namespace V1_0
}  // namespace galaxy_one
}  // namespace hardware
}  // namespace android

4.3.3 模块编译

现在除了需要的 rc 文件没有补充、galaxy-hal-service 服务没有生成外其余均已配置好了,现在进行编译生成对应的库。进入根目录下执行如下命令:(注意是在刚刚执行过的 source build/envsetup.shlunch 的窗口下编译,若是新窗口则需要重新执行这两条命令)

mmm  hardware/interfaces/galaxy_one/1.0

此时应该可以在 out/tartget/product/XXX/vendor/lib64/hwout/tartget/product/XXX/system/lib64/hw 目录下找到 android.hardware.galaxy_one@1.0.soandroid.hardware.galaxy_one@1.0-impl.so 两个动态库

4.3.4 serice 生成

上面过程将需要的动态库生成完毕,接下来我们需要生成对应的 service 可执行文件,这个过程一共分为三步:

1、在 /default 下的 Android.bp 文件中添加如下内容

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

2、补充 service.cpp 内容

内容很简单,defaultPassthroughServiceImplementation 帮我们自动注册服务:

#define LOG_TAG "android.hardware.galaxy_one@1.0-service"
 
#include <android/hardware/galaxy_one/1.0/IGalaxyOne.h>
#include <hidl/LegacySupport.h>
#include "GalaxyOne.h"
 
// Generated HIDL files
using android::hardware::galaxy_one::V1_0::IGalaxyOne;
using android::hardware::galaxy_one::V1_0::implementation::GalaxyOne;
 
using android::hardware::defaultPassthroughServiceImplementation;
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
 
int main() {
    return defaultPassthroughServiceImplementation<IGalaxyOne>();
} 

3、补充 rc 文件

注意这里的 galaxy-hal-service 相当于这个服务的别名,系统就是根据这个文件在启动的同时也将这个 service 启动,因此在下面我们手动启动测试时没有什么作用,不过这里先补充完整。

service galaxy-hal-service /vendor/bin/hw/android.hardware.galaxy_one@1.0-service
    class hal
    user system
    group system

同样执行 mmm hardware/interfaces/galaxy_one/1.0 命令,完成之后就会得到如下二进制可执行文件:

out/target/product/sailfish/vendor/bin/hw/android.hardware.galaxy_one@1.0-service

4.4 client 端

经过一系列过程之后,我们得到了三个产物
1、android.hardware.galaxy_one@1.0.so
2、android.hardware.galaxy_one@1.0-impl.so
3、android.hardware.galaxy_one@1.0-service

现在需要模拟一个客户端来测试调用,因此在 default 目录下新建 test 目录,并新建 client.cpp、Android.bp 文件,具体结构如下:

 

 

client.cpp 内容如下:

#include <android/hardware/galaxy_one/1.0/IGalaxyOne.h>
#include <hidl/Status.h>
#include <log/log.h>

using android::sp;
using android::hardware::galaxy_one::V1_0::IGalaxyOne;
using android::hardware::Return;

int main(){
    android::sp<IGalaxyOne> service = IGalaxyOne::getService();
    if (service == nullptr) {
        ALOGD("faile to get galaxy_one service......");
        return -1;
    }
    ALOGE("success to get galaxy_one service.....");

    uint32_t addResult = service->add(3,4);
    ALOGE("galaxy_one service add: result = %d",(int)addResult);

    uint32_t subResult = service->sub(8,3);
    ALOGE("galaxy_one service sub: result = %d",(int)subResult);

    uint32_t mulResult = service->mul(3,8);
    ALOGE("galaxy_one service mul: result = %d",(int)mulResult);

    uint32_t divResult = service->div(8,2);
    ALOGE("galaxy_one service div: result = %d",(int)divResult);

    return 0;
}

Android.bp 内容如下:

cc_binary {
    name: "galaxy_test",    //表示生成的 client 名称
    srcs: [
        "client.cpp"
    ],
    shared_libs: [
        "liblog",
        "android.hardware.galaxy_one@1.0",
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
        "libutils",
    ],
}

mmm hardware/interfaces/galaxy_one/1.0 命令编译之后,可以在 out/target/product/XXX/system/bin 目录下找到 galaxy_test

五、验证服务

5.1 push 设备

现在我们一共得到 4 个产物,使用 adb 命令将其 push 到手机对应目录下:

1、android.hardware.galaxy_one@1.0-impl.so ===> /vendor/lib64/hw
2、android.hardware.galaxy_one@1.0.so ===> vendor/lib64
3、android.hardware.galaxy_one@1.0-service ===> /vendor/bin/hw
4、galaxy_test ===> /system/bin

5、rc ===》 /vendor/etc/init

6、xml ===》/vendor/etc/vintf/manifest/

5.2 修改设备 manifest.xml

HIDL 想要被 framework 获取使用还需要在 manifest.xml 中注册,此在手机 vendor/etc/vintf 下,我们将这个文件 pull 出来添加如下代码之后再 push 原来位置:

    <hal format="hidl">
        <name>android.hardware.galaxy_one</name>
        <transport>hwbinder</transport>
        <version>1.0</version>
        <interface>
            <name>IGalaxyOne</name>
            <instance>default</instance>
        </interface>
        <fqname>@1.0::IGalaxyOne/default</fqname>
    </hal>

5.3 运行 service

这里我们手动启动,用于测试

./vendor/bin/hw/android.hardware.galaxy_one@1.0-service

5.4 运行 client

./system/bin/galaxy_test

运行成功之后可查看日志,有如下内容则表示服务建立成功:

 apk调用

在out下查找jar

 apk调用:

IGalaxyOne service = null;
        try {
            service = IGalaxyOne.getService(false);
            Log.e("longjiang",service.add(3,5)+"");
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        }

 

posted @ 2024-05-22 18:05  xiaowang_lj  阅读(15)  评论(0编辑  收藏  举报