我们看看AOSP有哪些HAL:
这些都是Android 设备上的硬件,因为Google 理论上只关心 Android 的框架层 和 上层软件,
但是 上层软件 依赖于底层的硬件实现,
但每家手机厂商或者CPU 厂商底层的硬件实现都是不一样的,
所以这个 HAL 层基本都是手机厂商或者CPU 厂商来实现的。
Google只是作为一个框架的指导,和Framework层API的接口定义,这些接口的实现都得由HAL去完成。
那么我们的 HIDL_Demon 就肩负了这个重任,控制底层硬件,
底层硬件都是由 Linux Kernel 驱动控制的,提供文件读写就可以简单控制驱动。
2.2.1 HIDL 接口文件定义
进入代码,我们假设 hidl_daemon作为标准 AOSP 的 HAL, 我们就把代码揉进标准 HAL 层去,
进入代码目录,创建 HIDL 目录:
mkdir -p hardware/interfaces/hidl_daemon/1.0/default
接着创建接口描述文件 Ihidl_daemon.hal 放在刚才的目录:
package android.hardware.hidl_daemon@1.0;
interface IHidl_daemon {
helloWorld(string name) generates (string result);
};
没错,这是一个Google定义的语言格式,C++和Java的结合体。
这里我们定义了一个INaruto接口文件,简单的添加了一个helloWorld接口,传入是一个string,返回一个string,后面我们会来实现这个接口。
2.2.2 生成 HAL 相关文件
Google帮我们提供了一些工具来生成HAL层相关的代码框架和代码实例,
这样子我们只需要关心实现部分,而不需要写一堆无用代码,浪费时间在搞Makefile和一些低级错误上。
使用hidl-gen工具
PACKAGE=android.hardware.hidl_daemon@1.0
LOC=hardware/interfaces/hidl_daemon/1.0/default/
make hidl-gen -j64
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
然后使用脚本来更新Makefile,自动生成Android,mk, Android.bp
./hardware/interfaces/update-makefiles.sh
现在,我们来添加两个空文件:
touch hardware/interfaces/hidl_daemon/1.0/default/android.hardware.hidl_daemon@1.0-service.rc
touch hardware/interfaces/hidl_daemon/1.0/default/service.cpp
├── 1.0
│ ├── Android.bp
│ ├── Android.mk
│ ├── default
│ │ ├── Android.bp
│ │ ├── android.hardware.hidl_daemon@1.0-service.rc
│ │ ├── Hidl_daemon.cpp
│ │ ├── Hidl_daemon.h
│ │ └── service.cpp
│ └── IHidl_daemon.hal
└── Android.bp
是不是so easy,我们写代码就写了一个 hidl_daemon.hal,其余代码都是自动生成的,
特别是 hidl_daemon.cpp 和 hidl_daemon.h 这两个文件是实现接口的关键文件。
2.2.3 实现HAL实现端的共享库
此时打开 Hidl_daemon.cpp 和 Hidl_daemon.h 这两个代码文件,我们要开始正式写代码了。
打开 Hidl_daemon.h
struct Hidl_daemon: public IHidl_daemon {
// 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" IHidl_daemon* HIDL_FETCH_INaruto(const char* name);
我们知道,HIDL 的实现有两种方式,一种是 Binderized 模式,另一种是 Passthrough 模式,
我们看到上面有两行注释掉的代码,看来这个代码是关键,来选择实现方式是 Binderized 还是 Passthrough。
我们这时使用 Passthrough 模式来演式,其实大家后面尝试这两种方式后会发现,其实这两种本质是一样的。
目前大部分厂商使用的都是 Passthrough 来延续以前的很多代码,
但是慢慢的都会被改掉的,所以目前我们打开这个 注释。
@ Hidl_daemon.h
# ifndef ANDROID_HARDWARE_NARUTO_V1_0_HIDL_DAENOM_H
# define ANDROID_HARDWARE_NARUTO_V1_0_HIDL_DAEMON_H
# include
# include
# include
namespace android {
namespace hardware {
namespace hidl_daemon{
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 Hidl_daemon: public IHidl_daemon {
// 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 hidl_daemon
} // namespace hardware
} // namespace android
# endif // ANDROID_HARDWARE_NARUTO_V1_0_HIDL_DAEMON_H
@ Hidl_daemon.cpp
# include "Hidl_daemon.h"
namespace android {
namespace hardware {
namespace hidl_daemon{
namespace V1_0 {
namespace implementation {
// Methods from INaruto follow.
Return<void> Hidl_daemon::helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) {
// TODO implement
char buf[100];
::memset(buf, 0x00, 100);
::snprintf(buf, 100, "Hello World, %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_IHidl_daemon(const char* /* name */) {
return new Hidl_daemon();
}
} // namespace implementation
} // namespace V1_0
} // namespace hidl_daemon
} // namespace hardware
} // namespace android
然后,可以查看一下 Android.bp 文件看下编译生成啥。
cc_library_shared {
name: "[email protected]",
relative_install_path: "hw",
proprietary: true,
srcs: [
"Hidl_daemon.cpp",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"[email protected]",
],
}
最终会生成 [email protected] , 生成在 /vendor/lib64/hw 下,
我们用 mmm 编译生成看看。
$ mmm hardware/interfaces/hidl_daemon/1.0/default/
# OUT_DIR=out
[2/2] bootstrap out/soong/.minibootstrap/build.ninja.in
[1/1] out/soong/.bootstrap/bin/minibp out/soong/.bootstrap/build.ninja
[2/3] glob hardware/interfaces/*/Android.bp
[1/1] out/soong/.bootstrap/bin/soong_build out/soong/build.ninja
No need to regenerate ninja file
[100% 3/3] out/soong/.bootstrap/bin/soong_build out/soong/build.ninja
[100% 18/18] build 'out/target/product/hon660/obj/SHARED_LIBRARIES/[email protected]_intermediates/[email protected]'
#### build completed successfully (02:35 (mm:ss))
2.2.4 代码调用流程
前面我们实现了代码端的编译,我们这节来看下整个 HIDL 的调用流程,
因为里面涉及到了好几个库文件,我们来看下这之前的关系。
HIDL 软件包中自动生成的文件会链接到与软件包同名的单个共享库。
该共享库还会导出单个头文件 Hidl_daemon.h,用于在 binder 客户端和服务端的接口文件。
下面的图诠释了我们的 IHidl_daemon.hal 编译后生成的文件走向。
从官网拷贝过来的,大家不用在乎文件名哈:
iFoo.h 接口文件
描述 C++ 类中的纯 IFoo 接口; 它包含 IFoo.hal 文件中的 IFoo 接口中所定义的方法和类型,必要时会转换为 C++ 类型。
不包含 与用于实现此接口的 RPC 机制(例如 HwBinder) 相关的详细信息。
类的命名空间包含软件包名 和 版本号,例如 ::android::hardware::samples::IFoo::V1_0.
客户端 和 服务端都名含此标头: 客户端用它来调用方法,服务器用它来实现这些方法。
iHwFoo.h 头文件
其中包含用于对接口中使用的数据类型进行序列化的函数的声明。
开发者不得直接包含其标头(它不包含任何类)
BpFoo.h 头文件
从 iFoo 继承的类,可描述接口的 HwBinder 代理(客户端)实现。开发者不得直接引用此类。
BnFoo.h 头文件
保存对 IFoo 实现的引用的类,可描述接口的 HwBinder 存根(服务器)实现。
开发者不得引用此类。
FooAll.cpp 头文件
包含 HwBinder 代理 和 HwBinder 存根的实现的类。
当客户端调用接口的方法时,代理会自动从客户端封送参数,关将事务发送到绑定内核驱动程序,该内核 驱动程序会将事务传送到另一端的存根(该存根随后会调用实际的服务器)
这些文件的结构类似于由 aidl-cpp 生成的文件。
获立于 HIDL 使用的 RPC 机制的唯一一个自动生成的文件是 IFoo.h,
其他所有文件都与HIDL 使用的 HwBinder RPC 机制相关联。
因此,客户端和服务器实现不得直接引用除 IFoo 之外的任何内容。
为满足这项要求,请只包含 IFoo.h 并链接到 生成的共享库。
我们这个实例会用到以下几个模块:
[email protected]
Hidl_daemon模块实现端的代码编译生成,binder server端
[email protected]
Hidl_daemon模块调用端的代码,binder client端
hidl_daemon_hal_service
通过直通式注册binder service,暴露接口给client调用
[email protected]
Android native 进程入口
2.2.5 启动binder server端进程
还记得我们之前创建的两个文件吗,我们还没有去实现呢,先来看一下rc文件
service hidl_daemon_hal_service /vendor/bin/hw/android.hardware.hidl_daemon@1.0-service
class hal
user system
group system
很简单,就是在设备启动的时候执行/vendor/bin/hw/[email protected]程序:
# define LOG_TAG "[email protected]"
# include
# include
using android::hardware::hidl_daemon::V1_0::INaruto;
using android::hardware::defaultPassthroughServiceImplementation;
int main() {
return defaultPassthroughServiceImplementation<IHidl_daemon>();
}
这个service是注册了INaruto接口文件里面的接口,作为 binder server 端,
很简单就一句话,
因为我们使用了 passthrough 的模式,Android 帮我们封装了这个函数,不需要我们自己去 addService 啦。
cc_binary {
name: "[email protected]",
defaults: ["hidl_defaults"],
proprietary: true,
relative_install_path: "hw",
srcs: ["service.cpp"],
init_rc: ["[email protected]"],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"liblog",
"[email protected]",
"[email protected]",
],
}
编译后可以在, vendor/bin/hw/下找到对应的文件。
OK,我们server端的进程和实现端共享库已经完成了。
但是这个时候你如果烧录镜像,会发现这个进程会启动失败,
原因是因为我们没有给这个进程配sepolicy,所以正确的做法是要给他加上selinux的权限,
我们这里就不去做了,因为我们可以用root权限去手动起这个service。
好了,接下来要看看client的代码怎么写了。
2.2.6 HIDL Client测试代码
测试代码如下:
@ client.cpp
# include
# include
# include
# include
# include
# include
using android::hardware::naruto::V1_0::INaruto;
using android::sp;
using android::hardware::hidl_string;
int main()
{
int ret;
android::sp<IHidl_daemon> service = IHidl_daemon::getService();
if(service == nullptr) {
printf("Failed to get service\n");
return -1;
}
service->helloWorld("aaaaaa", [&](hidl_string result) {
printf("%s\n", result.c_str());
});
return 0;
}
实例化binder service,通过INaruto::getService(),获取到binder server端接接口代理类,然后就可以调用他的方法了,我们这里调用helloWorld接口,然后通过callback获取结果。
Makefile
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE := hidl_daemon_test
LOCAL_SRC_FILES := \
client.cpp \
LOCAL_SHARED_LIBRARIES := \
liblog \
libhidlbase \
libutils \
android.hardware.hidl_daemon@1.0 \
include $(BUILD_EXECUTABLE)
记得在manifest文件里添加vendor接口的定义,
不然在client端是没法拿到service的,在相应的manifest.xml里面加入:
<hal format="hidl">
<name>android.hardware.hidl_daemonname>
<transport>hwbindertransport>
<version>1.0version>
<interface>
<name>IHidl_daemonname>
<instance>defaultinstance>
interface>
hal>
然后我们来测试一下代码吧:
手动运行service:
/vendor/bin/hw/[email protected]
动行测试代码
./hidl_daemon_test