Android O(8.0) 版本之后,底层实现有了比较大的变化,最显著的一个方面就是 HIDL 机制的全面实施。本文对于理解系统源码中 Gnss、Usb、Camera 等模块的工作原理有极大帮助。
在 Android O(8.0) 之前系统的升级牵扯多方协作,极为麻烦,HIDL机制的推出就是将 framework 与 hal 层分开,使得框架部分可以直接被覆盖、更新,而不需要重新对 HAL 进行编译,这样在系统升级时,OEM 厂商 跳过 SoC 厂商,先对 framework 进行升级。
framework 与 hal 紧紧耦合存在于 system.img 中,因此在版本升级时需要: OEM 厂商适配 framework ,SoC厂商 适配 hal, 之后将修改打包到 system.img,生成 OTA 升级包,推送到手机进行 OTA 升级
framework 与 hal 进行了解耦, framework 存在于 system.img,hal 存在于vendor.img,进行版本升级时,分为两次升级:
正如上述所言,旧版的系统架构中, Android Framework 层与 Hal 层是打包成一个 system.img 的,且 Framework 与 hal 层之间是紧密耦合的,通过链接的方式使用相应的硬件 so 库。它们之间的架构一般有如下两种方式:
为了解决两者之间这种紧耦合所带来的弊端,google 引入 HIDL 来定义 Framework 与 HAL 之间的接口,可以用下图来描述:
事实上虽然 google 推出了这种机制,但是很多厂商没有很快的跟上节奏,因此为了向前兼容, google 定义了三种类型:
上述可总结为:
上述介绍参考此处
绑定模式 是 google 为了向前兼容而定义的一种类型,且 Android 8.0 及后续版本的设备都必须只支持这种模式。这种模式下 Framework 与 Hal 分别位于不同的进程中,其实从具体实现来讲这种模式也更应该被称为 Binder 化的直通式。下面将通过这种方式实现一个具有加减乘除功能的 HIDL 服务,该服务的名称为 MyTest
在系统源码中的 hardware/interfaces 目录下有很多的 HIDL,我们仿照其他 HIDL 来创建自己的目录,在源码根目录执行以下命令:
mkdir -p hardware/interfaces/my_test/1.0/default
之后创建 IMyTest.hal 文件:
touch hardware/interfaces/my_test/1.0/IMyTest.hal
这里定义了四种基本的运算:加、减、乘、除,这是上层调用 HAL 的入口,该文件内容如下:
package [email protected];
interface IMyTest{
//加法
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);
};
Google帮我们提供了 hidl-gen 工具来生成 HAL 层相关的代码框架和代码实例,这样子我们只需要关心实现部分。在源码根目录执行以下命令:
source build/envsetup.sh
lunch xxx
[email protected]
LOC=hardware/interfaces/my_test/1.0/default/
make hidl-gen
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
之后执行 update-makefiles.sh 脚本来为 HIDL 生成对应的 Android.bp 文件,源码根目录执行:
./hardware/interfaces/update-makefiles.sh
现在我们的工程目录结构如下:
接下来我们需要再创建两个文件:
touch hardware/interfaces/my_test/1.0/default/[email protected]
touch hardware/interfaces/my_test/1.0/default/service.cpp
最终我们的工程目录结构如下:
上述过程已经将 HIDL 服务所需要的文件创建完成,虽然其中很多文件还没有具体实现,我们先放在一边,先来对整体的调用流程及各个文件的作用略作说明:
Application | 上层应用 |
JNI | 指 framework 层,getService 获取 hal 层 service |
[email protected] | 接口库,由hardware/interfaces/my_test/1.0/Android.bp 通过 IMyTest.hal 生成,这样只要这个接口库不变,那么 framework 的更新和 hal 层就隔绝开了 |
[email protected] | 实现库,上层应用的最终调用 |
mytest-hal-service | service的名称 |
[email protected] | 设备开机时通过解析 rc 文件启动此服务 |
[email protected] 由hardware/interfaces/my_test/1.0/Android.bp 通过 IMyTest.hal 生成,该 Android.bp 文件是在上面一系列命令执行之后生成,而接口库是当我们最终执行编译模块时生成,可以说这个过程不需要我们手动参与。
由 hardware/interfaces/galaxy_one/1.0/default/Android.bp 在最后模块编译时通过 GalaxyOne.cpp 生成,
Android.bp 内容如下:
// FIXME: your file license if you have one
cc_library_shared {
// FIXME: this should only be -impl for a passthrough hal.
// In most cases, to convert this to a binderized implementation, you should:
// - change '-impl' to '-service' here and make it a cc_binary instead of a
// cc_library_shared.
// - add a *.rc file for this module.
// - delete HIDL_FETCH_I* functions.
// - call configureRpcThreadpool and registerAsService on the instance.
// You may also want to append '-impl/-service' with a specific identifier like
// '-vendor' or '-' etc to distinguish it.
name: "[email protected]",
relative_install_path: "hw",
// FIXME: this should be 'vendor: true' for modules that will eventually be
// on AOSP.
proprietary: true,
srcs: [
"MyTest.cpp",
],
shared_libs: [ //可以添加需要的库
"liblog",
"libhidlbase",
"libhidltransport",
"libhwbinder",
"libutils",
"[email protected]",
],
}
实现库是通过 MyTest.cpp 编译生成的,现在完善 MyTest.h 和 MyTest.cpp
MyTest.h
Binder化直通式,同样需要将 HIDL_FETCH_XXX 打开
// FIXME: your file license if you have one
#pragma once
#include
#include
#include
#include
namespace android::hardware::my_test::V1_0::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 MyTest : public V1_0::IMyTest {
// Methods from ::android::hardware::my_test::V1_0::IMyTest follow.
Return add(uint32_t a, uint32_t b) override;
Return sub(uint32_t a, uint32_t b) override;
Return mul(uint32_t a, uint32_t b) override;
Return 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" IMyTest* HIDL_FETCH_IMyTest(const char* name);
} // namespace android::hardware::my_test::V1_0::implementation
注意,hidl-gen生成的 MyTest.h 的代码中,命名空间不对;将android::hardware::my_test::implementation 改成 android::hardware::my_test::V1_0::implementation
MyTest.cpp
// FIXME: your file license if you have one
#include "MyTest.h"
namespace android::hardware::my_test::V1_0::implementation {
// Methods from ::android::hardware::my_test::V1_0::IMyTest follow.
Return MyTest::add(uint32_t a, uint32_t b) {
// TODO implement
uint32_t result = a + b;
ALOGE("MyTest::add a = %d,b = %d,result = %d",a,b,result);
return uint32_t {};
}
Return MyTest::sub(uint32_t a, uint32_t b) {
// TODO implement
uint32_t result = a - b;
ALOGE("MyTest::sub a = %d,b = %d,result = %d",a,b,result);
return uint32_t {};
}
Return MyTest::mul(uint32_t a, uint32_t b) {
// TODO implement
uint32_t result = a * b;
ALOGE("MyTest::mul a = %d,b = %d,result = %d",a,b,result);
return uint32_t {};
}
Return MyTest::div(uint32_t a, uint32_t b) {
// TODO implement
uint32_t result = a / b;
ALOGE("MyTest::div a = %d,b = %d,result = %d",a,b,result);
return uint32_t {};
}
// Methods from ::android::hidl::base::V1_0::IBase follow.
IMyTest* HIDL_FETCH_IMyTest(const char* /* name */) {
ALOGE("my_test service init success....");
return new MyTest();
}
//
} // namespace android::hardware::my_test::V1_0::implementation
同样需要更改命名空间
现在除了需要的 rc 文件没有补充、mytest-hal-service 服务没有生成外其余均已配置好了,现在进行编译生成对应的库。进入根目录下执行如下命令:(注意是在刚刚执行过的 source build/envsetup.sh 和 lunch 的窗口下编译,若是新窗口则需要重新执行这两条命令)
mmm hardware/interfaces/my_test/1.0
编译成功后会在 out\target\product\xxxxxxx\vendor\lib64\hw 下生成 [email protected];在 out\target\product\xxxxxxx\system\lib64 下生成 [email protected];
接下来我们需要生成对应的 service 可执行文件,这个过程一共分为三步:
1.在 /default 下的 Android.bp 文件中追加如下内容
cc_binary {
name: "[email protected]",
defaults: ["hidl_defaults"],
relative_install_path: "hw",
vendor: true,
srcs: [
"service.cpp"
],
init_rc: ["[email protected]"],
shared_libs: [
"liblog",
"libhidlbase",
"libhidltransport",
"libhwbinder",
"libutils",
"[email protected]",
],
}
2.补充 service.cpp
defaultPassthroughServiceImplementation 会帮我们自动注册服务;
#define LOG_TAG "[email protected]"
#include
#include
#include "MyTest.h"
// Generated HIDL files
using android::hardware::my_test::V1_0::IMyTest;
using android::hardware::my_test::V1_0::implementation::MyTest;
using android::hardware::defaultPassthroughServiceImplementation;
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
int main() {
return defaultPassthroughServiceImplementation();
}
3.补充 rc 文件
系统开机会解析该 .rc 文件,启动该服务。这里的 mytest-hal-service 相当于这个服务的别名;如果需要开机自启动,需要配置 seLinux 权限,这里暂不介绍;因此下面我们测试验证的时候通过手动启动的方式启动服务。
service mytest-hal-service /vendor/bin/hw/[email protected]
class hal
user system
group system
再次在根目录执行如下命令:
mmm hardware/interfaces/my_test/1.0
完成之后就会得到如下二进制可执行文件:
out\target\product\xxxxxxx\vendor\bin\hw\[email protected]
经过一系列过程之后,我们得到了三个产物:
1、[email protected]
2、[email protected]
3、[email protected]
接下来模拟一个客户端来测试调用;在 default 目录下创建 test 目录,并新建 client.cpp、Android.bp 文件;创建好后工程目录如下:
client.cpp内容:
#include
#include
#include
using android::sp;
using android::hardware::my_test::V1_0::IMyTest;
using android::hardware::Return;
int main(){
android::sp service = IMyTest::getService();
if (service == nullptr) {
ALOGD("faile to get my_test service......");
return -1;
}
ALOGE("success to get my_test service.....");
uint32_t addResult = service->add(3,4);
ALOGE("my_test service add: result = %d",(int)addResult);
uint32_t subResult = service->sub(8,3);
ALOGE("my_test service sub: result = %d",(int)subResult);
uint32_t mulResult = service->mul(3,8);
ALOGE("my_test service mul: result = %d",(int)mulResult);
uint32_t divResult = service->div(8,2);
ALOGE("my_test service div: result = %d",(int)divResult);
return 0;
}
Android.bp内容:
cc_binary {
name: "my_hidl_test", //表示生成的 client 名称
srcs: [
"client.cpp"
],
shared_libs: [
"liblog",
"[email protected]",
"libhidlbase",
"libhidltransport",
"libhwbinder",
"libutils",
],
}
根目录执行 mmm hardware/interfaces/my_test/1.0 编译工程,成功后可在 out\target\product\xxxxxxx\system\bin 目录下找到 my_hidl_test
现在我们一共得到 4 个产物,使用 adb 命令将其 push 到车机对应目录下:
[email protected] ====》 /vendor/lib64
[email protected] ====》 /vendor/lib64/hw
[email protected] ====》 /vendor/bin/hw
my_hidl_test ====》 /system/bin
HIDL 想要被 framework 获取使用还需要在 manifest.xml 中注册,该文件在车机 /vendor/etc/vintf/ 目录下(不同厂商可能不同,以实际情况为准),添加下面的内容:
android.hardware.my_test
hwbinder
1.0
IMyTest
default
@1.0::IMyTest/default
手动后台运行
adb root
adb remount
./vendor/bin/hw/[email protected] &
./system/bin/my_hidl_test &
运行后查看系统日志,有如下内容则成功:
01-01 00:01:58.127 13377 13377 E my_hidl_test: success to get my_test service.....
01-01 00:01:58.127 11110 11110 E [email protected]: MyTest::add a = 3,b = 4,result = 7
01-01 00:01:58.128 13377 13377 E my_hidl_test: my_test service add: result = 0
01-01 00:01:58.128 11110 11110 E [email protected]: MyTest::sub a = 8,b = 3,result = 5
01-01 00:01:58.128 13377 13377 E my_hidl_test: my_test service sub: result = 0
01-01 00:01:58.128 11110 11110 E [email protected]: MyTest::mul a = 3,b = 8,result = 24
01-01 00:01:58.128 13377 13377 E my_hidl_test: my_test service mul: result = 0
01-01 00:01:58.129 11110 11110 E [email protected]: MyTest::div a = 8,b = 2,result = 4
01-01 00:01:58.129 13377 13377 E my_hidl_test: my_test service div: result = 0