自定义hal 接口实现流程

一、定义hal 层文件接口

https://blog.csdn.net/sevenjoin/article/details/107619014

进入代码,我们假设Naruto作为标准AOSP的HAL,我们就把代码揉进标准HAL(hardware/interface)层去,进入代码目录创建HIDL目录:

mkdir -p hardware/interfaces/naruto/1.0/default

接着创建接口描述文件INaruto.hal,放在刚才创建的1.0/default目录中:

// INaruto.hal

package android.hardware.naruto@1.0;

interface INaruto {

helloWorld(string name) generates (string result);

};

二、生成hdil 文件

2. 生成HAL相关文件

如上,hal接口文件已经定义完成,接下来我们需要继承实现这个接口,那么我们应该如何继承如何编写呢,幸运的是Google还是帮我们提供了一套工具hidl-gen来生成HAL层相关的代码框架和代码实例,这样子我们只需要关心实现部分,而不需要写一堆无用代码,浪费时间在搞Makefile和一些低级错误上。

hidl-gen 可以自动帮我们生成hal层实例框架和Android.mk/bp编译控制

因此,系统已经帮我们提供了一个执行脚本hardware/interface/update-makefiles.sh, 如下执行将会自动为我们生成所需要的文件:

./hardware/interface/update-makefiles.sh

查看我们最新的代码目录: hardware/interface/naruto:

├── 1.0

│ ├── Android.bp //自动生成

│ ├── Android.mk //自动生成

│ ├── default

│ │ ├── Android.bp //需要自己编写

│ │ ├── android.hardware.naruto@1.0-service.rc //用于加入开机启动服务

│ │ ├── Naruto.cpp //自动生成,可以自动修改。

│ │ ├── Naruto.h //自动生成

│ │ └── service.cpp // hidl服务端注册入口程序

│ └── INaruto.hal //定义接口

└── Android.bp //自动生成

特别是Naruto.cpp和Naruto.h这两个文件是实现接口的关键文件。

三、实现HAL服务端的共享库

3.1 修改源文件,实现接口

打开Naruto.h文件:

// Naruto.h

#ifndef ANDROID_HARDWARE_NARUTO_V1_0_NARUTO_H

#define ANDROID_HARDWARE_NARUTO_V1_0_NARUTO_H

#include <android/hardware/naruto/1.0/INaruto.h>

#include <hidl/MQDescriptor.h>

#include <hidl/Status.h>

namespace android {

namespace hardware {

namespace naruto {

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 Naruto : public INaruto {

// Methods from INaruto follow.

Return<void> helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) override;

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

};

// FIXME: most likely delete, this is only for passthrough implementations

// extern "C" INaruto* HIDL_FETCH_INaruto(const char* name);

} // namespace implementation

} // namespace V1_0

} // namespace naruto

} // namespace hardware

} // namespace android

#endif // ANDROID_HARDWARE_NARUTO_V1_0_NARUTO_H

我们知道,HIDL的实现有两种方式,一种是Binderized模式,另一种是Passthrough模式,我们看到上面有两行注释掉的代码,看来这个代码是关键,来选择实现方式是Binderized还是Passthrough。

我们这里使用Passthrough模式来演示,其实大家后面尝试这两种方式后会发现其实这两种本质是一样的,目前大部分厂商使用的都是Passthrough来延续以前的很多代码,但是慢慢的都会被改掉的,所以我们来打开这个注释 - extern “C” INaruto* HIDL_FETCH_INaruto(const char* name);

打开并修改Naruto.cpp:

// Naruto.cpp

#include "Naruto.h"

namespace android {

namespace hardware {

namespace naruto {

namespace V1_0 {

namespace implementation {

// Methods from INaruto follow.

Return<void> Naruto::helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) {

// TODO implement

char buf[100];

memset(buf, 0, 100);

snprintf(buf, 100, "HelloWorld, %s", name.c_str());

hidl_string result(buf);

_hidl_cb(result);

return Void();

}

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

INaruto* HIDL_FETCH_INaruto(const char* /* name */) {

return new Naruto();

}

} // namespace implementation

} // namespace V1_0

} // namespace naruto

} // namespace hardware

} // namespace android

这个文件我们做了两处改动:

打开了HIDL_FETCH_I的注释,让我们的HIDL使用Passthrough方式去实现

添加helloWorld函数的实现,简单的做了字符串拼接(学过C/C++)的同学应该都看得懂

3.2 编写bp文件,生成so库

接下来我们编写一下default/Android.bp文件生成我们需要impl实现的so库。

default/Android.bp

cc_library_shared {

name: "android.hardware.naruto@1.0-impl",

relative_install_path: "hw",

proprietary: true,

srcs: [

"Naruto.cpp",

],

shared_libs: [

"libhidlbase",

"libhidltransport",

"libutils",

"android.hardware.naruto@1.0",

],

}

