1. 环境配置

首先将 AOSP 的源码编译一下,编译步骤详见传送门

编译完成之后,查看:源码根目录 /out/host/linux-x86/bin 中是否有 hidl-gen 工具。

  • 如果有就可以直接配置环境变量

  • 如果没有使用 (在根目录):make hidl-gen 生成 hidl-gen 工具

生成hidl-gen工具.png

生成 hidl-gen 工具.png

配置环境变量:

shell
1
jackou@ubuntu:~$ vim .bashrc 

在末尾添加

export PATH=$PATH:/home/jackou/work_directory/out/host/linux-x86/bin/

2. 新建 HIDL 模块

2.1 新建 hal 接口文件

在 hardware/interfaces 目录新建存放接口的目录

shell
1
mkdir -p hidltest/1.0

创建 hal 文件,名为 IHidlTest.hal

java
1
2
3
4
5
package android.hardware.hidltest@1.0;

interface IHidlTest {
helloWorld(string name) generates (string result);
};

2.2 生成.h 和.cpp 文件

在根目录下利用 hidl-gen 生成对应的服务端代码:

shell
1
2
3
4
PACKAGE=android.hardware.hidltest@1.0
LOC=hardware/interfaces/hidltest/1.0/default/
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
生成bp文件
生成 bp 文件
c++
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//自动生成的HidlTest.h头文件
#ifndef ANDROID_HARDWARE_HIDLTEST_V1_0_HIDLTEST_H
#define ANDROID_HARDWARE_HIDLTEST_V1_0_HIDLTEST_H

#include <android/hardware/hidltest/1.0/IHidlTest.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>

namespace android {
namespace hardware {
namespace hidltest {
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 HidlTest : public IHidlTest {
// Methods from ::android::hardware::hidltest::V1_0::IHidlTest 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
// 如果使用直通模式,一定把下面语句注释删掉
// 此软件包经过 dlopen 处理,且实现使用 HIDL_FETCH_IHidlTest 进行了实例化。
// 参考说明:https://source.android.com/devices/architecture/hidl
extern "C" IHidlTest* HIDL_FETCH_IHidlTest(const char* name);

} // namespace implementation
} // namespace V1_0
} // namespace hidltest
} // namespace hardware
} // namespace android

#endif // ANDROID_HARDWARE_HIDLTEST_V1_0_HIDLTEST_H

c++
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
26
27
28
29
30
31
//自动生成的c++实现
#include "HidlTest.h"

namespace android {
namespace hardware {
namespace hidltest {
namespace V1_0 {
namespace implementation {

// Methods from ::android::hardware::hidltest::V1_0::IHidlTest follow.
Return<void> HidlTest::helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) {
// TODO implement
char buf[100];
::memset(buf,0x00,sizeof(buf));
::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.
IHidlTest* HIDL_FETCH_IHidlTest(const char* /* name */) {
return new HidlTest();
}
//
} // namespace implementation
} // namespace V1_0
} // namespace hidltest
} // namespace hardware
} // namespace android

2.3 更新 bp 文件

使用一下脚本更新 bp 文件,不知道为什么,我使用的 Android P 代码,lunch 10 产品,使用一下脚本始终不能像博友生成 Android.mk 文件,只生成了 Andorid.bp 文件。不过没关系,不影响编译,bp 文件也是可以编译的。

Code
1
./hardware/interfaces/update-makefiles.sh

更新makefile文件.png

更新 makefile 文件.png

使用脚本更新bp文件.png

使用脚本更新 bp 文件.png

shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Android.bp文件
// This file is autogenerated by hidl-gen -Landroidbp.

hidl_interface {
name: "android.hardware.hidltest@1.0",
root: "android.hardware",
vndk: {
enabled: true,
},
srcs: [
"IHidlTest.hal",
],
interfaces: [
"android.hidl.base@1.0",
],
gen_java: true,
}

标签说明:

  • name 需要与 package name 相同,编译的时候会根据需要生成对应的 so 或 jar
  • root 即为与 hidl 对应的 root name
  • interfaces 为编译过程中依赖的接口名称,如 c 中的 shared library
  • types 为模块中所需要的自定义类型
  • 如果有需要的 java 代码可以将 gen_java 设为 true,如果没有(例如 passthrough 模式)需要将这里设为 false。不过一般通过 update_makefiles.sh 就可以自动生成。详细看 Android HIDL 中 hidl-gen 使用

