1.环境配置

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

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

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

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

生成hidl-gen工具.png

配置环境变量:

1
jackou@ubuntu:~$ vim .bashrc 

在末尾添加

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

2.新建HIDL模块

2.1 新建hal接口文件

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

1
mkdir -p hidltest/1.0

创建hal文件,名为IHidlTest.hal

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生成对应的服务端代码:

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文件
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

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文件也是可以编译的。

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

更新makefile文件.png

使用脚本更新bp文件.png

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

2.4 采取直通模式

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

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

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

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

2.5 实现功能

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

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 编译模块

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

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

编译成功.png

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

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

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

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文件

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中添加对服务器的编译:

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

在Android.bp中添加:

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文件最后添加:

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 再次编译模块

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文件,并且编写客户端代码

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

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

2.14 编译模块

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

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中加入以下配置

1
2
3
4
5
6
7
android {
......
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
}
2.15.3 编写代码
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) —- 注册回调