Android 8.0 之后,google 为了解决 Android 版本碎片化问题, 推出了 treble 架构,参考 Android Treble架构解析 。核心思想就是 vendor 分区和 system 分区的隔离。把厂商的更改限制在 vendor 分区,system 分区由 google 把控。system 分区需要访问 vendor 分区的话,需要通过 hidl 的形式来访问。 网上有很多介绍 hidl 的文章,但没有一篇是介绍得比较全面的,都会遗漏一些小细节,由于这样,那样的小细节,自己到处找资料,折腾了两天才把 hidl 的demo 跑起来。以下作详细的记录,手把手教你从零创建自己的 hidl 服务。关于什么是 hidl ,及为什么要用 hidl 的理论知识这里就不作介绍了,可以参考官网的介绍 : hidl概览
1. 添加 hal 目录 网上的 demo 全部都是直接在 android/hardware/interfaces 目录下添加了, 但我觉得最好是在自己的 device 目录下添加比较好维护一点。
1 mkdir -p device/qiushao/pure/models/hidl/tvserver/1.0
2. 定义 hal 接口 在 device/qiushao/pure/models/hidl/tvserver/1.0 目录下创建文件 ITVServer.hal
1 2 3 4 5 package device.qiushao.pure.tvserver@1.0 ;interface ITVServer { hello(string name) generates (string result); };
3. 根据 .hal 自动生成 cpp 实现 1 2 3 qiushao@qiushao-pc:~/source /android-10$ PACKAGE=device.qiushao.pure.tvserver@1.0 qiushao@qiushao-pc:~/source /android-10$ LOC=device/qiushao/pure/models/hidl/tvserver/1.0/default qiushao@qiushao-pc:~/source /android-10$ hidl-gen -o $LOC -Lc++-impl -rdevice.qiushao.pure:device/qiushao/pure/models/hidl $PACKAGE
执行完以上命令之后在 default 目录下生成了 TVServer.h 和 TVServer.cpp 这两个文件 我们需要对 TVServer.cpp 实现进行完善,只需要对我们定义的 hello 接口进行实现就行了,修改如下:
1 2 3 4 5 6 7 8 Return<void> TVServer::hello(const hidl_string& name, hello_cb _hidl_cb) { char buf[100]; ::memset(buf, 0, 100); ::snprintf(buf, 100, "hello %s from TVServer", name.c_str()); hidl_string result(buf); _hidl_cb(result); return Void(); }
我们还需要一个进程来容纳我们的服务,创建一个 main.cpp 文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <hidl/HidlTransportSupport.h> #include <utils/Looper.h> #include <utils/StrongPointer.h> #include "TVServer.h" using android::hardware::configureRpcThreadpool; using android::hardware::joinRpcThreadpool; using device::qiushao::pure::tvserver::V1_0::ITVServer; using device::qiushao::pure::tvserver::V1_0::implementation::TVServer; int main(int argc, char **argv) { // sleep for a second, wait /data/vendor to be mounted sleep(1); configureRpcThreadpool(4, true /* callerWillJoin */); android::sp<ITVServer> service = new TVServer(); android::status_t ret = service->registerAsService(); if (ret != android::NO_ERROR) { } joinRpcThreadpool(); return 0; }
4. 开机自动启动服务 在 tvserver/1.0/default 目录下创建 device.qiushao.pure.tvserver@1.0-service.rc 文件:
1 2 3 4 service tvserver /vendor/bin/hw/device.qiushao.pure.tvserver@1.0-service class hal user root group root
5. 生成 Android.bp 1 hidl-gen -o $LOC -Landroidbp-impl -rdevice.qiushao.pure:device/qiushao/pure/models/hidl $PACKAGE
在 Android 9.0 之后,查看自动生成的 Android.bp 文件,里面有些提示,说明自动生成的文件还需要稍作修改,我们修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 cc_binary { name: "device.qiushao.pure.tvserver@1.0-service", init_rc: ["device.qiushao.pure.tvserver@1.0-service.rc"], relative_install_path: "hw", vendor: true, srcs: [ "TVServer.cpp", "main.cpp", ], shared_libs: [ "libhidlbase", "libhidltransport", "libutils", "device.qiushao.pure.tvserver@1.0", ], }
其中 device.qiushao.pure.tvserver@1.0-service.rc 文件会被安装到 /vendor/etc/init/ 目录。系统启动时会自动加载这个目录下的所有 rc 文件。
此时目录结构是这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 qiushao@qiushao-pc:~/source /android-10/device/qiushao/pure/models/hidl$ tree tvserver/ tvserver/ └── 1.0 ├── default │ ├── Android.bp │ ├── main.cpp │ ├── TVServer.cpp │ └── TVServer.h │ └── device.qiushao.pure.tvserver@1.0-service.rc └── ITVServer.hal 2 directories, 6 files qiushao@qiushao-pc:~/source /android-10/device/qiushao/pure/models/hidl$
6. 自动生成 hal 接口的 Android.bp 这里需要用到 update-makefiles.sh 这个脚本,我们可以从 $(TOP)/hardware/interfaces 里面 copy 一份过来放到 device/qiushao/pure/models/hidl 目录,稍作修改即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #!/bin/bash set -eif [ -z "$ANDROID_BUILD_TOP " ]; then echo "Missing ANDROID_BUILD_TOP env variable. Run 'lunch' first." exit 1 fi source $ANDROID_BUILD_TOP /system/tools/hidl/update-makefiles-helper.shdo_makefiles_update \ "device.qiushao.pure:device/qiushao/pure/models/hidl"
然后在 Android 根目录执行
1 ./device/qiushao/pure/models/hidl/update-makefiles.sh
这个命令生成了 device/qiushao/pure/models/hidl/tvserver/1.0/Android.bp 这个文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 // This file is autogenerated by hidl-gen -Landroidbp. hidl_interface { name: "device.qiushao.pure.tvserver@1.0", root: "device.qiushao.pure", product_specific: true, srcs: [ "ITVServer.hal", ], interfaces: [ "android.hidl.base@1.0", ], gen_java: true, }
我们需要在这个文件最前面添加以下配置才行,不然会编译不过:
1 2 3 4 hidl_package_root { name: "device.qiushao.pure", path: "device/qiushao/pure/models/hidl", }
目前目录结构是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 qiushao@qiushao-pc:~/source /android-10/device/qiushao/pure/models/hidl$ tree . ├── tvserver │ └── 1.0 │ ├── Android.bp │ ├── default │ │ ├── Android.bp │ │ ├── main.cpp │ │ ├── TVServer.cpp │ │ └── TVServer.h │ │ └── device.qiushao.pure.tvserver@1.0-service.rc │ └── ITVServer.hal └── update-makefiles.sh 3 directories, 9 files qiushao@qiushao-pc:~/source /android-10/device/qiushao/pure/models/hidl$
7. 更新 current.txt current.txt 记录了所有 hal 接口的 hash 值,接口有变化时,同时需要更新 current.txt 中的 hash 值 在 interfaces 目录下新建 current.txt 文件,随便写个 hash 值
1 123456 device.qiushao.pure.tvserver@1.0::ITVServer
再执行一遍 update-makefiles.sh,这个时候就会发现提示 hash 值不正确了,同时会给出正确的 hash 值,我们把正确的 hash 值替换到 current.txt 即可。
8. 模块编译 1 mmm device/qiushao/pure/models/hidl/tvserver/1.0
编译完成后,可以发现 $(TARGET_OUT)/vendor/bin/hw 目录下生成了我们的服务 device.qiushao.pure.tvserver@1.0-service
9. 更新 manifest.xml 复制 device/generic/goldfish/manifest.xml 到 device/qiushao/pure/manifest.xml manifest.xml 中加上
1 2 3 4 5 6 7 8 9 <hal format ="hidl" > <name > device.qiushao.pure.tvserver</name > <transport > hwbinder</transport > <version > 1.0</version > <interface > <name > ITVServer</name > <instance > default</instance > </interface > </hal >
pure/product_copy_files.mk 中加上:
1 2 PRODUCT_COPY_FILES += \ device/qiushao/pure/manifest.xml:vendor/manifest.xml
10. 添加到 PRODUCT_PACKAGES 修改 device.mk 增加
1 2 PRODUCT_PACKAGES += \ device.qiushao.pure.tvserver@1.0-service
11. 添加 selinux 规则 系统原生的 selinux 规则在 system/sepolicy 目录下, 我们可以直接修改里面的规则,但不推荐这么做,最好是在 device 目录下添加厂商自己的规则。 在 BoardConfig.mk 里面添加:
1 BOARD_SEPOLICY_DIRS += device/qiushao/pure/sepolicy
然后我们就可以在 pure/sepolicy 目录下添加我们的 selinux 规则了。
新增 tvserver.te
1 2 3 4 5 6 7 8 9 type tvserver, domain; type tvserver_exec, exec_type, vendor_file_type, file_type; hwbinder_use(tvserver); init_daemon_domain(tvserver) add_hwservice(tvserver, tvserver_hwservice) allow tvserver hwservicemanager_prop:file {map read open getattr}; allow tvserver system_file:dir {read open getattr search};
新增 file_contexts
1 /vendor/bin/hw/device.qiushao.pure.tvserver@1.0-service u:object_r:tvserver_exec:s0
新增 hwservice.te
1 type tvserver_hwservice, hwservice_manager_type;
新增 hwservice_contexts
1 device.qiushao.pure.tvserver::ITVServer u:object_r:tvserver_hwservice:s0
然后去编译整个系统, 运行,嘿,我们的服务跑起来了:
1 2 3 130|pure:/ # ps -A | grep qiushao root 1648 1 15420 3864 futex_wait_queue_me 0 S device.qiushao.pure.tvserver@1.0-service pure:/ #
12. 编写 client 调用服务 在 1.0 目录下增加 test 目录,新建 TVServerTest.cpp 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <device/qiushao/pure/tvserver/1.0/ITVServer.h> #include <hidl/Status.h> #include <utils/misc.h> #include <hidl/HidlSupport.h> #include <stdio.h> #include <string> using namespace std; using ::android::hardware::hidl_string; using ::android::sp; using device::qiushao::pure::tvserver::V1_0::ITVServer; int main(){ android::sp<ITVServer> service = ITVServer::getService(); if (service == nullptr){ printf("Failed to get service\n"); return -1; } service->hello("qiushao", [&](hidl_string result){ printf("%s\n", result.c_str()); }); return 0; }
新建 Android.bp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 cc_binary { name: "TVServerTest", vendor: true, srcs: ["TVServerTest.cpp"], shared_libs: [ "liblog", "libhardware", "libhidlbase", "libhidltransport", "libutils", "device.qiushao.pure.tvserver@1.0", ], }
在 device.mk 中加上
1 2 PRODUCT_PACKAGES += \ TVServerTest
然后整编译系统,运行:
1 2 3 pure:/ hello qiushao from TVServer pure:/
成功调用了 hidl 服务。
最终代码目录结构是这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 qiushao@qiushao-pc:~/source/android-10/device/qiushao/pure/models/hidl$ tree . ├── current.txt ├── tvserver │ └── 1.0 │ ├── Android.bp │ ├── default │ │ ├── Android.bp │ │ ├── device.qiushao.pure.tvserver@1.0-service.rc │ │ ├── main.cpp │ │ ├── TVServer.cpp │ │ └── TVServer.h │ ├── ITVServer.hal │ └── test │ ├── Android.bp │ └── TVServerTest.cpp └── update-makefiles.sh 4 directories, 11 files qiushao@qiushao-pc:~/source/android-10/device/qiushao/pure/models/hidl$
经过了这么多的步骤终于完成了一个 hidl 的 hello world。 以后我们只要在这个框架上慢慢添加功能就行了。
网上大部分文章会忽略掉 current.txt, manifest.xml, selinux 及在 device/product 目录下添加模块的这些问题,导致自己跟着那些文章实现的时候,总是遇到各种问题,希望这篇记录能够帮助到想创建 hidl 服务的同学。