配置完成之后,编译会在 **~/work_directory/out/soong/.intermediates/hardware/interfaces/hidltest/1.0** 生成如下文件:

根目录Android.bp生成文件说明.png

根目录 Android.bp 生成文件说明.png

2.4 采取直通模式

将 HidlTest.h 中的以下注释解开,代码如 2.2 小节

c++
1
// extern "C" IHidlTest* HIDL_FETCH_IHidlTest(const char* name);

将 HidlTest.cpp 中的以下代码注解解开,代码如 2.2 小节

c++
1
2
3
IHidlTest* HIDL_FETCH_IHidlTest(const char* /* name */) {
return new HidlTest();
}

2.5 实现功能

在 HidlTest.cpp 中实现接口的功能,代码见代码如 2.2 小节

c++
1
2
3
4
5
6
7
8
9
Return<void> HidlTest::helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) {
// TODO implement
char buf[100];
::memset(buf,0x00,sizeof(buf));
::snprintf(buf,100,"hello world, %s",name.c_str());
hidl_string result(buf);
_hidl_cb(result);
return Void();
}

ok,现在大功告成; 准备开始编译。

2.6 编译模块

在根目录使用以下命令编译模块

shell
1
mmm hardware/interfaces/hidltest/1.0/default/

编译成功.png

编译成功.png

具有实现的 so 库有了,我们开始构建 binder 通信服务。

2.7 编写 android.hardware.hidltest@1.0-service.rc 文件

在 default 目录创建 android.hardware.hidltest@1.0-service.rc 文件,作为启动文件

shell
1
2
3
4
service hidltest_hal_service /vendor/bin/hw/android.hardware.hidltest@1.0-service
class hal
user system
group system

配置服务的名字为 hidltest_hal_service。

2.8 编写 service 文件

c++
1
2
3
4
5
6
7
8
9
10
// default目录下建立service.cpp
#define LOG_TAG "android.hardware.hidltest@1.0-service"
#include <android/hardware/hidltest/1.0/IHidlTest.h>
#include <hidl/LegacySupport.h>
using android::hardware::hidltest::V1_0::IHidlTest;
using android::hardware::defaultPassthroughServiceImplementation;
int main(){
printf("start hidltest service");
return defaultPassthroughServiceImplementation<IHidlTest>();
}

2.9 在 Android.bp 中添加对服务器的编译:

shell
1
vim hardware/interfaces/hidltest/1.0/default/Android.bp

在 Android.bp 中添加:

shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cc_binary {
name: "android.hardware.hidltest@1.0-service",
relative_install_path: "hw",
proprietary: true,
init_rc: ["android.hardware.hidltest@1.0-service.rc"],
srcs: ["service.cpp"],
shared_libs: [
"liblog",
"libcutils",
"libdl",
"libbase",
"libutils",
"libhardware",
"libhidlbase",
"libhidltransport",
"android.hardware.hidltest@1.0",
],
}

2.10 编写 manifest.xml 文件

为了使客户端能够调用服务端的代码需要在 manifest.xml 中添加服务:在 device/qcom/msm8996/manifest.xml 文件最后添加:

xml
1
2
3
4
5
6
7
8
9
10
<hal format="hidl">
<name>android.hardware.hidltest</name>
<!--由于之前采用的是直通式,所以这里也要用直通式,否则会找不到服务-->
<transport>passthrough</transport>
<version>1.0</version>
<interface>
<name>IHidlTest</name>
<instance>default</instance>
</interface>
</hal>

或者在车机 /vendor/etc/vintf/manifest.xml 中

2.11 再次编译模块

shell
1
mmm hardware/interfaces/hidltest/1.0/default/

最后生成的文件

out/target/product/generic_x86_64/vendor/lib64/hw/android.hardware.hidltest@1.0-impl.so

out/target/product/generic_x86_64/vendor/bin/hw/android.hardware.hidltest@1.0-service

out/target/product/generic_x86_64/system/lib64/vndk-28/android.hardware.hidltest@1.0.so