最终会生成一个android.hardware.naruto@1.0-impl.so, 生成在/vendor/lib/hw/下,如果是64bits的平台会生成在/vendor/lib64/hw,在这里我用的是32bits平台,最终会生成在/vendor/lib/hw/目录下;

我们可以用mmm编译生成共享库

mmm hardware/interface/naruto

这样将会生成两个so文件,一个是INaruto接口框架库文件:android.hardware.naruto@1.0.so 一个是INaruto实现接口库文件:android.hardware.naruto@1.0-impl.so

四、 Hal server端启动注册程序

现在,我们有了HIDL接口及接口的实现实例,接下来需要将这个服务端注册到HWServiceManager中,客户端才能通过binder接口getService()获取到服务端的接口并使用它

如需使服务器接口的实现可供客户端使用,您可以:

  1. hwservicemanager 注册接口实现(详情见下文) 或者

  1. 将接口实现作为接口方法的参数进行传递。异步回调

还记得我们之前创建的两个文件吗,我们还没有去实现呢,先来看一下rc文件

# android.hardware.naruto@1.0-service.rc

service naruto_hal_service /vendor/bin/hw/android.hardware.naruto@1.0-service

class hal

user system

group system

很简单,就是在设备启动的时候执行/vendor/bin/hw/android.hardware.naruto@1.0-service程序,这个程序就是去注册服务的到serviceManager管理中的,具体实现如下:

service.cpp文件:

// service.cpp

# define LOG_TAG "android.hardware.naruto@1.0-service"

# include <android/hardware/naruto/1.0/INaruto.h>

# include <hidl/LegacySupport.h>

using android::hardware::naruto::V1_0::INaruto;

using android::hardware::defaultPassthroughServiceImplementation;

int main() {

return defaultPassthroughServiceImplementation<INaruto>();

}

这个service是注册了INaruto接口文件里面的接口,作为binder server端,很简单就一句话,因为我们使用了passthrough的模式,Android帮我们封装了这个函数,不需要我们自己去addService啦,直接调用defaultPassthroughServiceImplementation即可。

还有另一种注册方式是针对binderies的:

接口可作为对象传递。“接口”一词可用作 android.hidl.base@1.0::IBase 类型的语法糖;此外,当前的接口以及任何导入的接口都将被定义为一个类型。

持有接口的变量应该是强指针:sp<IName>。接受接口参数的 HIDL 函数会将原始指针转换为强指针,从而导致不可预料的行为(可能会意外清除指针)。为避免出现问题,请务必将 HIDL 接口存储为 sp<>

https://source.android.google.cn/docs/core/architecture/hidl-cpp/types

::android::sp<IFoo> myFoo = new FooImpl();

::android::sp<IFoo> mySecondFoo = new FooAnotherImpl();

status_t status = myFoo->registerAsService();

status_t anotherStatus = mySecondFoo->registerAsService("another_foo");

hwservicemanager 会将 [package@version::interface, instance_name] 组合视为唯一,以使不同的接口(或同一接口的不同版本)能够采用完全相同的实例名称无冲突地注册。如果您调用的 registerAsService() 具有完全相同的软件包版本、接口和实例名称,则 hwservicemanager 将丢弃对先前注册的服务的引用,并使用新的服务。

接下来,在Android.bp中添加对应的编译控制逻辑:

cc_binary {

name: "android.hardware.naruto@1.0-service",

defaults: ["hidl_defaults"],

proprietary: true,

relative_install_path: "hw",

srcs: ["service.cpp"],

init_rc: ["android.hardware.naruto@1.0-service.rc"],

shared_libs: [

"libhidlbase",

"libhidltransport",

"libutils",

"liblog",

"android.hardware.naruto@1.0",

"android.hardware.naruto@1.0-impl",

],

}

编译后可以在, vendor/bin/hw/下找到对应的文件

OK,我们server端的进程实现端共享库已经完成了。

五、selinux 权限

所以正确的做法是要给他加上selinux的权限,我们这里就不去做了,因为我们可以用root权限(直接push到设备)去手动起这个service。

六、客户端实现

# include <android/hardware/naruto/1.0/INaruto.h>

# include <hidl/Status.h>

# include <hidl/LegacySupport.h>

# include <utils/misc.h>

# include <hidl/HidlSupport.h>

# include <stdio.h>

using android::hardware::naruto::V1_0::INaruto;

using android::sp;

using android::hardware::hidl_string;

int main()

{

int ret;

android::sp<INaruto> service = INaruto::getService();

if(service == nullptr) {

printf("Failed to get service\n");

return -1;

}

service->helloWorld("SvenCheng", [&](hidl_string result) {

printf("%s\n", result.c_str());

});

return 0;

}

