摘要:本节主要来讲解Android10.0 Native层的HIDL服务创建和Native层的Client验证
阅读本文大约需要花费18分钟。
文章首发微信公众号:IngresGe
专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢!
[Android取经之路] 的源码都基于Android-Q(10.0) 进行分析
[Android取经之路] 系列文章:
《系统启动篇》
《日志系统篇》
《Binder通信原理》:
《HwBinder通信原理》
为了和Android原生代码进行解耦,我在vendor的仓库中创建了一个ingres/interfaces的文件夹,hidl的相关实例都会放到这个目录下实现
我们接下来准备写一个Native的hal的服务程序,client端为一个Native进程。
命令:
mkdir -p vendor/ingres/interfaces/demo/1.0/default
命令:
vim vendor/ingres/interfaces/demo/1.0/IDemo.hal
写一个接口为getHelloString,传入参数类型为string,返回值generates 也为string类型
code:
package [email protected];
interface IDemo {
getHelloString(string name) generates (string result);
};
下面的hidl_package_root 用来指向hal的目录,hidl编译时,需要用到该变量
内容如下:
subdirs = [
"*"
]
hidl_package_root {
name: "vendor.ingres.demo",
path: "vendor/ingres/interfaces/demo",
}
命令:
./build.sh xxx -m hidl-gen
命令:
vim vendor/ingres/interfaces/demo/update-all.sh
下面这个shell脚本,会生成4个文件:
hal文件对应的Android.bp
hal文件对应的hash--current.txt //哈希是一种旨在防止意外更改接口并确保接口更改经过全面审查的机制
default中demo的代码--Demo.cpp、Demo.
default中demo对应代码的Android.bp
使用hidl-gen工具生成hal的Android.bp、current.txt 、代码和对应的Android.bp
Code:
#!/bin/bash
HAL_PACKAGES=(
"[email protected]"
)
HAL_PACKAGE_ROOT=vendor.ingres.demo
HAL_PATH_ROOT=vendor/ingres/interfaces/demo
HAL_OUTPUT_CODE_PATH=vendor/ingres/interfaces/demo/1.0/default/
HASH_FILE=$HAL_PATH_ROOT/current.txt
for pkg in "${HAL_PACKAGES[@]}"
do
echo "Generating hash for $pkg interface"
echo "" >> $HASH_FILE
hidl-gen -L hash -r $HAL_PACKAGE_ROOT:$HAL_PATH_ROOT -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport $pkg >> $HASH_FILE
echo "Updating $pkg Android.bp"
hidl-gen -L androidbp -r $HAL_PACKAGE_ROOT:$HAL_PATH_ROOT -r android.hidl:system/libhidl/transport $pkg
echo "Updating $pkg code's Android.bp"
hidl-gen -o $HAL_OUTPUT_CODE_PATH -Landroidbp-impl -r $HAL_PACKAGE_ROOT:$HAL_PATH_ROOT -randroid.hidl:system/libhidl/transport $pkg
echo "Updating $pkg code"
hidl-gen -o $HAL_OUTPUT_CODE_PATH -Lc++-impl -r $HAL_PACKAGE_ROOT:$HAL_PATH_ROOT -randroid.hidl:system/libhidl/transport $pkg
done
此时的目录层级结构:
demo
├─1.0
│ └─ITestHal.hal
│ └─default
├─update-all.sh
├─Android.bp
命令:
./hardware/interfaces/demo/update-all.sh
命令执行后,会在hardware/interfaces/demo/1.0中生成Android.bp ,并在hardware/interfaces/demo/1.0/default 中生成源码:Demo.cpp 和Demo.h
此时的层级结构:
demo
├─1.0
│ └─ITestHal.hal
│ └─Android.bp
│ └─default
│ └─Demo.h
│ └─Demo.cpp
│ └─Android.bp
├─update-all.sh
├─Android.bp
命令:
mmm /hardware/interfaces/demo/1.0
生成文件:
1)Android 的jar包,供JAVA层使用
\product\framework\vendor.ingres.demo-V1.0-java.jar
vendor.ingres.demo-V1.0-java-shallow.jar
2)系统库so,供Native层调用- C++
\product\lib\[email protected]
\product\lib\[email protected]
\product\lib\[email protected]
\product\lib\[email protected]
\product\lib64\[email protected]
\product\lib64\[email protected]
\product\lib64\[email protected]
\product\lib64\[email protected]
\vendor\lib\[email protected]
\vendor\lib\[email protected]
\vendor\lib64\[email protected]
\vendor\lib64\[email protected]
Android规定,在Android8.0之后,vendor扩展的HAL,都要使用绑定式HAL,因此我们这里采用绑定式的HAL执行。
由[2.5.2]的脚本自动生成,不需要做特殊处理
// FIXME: your file license if you have one
#pragma once
#include
#include
#include
namespace vendor {
namespace ingres {
namespace demo {
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 Demo : public IDemo {
// Methods from ::vendor::ingres::demo::V1_0::IDemo follow.
Return getHelloString(const hidl_string& name, getHelloString_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" IDemo* HIDL_FETCH_IDemo(const char* name);
} // namespace implementation
} // namespace V1_0
} // namespace demo
} // namespace ingres
} // namespace vendor
由[2.5.2]的脚本自动生成,不需要做特殊处理
// FIXME: your file license if you have one
#include "Demo.h"
namespace vendor {
namespace ingres {
namespace demo {
namespace V1_0 {
namespace implementation {
// Methods from ::vendor::ingres::demo::V1_0::IDemo follow.
Return Demo::getHelloString(const hidl_string& name, getHelloString_cb _hidl_cb) {
// TODO implement
char buf[100];
::memset(buf, 0x00, 100);
::snprintf(buf, 100, "Hello , %s", name.c_str());
hidl_string result(buf);
_hidl_cb(result);
return Void();
}
// Methods from ::android::hidl::base::V1_0::IBase follow.
//IDemo* HIDL_FETCH_IDemo(const char* /* name */) {
//return new Demo();
//}
//
} // namespace implementation
} // namespace V1_0
} // namespace demo
} // namespace ingres
} // namespace vendor
#include
#include
#include
#include
#include "Demo.h"
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using vendor::ingres::demo::V1_0::implementation::Demo;
int main() {
//1.和"dev/hwbinder" 进行通信,设置最大的线程个数为4
configureRpcThreadpool(4, true);
Demo demo;
//2.注册服务
auto status = demo.registerAsService();
CHECK_EQ(status, android::OK) << "Failed to register demo HAL implementation";
//3.把当前线程加入到线程池
joinRpcThreadpool();
return 0; // joinRpcThreadpool shouldn't exit
}
命令:
vim /hardware/interfaces/demo/1.0/default/[email protected]
填充内容:
service vendor_ingres_demo /vendor/bin/hw/[email protected]
class hal
user system
group system
增加一个进程的编译
cc_binary {
name: "[email protected]",
relative_install_path: "hw",
defaults: ["hidl_defaults"],
proprietary: true,
init_rc: ["[email protected]"],
srcs: [
"Service.cpp",
],
shared_libs: [
"libbase",
"liblog",
"libdl",
"libutils",
"libhardware",
"libhidlbase",
"libhidltransport",
"[email protected]",
"[email protected]",
],
}
为了让服务可以被客户端访问到,需要添加manifest
命令:vim /hardware/interfaces/demo/1.0/default/manifest.xml
填充内容:
vendor.ingres.demo
hwbinder
1.0
IDemo
default
创建一个 moduleconfig.mk ,把manifest.xml的内容加入到系统的manifext.xml
命令:
vim /hardware/interfaces/demo/1.0/default/moduleconfig.mk
填充内容:
$(eval LOCAL_MK_PATH := $(lastword $(MAKEFILE_LIST)))
$(eval DEVICE_MANIFEST_FILE += $(dir $(LOCAL_MK_PATH))manifest.xml)
$(warning DEVICE_MANIFEST_FILE = $(DEVICE_MANIFEST_FILE))
整个系统版本编译时,最终可以在 /vendor/etc/vintf/manifest.xml中,看到我们这里添加的manifest.xml里面的内容
命令:
mmm /vendor/ingres/interfaces/demo/1.0/default
生成文件:
1)服务进程:
\vendor\bin\hw\[email protected]
2)rc文件:
vendor\etc\init\[email protected]
3)implement库
\vendor\lib64\hw\[email protected]
Client 层级目录:
hal_demo
│
cpp
├─hal_demo_test.cpp
└─Android.bp
命令:
mkdir -p vendor/ingres/hal_demo/cpp/
vim vendor/ingres/hal_demo/cpp/hal_demo_test.cpp
配置源码:
#include
#include
#include
#include
using vendor::ingres::demo::V1_0::IDemo;
using android::sp;
using android::hardware::hidl_string;
int main() {
//1.获取到Demo的服务对象
android::sp service = IDemo::getService();
if(service == nullptr) {
printf("Failed to get service\n");
return -1;
}
//2.通过服务对象,获取服务的getHelloString() hidl接口实现
service->getHelloString("IngresGe", [&](hidl_string result) {
printf("%s\n", result.c_str());
});
return 0;
}
命令:
vim vendor/ingres/hal_demo/cpp/Android.bp
填充内容:
cc_binary {
name: "hal_demo_test",
relative_install_path: "hw",
defaults: ["hidl_defaults"],
proprietary: true,
srcs: [
"hal_demo_test.cpp",
],
shared_libs: [
"libbase",
"liblog",
"libdl",
"libutils",
"libhardware",
"libhidlbase",
"libhidltransport",
"[email protected]",
],
}
命令:
mmm vendor/ingres/hal_demo/cpp/
生成文件:
out\target\product\xxx\vendor\bin\hw\hal_demo_test
由于我是单编模块,因此manifest.xml没有重新生成,需要手动把vendor/etc/vintf/ 中的manifest.xml pull到本地,增加如下内容,再push到vendor/etc/vintf/中。
manifest.xml新增内容:
vendor.ingres.demo
hwbinder
1.0
IDemo
default
@1.0::IDemo/demo
push 以下文件:
\vendor\bin\hw\hal_demo_test
\product\lib64\[email protected]
\vendor\lib64\[email protected]
\vendor\bin\hw\[email protected]
\vendor\etc\init\[email protected]
\vendor\lib64\hw\[email protected]
服务端执行:
客户端执行:
输出结果:
我的微信公众号:IngresGe