2.12 编写 C++ 客户端

在 hardware/interfaces/hidltest/1.0 / 建立 test 目录,用于存储 c++ 客户端代码。

创建 HidlTestClient.cpp 文件,并且编写客户端代码

c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <android/hardware/hidltest/1.0/IHidlTest.h>
#include <hidl/Status.h>
#include <hidl/LegacySupport.h>
#include <utils/misc.h>
#include <hidl/HidlSupport.h>
#include <stdio.h>
using ::android::hardware::hidl_string;
using ::android::sp;
using android::hardware::hidltest::V1_0::IHidlTest;

int main(){
android::sp<IHidlTest> service = IHIdlTest::getService();
if (service == nullptr){
printf("Failed to get service\n");
return -1;
}
service->helloWorld("HidlTest", [&](hidl_string result){
printf("%s\n", result.c_str());
});
return 0;
}

2.13 编写客户端编译脚本

在 hardware/interfaces/hidltest/1.0/test 目录下,创建 Android.bp

shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cc_binary {
relative_install_path: "hw",
defaults: ["hidl_defaults"],
name: "hidltest_client",
proprietary: true,
srcs: ["HidlTestClient.cpp"],
shared_libs: [
"liblog",
"libhardware",
"libhidlbase",
"libhidltransport",
"libutils",
"android.hardware.hidltest@1.0",
],
}

客户端代码.png

客户端代码.png

2.14 编译模块

shell
1
mmm hardware/interfaces/hidltest/1.0/
  • out/target/product/generic_x86_64/system/framework/oat/x86_64 / 目录下生成 android.hardware.hidltest-V1.0-java.odex 可执行文件

hidl客户端编译成功.png

hidl 客户端编译成功.png

2.15 编写 Android app 端代码

2.15.1 取的 jar 包

从 2.3 中~/work_directory/out/soong/.intermediates/hardware/interfaces/hidltest/1.0/android.hardware.hidltest-V1.0-java/android_common/combined 目录拿到 Android 端使用的 jar 包。

2.15.2 建立 AS 工程

由于 HIDL 调用需要 java 8,所以在 build.gradle 中加入以下配置

groovy
1
2
3
4
5
6
7
android {
......
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
}
2.15.3 编写代码
java
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class MainActivity extends AppCompatActivity {

private static final String TAG = MainActivity.class.getSimpleName();
private Button mShow;
private TextView mTextShow;
IHidlTest mService;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mTextShow = findViewById(R.id.textView);
mShow = findViewById(R.id.button);

String content;
try {
// 获得服务
mService = IHidlTest.getService();
if (mService == null) {
Log.e(TAG, "service is null");
return;
}
content = mService.helloWorld("hello world!");
mTextShow.setText(content);
} catch (RemoteException e) {
e.printStackTrace();
}

mShow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
String content = mService.helloWorld("hello world!" + System.currentTimeMillis());
mTextShow.setText(content);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
}

编译上面 demo 工程,生成 apk 文件,准备在目标板子上运行。

至此,所有准备工作已经做完了。

2.16 将目标文件烧写到车机

需要准备的文件

base path: ~/work_directory/out/target/product/generic_x86_64

abstract path:

车机中的 manifest.xml 修改之后 push 到原来的目录

2.16.1 push 路径说明

android.hardware.hidltest@1.0-impl.so –> /vendor/lib64/hw

android.hardware.hidltest@1.0.so –> /system/lib64

hidltest_client –> /vendor/bin/hw

android.hardware.hidltest@1.0-service –> /vendor/bin/hw

manifest.xml –> /vendor/etc/vintf

2.16.2 安装 apk

adb install -r / 本地路径 /app-debug.apk

2.17 启动服务,测试功能

1. 启动 Hidl 模块服务
./vendor/bin/hw/android.hardware.hidltest@1.0-service

2. 启动客户端
./vendor/bin/hw/hidltest_client

3. 运行 apk 看界面现象

Notice:HAL 回调实现下一小节叙述,或者见参考文献 3

参考文献

1. Android HIDL 实例

2. HIDL 实战笔记

3. Android HIDL 学习(3) —- 注册回调