Makefile吧

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_PROPRIETARY_MODULE := true

LOCAL_MODULE := naruto_test

LOCAL_SRC_FILES := \

naruto_client.cpp \

LOCAL_SHARED_LIBRARIES := \

liblog \

libhidlbase \

libutils \

android.hardware.naruto@1.0 \

include $(BUILD_EXECUTABLE)

以上,通过mmm vendor/mediatek/proprietary/external/naruto编译完成后会生成在/system/bin/naruto_test中。

七、添加接口

记得在manifest文件里添加vendor接口的定义,不然在client端是没法拿到service的,在相应的manifest.xml里面加入

<hal format="hidl">

<name>android.hardware.naruto</name>

<transport>hwbinder</transport>

<version>1.0</version>

<interface>

<name>INaruto</name>

<instance>default</instance>

</interface>

</hal>

一般都是在device///manifest.xml

修改manifest.xml文件后需要重新编译整个aosp并烧录镜像到设备中才可以,否则启动程序时会报: 无法找到接口getService()。

八、文件的生成和对应的位置

通过以上,我们总共产出了如下文件:

android.hardware.naruto@1.0.so: Naruto模块hal框架接口,暴露给客户端使用。

android.hardware.naruto@1.0-impl.so: Naruto模块服务端的代码实现,接口被调用后在binder server端的真正行为。

android.hardware.naruto@1.0-service: Naruto服务端启动注册入口程序

android.hardware.naruto@1.0-service.rc: Android native 进程入口

naruto_test: 客户端调用程序

我们需要将上述文件分别push到对应目录中:

adb push android.hardware.naruto@1.0.so /system/lib/

adb push android.hardware.naruto@1.0-impl.so /vendor/lib/hw/

adb push android.hardware.naruto@1.0-service /system/bin

adb push naruto_test /vendor/bin/hw #必须这个目录,否则会出现dlsysm失败问题

九、aidl for hal

如果 HAL 使用 AIDL 在框架组件(例如 system.img 中的组件)和硬件组件(例如 vendor.img 中的组件)之间进行通信,必须使用稳定的 AIDL。不过,如需在分区内进行通信(例如从一个 HAL 到另一个 HAL),则对需要使用的 IPC 机制没有任何限制。

为了最大限度提高代码可移植性并避免出现潜在问题(例如不必要的额外库),建议停用 CPP 后端。

在 AOSP 中,适用于 HAL 的稳定 AIDL 接口所在的基础目录与 HIDL 接口所在的基础目录相同,位于 aidl 文件夹中。

  • hardware/interfaces

  • frameworks/hardware/interfaces

  • system/hardware/interfaces

您应将扩展接口放入 vendorhardware 下的其他 hardware/interfaces 子目录中。

Android 的每个版本都提供了一套官方 AOSP 接口。如果 Android 合作伙伴需要向这些接口添加功能,应注意不能直接更改这些接口,否则会造成自己的 Android 运行时与 AOSP Android 运行时不兼容。对于 GMS 设备,也应避免直接更改这些接口,以确保 GSI 映像能够正常运行。

扩展可以通过两种不同的方式进行注册:

  • 独立注册(在全局注册和在 VINTF 内注册)。

无论以哪种方式注册扩展,当特定于供应商(即不属于上游 AOSP 的组成部分)的组件使用接口时,都不可能出现合并冲突

AIDL 具有三个不同的后端:Java、NDK、CPP。如需使用稳定的 AIDL,您必须始终使用位于 system/lib*/libbinder.so 的 libbinder 的系统副本,并能够访问 /dev/binder

对于供应商映像中的代码,这意味着 libbinder(来自 VNDK)无法使用:此库包含不稳定的 C++ API 和不稳定的内件。

而原生供应商代码必须使用 AIDL 的 NDK 后端,链接到 libbinder_ndk(由系统 libbinder.so 提供支持)以及由 aidl_interface 条目创建的 -ndk_platform 库。

IPC 域说明
/dev/binder框架/应用进程之间的 IPC,使用 AIDL 接口
/dev/hwbinder框架/供应商进程之间的 IPC,使用 HIDL 接口
供应商进程之间的 IPC,使用 HIDL 接口
/dev/vndbinder供应商/供应商进程之间的 IPC,使用 AIDL 接口

只有一个自动生成的文件独立于 HIDL 使用的 RPC 机制,那就是 IFoo.h;其他所有文件都与 HIDL 使用的 HwBinder RPC 机制相关联。因此,客户端和服务器实现不得直接引用除 IFoo 之外的任何内容。为了满足这项要求,请只包含 IFoo.h 并关联到生成的共享库。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值