需求是这样的,system_server有一个LocalService,需要向一个具有root权限的native进程进行消息传递(下发请求指令,获取状态信息等)
首先附上官方介绍:https://source.android.com/devices/architecture/hidl
此功能最早使用socket实现(参考lmkd的实现),但是由于如下几点原因,因此决定使用HIDL重构:
1、socket传输效率低,开销大,通信过程中至少有两次拷贝过程;
2、由于1的原因,从system_server调用方法主动获取native进程状态时效性比binder通信差;
3、由于我们使用的SoC平台普遍性能较差,因此为了保证整机性能,需要尽可能减少system_server的压力;
4、符合Android设计规范,实现更优雅……(汗)
同时考虑到该需求有如下几点特点:
1、通信两侧语言不同(JAVA - C/C++),虽然可以手动添加一层JNI来实现语言对等,但是能偷懒就偷懒呗;
2、改动需要符合CTS/VTS认证,不能出现neverallow的sepolicy,并且有利于后期推送OS升级的FOTA;
3、native部分尽可能模块化,甚至可以闭源;
因此使用binderized HIDL看上去是一个比较好的选择;
mkdir -p hardware/interfaces/example/1.0/
touch hardware/interfaces/example/1.0/IExample.hal
emacs -nw hardware/interfaces/example/1.0/IExample.hal
由于是测试用,就写个简单的Hello World:
package [email protected];
interface IExample {
helloWorld(string in) generates (string out);
};
首先生成.hal文件对应的Android.bp:
hidl-gen -Landroidbp -rvendor.zsui.hardware:hardware/interfaces/ -randroid.hidl:system/libhidl/transport [email protected]
然后根据.hal文件生成实现类与头文件:
hidl-gen -o hardware/interfaces/example/1.0/default/ -Landroidbp-impl -rvendor.zsui.hardware:hardware/interfaces/ -randroid.hidl:system/libhidl/transport [email protected]
最后生成实现类的Android.bp:
hidl-gen -o hardware/interfaces/example/1.0/default/ -Lc++-impl -rvendor.zsui.hardware:hardware/interfaces/ -randroid.hidl:system/libhidl/transport [email protected]
若无报错,最后目录结构应该是这样的:
hardware/interfaces/example/
└── 1.0
├── Android.bp
├── default
│ ├── Android.bp
│ ├── Example.cpp
│ └── Example.h
└── IExample.hal
简单解读一下:
1.0目录下的Android.bp用于解释HIDL接口相关信息;
default目录下的Android.bp用于描述HIDL接口实际实现的相关信息;
不太严谨地用AIDL类比一下,1.0目录下的.hal文件可以类比为.aidl文件,而default目录下的.cpp和.h文件就是这个接口的具体实现;
此例中的两个目标模板就创建完成了,并且生成了对应的Android.bp,照理话说此时两个模块已经可以进行编译了,但是只要你试一下,就会发现如下报错:
$ make [email protected]
error: hardware/interfaces/example/1.0/Android.bp:9:15: module "[email protected]_interface": interfaces: Cannot find package root specification for package root 'vendor.zsui.hardware' needed for module '[email protected]'. Either this is a mispelling of the package root, or a new hidl_package_root module needs to be added. For example, you can fix this error by adding the following to /Android.bp:
hidl_package_root {
name: "vendor.zsui.hardware",
path: "",
}
This corresponds to the "-rvendor.zsui.hardware:" option that would be passed into hidl-gen.
ninja: build stopped: subcommand failed.
19:34:12 soong bootstrap failed with: exit status 1
这个是因为之前的参数-rvendor.zsui.hardware:hardware/interfaces/指定hardware/interfaces/为包名vendor.zsui.hardware的package root,但是该路径下并没有定义vendor.zsui.hardware包名的package root;
解决方案有三个,鉴于Google架构设计考虑,建议采用c:
a. 修改包名为hardware/interfaces/Android.bp中定义的hidl_package_root的name,即"android.hardware";
b. 在hardware/interfaces/Android.bp中添加name为的hidl_package_root;
hidl_package_root {
name: "vendor.zsui.hardware",
path: "vendor/zsui/proprietary/hardware/interfaces/",
}
c. 使用其他目录结构,并手动声明hidl_package_root,例如:
mkdir -p vendor/zsui/proprietary/hardware/interfaces/
mv hardware/interfaces/example/ vendor/zsui/proprietary/hardware/interfaces/
touch vendor/zsui/proprietary/hardware/interfaces/Android.bp
emacs -nw vendor/zsui/proprietary/hardware/interfaces/Android.bp
在Android.bp中添加:
hidl_package_root {
name: "vendor.zsui.hardware",
path: "vendor/zsui/proprietary/hardware/interfaces/",
}
然后编译就可以成功了;
只是里面没有实现,也不会有任何服务跑起来,只是两个.so库而已;
out/target/product/xxx/system/lib(64)/[email protected]
out/target/product/xxx/vendor/lib(64)/hw/[email protected]
上一步已经说了,default目录下就是我们接口的具体实现,因此我们只需要把实现代码完成:
完善Example.cpp:
#include "Example.h"
namespace vendor {
namespace zsui {
namespace hardware {
namespace example {
namespace V1_0 {
namespace implementation {
// Methods from ::vendor::zsui::hardware::example::V1_0::IExample follow.
Return Example::helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) {
// TODO implement
char buf[100];
memset(buf, 0, 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.
//IExample* HIDL_FETCH_IExample(const char* /* name */) {
//return new Example();
//}
//
} // namespace implementation
} // namespace V1_0
} // namespace example
} // namespace hardware
} // namespace zsui
} // namespace vendor
示例用,就返回了个字符串拼接;
现在实现和接口都有了,也都可以编译了,问题就剩下:谁是服务端,谁是客户端了;
由于使用binderized HIDL,因此我们可以直接基于impl创建服务端:
a. 创建两个文件
touch vendor/zsui/proprietary/hardware/interfaces/example/1.0/default/service.cpp
touch vendor/zsui/proprietary/hardware/interfaces/example/1.0/default/[email protected]
其中service.cpp为服务入口,[email protected]为启动相关注册信息;
b. 在service.cpp中添加入口函数并注册服务:
#include
#define LOG_TAG "Example-hal"
#include
#include "Example.h"
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using vendor::zsui::hardware::example::V1_0::IExample;
using vendor::zsui::hardware::example::V1_0::implementation::Example;
int main() {
configureRpcThreadpool(2, true /*callerWillJoin*/);
android::sp service = new Example();
if(service->registerAsService() != android::OK){
ALOGE("hal registration FAILED");
} else {
ALOGV("hal ready");
joinRpcThreadpool();
}
ALOGW("hal exiting");
return 0;
}
c. 由于我们需要编译出可执行的二进制文件,而不是一个.so库,因此需要修改Android.bp:(注意//Modify与//Add字段)
cc_binary { //Modify
// 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]", //Modify
relative_install_path: "hw",
// FIXME: this should be 'vendor: true' for modules that will eventually be
// on AOSP.
proprietary: true,
defaults: ["hidl_defaults"], //Add
srcs: [
"Example.cpp",
"service.cpp", //Add
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"liblog", //Add
"[email protected]",
],
init_rc: ["[email protected]"], //Add
}
d. 添加对应服务启动信息到[email protected]:
service example-hal-1-0 /vendor/bin/hw/[email protected]
class hal
user root
group system
writepid /dev/cpuset/system-background/tasks
至此,编译下[email protected],应该是可以编译过的;
服务端的创建差不多就到这里了,下一篇会着重讲解sepolicy的设置、以及JAVA端调用的方式;
文笔有限,若有谬误,还请指出;
感谢!