Android HIDL 实例

本文基于Android P,通过HIDL中的HelloWorld实例分析,介绍HIDL使用。涵盖确定环境、创建核心hal、编译文件、创建服务与实现、添加rc文件、启动服务及实现客户端等步骤,还提及相关编程规范、数据类型等知识。

前言:

Android HIDL 详解 一文得知HIDL 使用passthrough 模式,为了与原来的HAL 版本兼容。除了passthrough 模式,还有一种binderized模式。本文通过HIDL 中的Helloworld 进行实例分析,进一步了解HIDL 使用。

本文source code 基于Android P。

Step 1 确定环境

Android HIDL 编程规范一文中得知需要确定HIDL 的目录结构和软件包名称
在这里插入图片描述

下表列出了软件包前缀和位置:

软件包前缀位置
android.hardware.*hardware/interfaces/*
android.frameworks.*frameworks/hardware/interfaces/*
android.system.*system/hardware/interfaces/*
android.hidl.*system/libhidl/transport/*

如上表,hardware/interfaces 目录一般放的是源生hal 相关的接口,如下图所示:

除了上表中提到这几个源生的目录,一般OEM 的hal 都是在vendor下,详细看Android HIDL 接口和软件包使用 一文。

本文中Helloworld 位于hardware/interfaces中,所以目录路径为hardware/interfaces/helloworld/1.0。

 

确定目录后就是确定软件包名,根据Android HIDL 接口和软件包使用 一文得知在 hardware/interfaces目录下的软件包package 为android.hardware,所以package name 应该是 android.hardware.helloworld@1.0。

 

Step 2 创建核心hal

确定HIDL 模块的路径和包名之后,需要创建一个hal 文件,这个文件包含了client 需要调用HAL 的入口api,这里命名为IHelloWorld.hal,代码如下:


 
  1. package android.hardware.helloworld@ 1.0;
  2. interface IHelloWorld {
  3. justTest( string name) generates ( string result, HelloTest value);
  4. justTest1(HelloTest name);
  5. };

 

其中HelloTest 是用户自定义类型,如果是模块公共的类型,可以定义在types.hal 中,例如这里:


 
  1. package android.hardware. helloworld@ 1.0;
  2. enum HelloTest : uint8_t {
  3. V_TEST1 = 0,
  4. V_TEST2 = 1,
  5. };

详细代码格式和数据类型可以查看:Android HIDL 编程规范 和 Android HIDL 中的数据类型

 

Step 3 创建编译的Android.bp

根据Step 2中的hal 文件利用hidl-gen(详细看 Android HIDL 中 hidl-gen使用) 生成对应的Android.bp:


 
  1. // This file is autogenerated by hidl-gen -Landroidbp.
  2. hidl_interface {
  3. name: "android.hardware.helloworld@1.0",
  4. root: "android.hardware",
  5. vndk: {
  6. enabled: true,
  7. },
  8. srcs: [
  9. "types.hal",
  10. "IHelloWorld.hal",
  11. ],
  12. interfaces: [
  13. "android.hidl.base@1.0",
  14. ],
  15. types: [
  16. "HelloTest",
  17. ],
  18. gen_java: true,
  19. }

注意:

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

 

Step 4 编译HIDL

在对应的hidl 目录下mm 编译或者在根目录下make PQName 即可,最终在out/soong/.interfaces/hardware/interfaces/下会生成对应模块的obj,这里目录决定为hardware/interfaces其实就是跟上面root 设定有关系。详细如下图:

其中:

  • android.hardware.helloworld@1.0 就是模块对应的库文件;
  • android.hardware.helloworld@1.0_genc++ 为生成对应的C++临时文件,在使用的时候都是链接到这里;

 
  1. -rw-rw-r-- 1 shift shift 26745 1月 22 14:20 HelloWorldAll.cpp
  2. -rw-rw-r-- 1 shift shift 281 1月 22 14:20 HelloWorldAll.cpp.d
  3. -rw-rw-r-- 1 shift shift 874 1月 22 14:20 types.cpp
  • android.hardware.helloworld@1.0_genc++_headers 为生成的C++ 所需的头文件;

 
  1. -rw-rw-r-- 1 shift shift 2264 1月 22 14:20 BnHwHelloWorld.h
  2. -rw-rw-r-- 1 shift shift 3008 1月 22 14:20 BpHwHelloWorld.h
  3. -rw-rw-r-- 1 shift shift 17785 1月 22 14:20 BsHelloWorld.h
  4. -rw-rw-r-- 1 shift shift 519 1月 22 14:20 hwtypes.h
  5. -rw-rw-r-- 1 shift shift 5296 1月 22 14:20 IHelloWorld.h
  6. -rw-rw-r-- 1 shift shift 279 1月 22 14:20 IHelloWorld.h.d
  7. -rw-rw-r-- 1 shift shift 684 1月 22 14:20 IHwHelloWorld.h
  8. -rw-rw-r-- 1 shift shift 4520 1月 22 14:20 types.h
  • android.hardware.helloworld-V1.0-java 为java 代码所使用的java 库文件;
  • android.hardware.helloworld-V1.0-java_gen_java 为java 代码所使用的java 文件

 
  1. -rw-rw-r-- 1 shift shift 922 1月 22 14:20 HelloTest.java
  2. -rw-rw-r-- 1 shift shift 24875 1月 22 14:20 IHelloWorld.java
  3. -rw-rw-r-- 1 shift shift 287 1月 22 14:20 IHelloWorld.java.d

 

Step 5 创建service 和impl

其实当IHelloworld.hal 创建完成就可以创建对应的HIDL 实现代码(impl 和service),而hidl-gen 也提供了默认生成的方式,详细看Android HIDL 中 hidl-gen使用,最终生成的文件为:


 
  1. -rw-rw-r-- 1 shift shift 973 1月 17 20:05 Android.bp
  2. -rw-rw-r-- 1 shift shift 605 1月 17 20:05 HelloWorld.cpp
  3. -rw-rw-r-- 1 shift shift 1159 1月 17 20:05 HelloWorld.h

先来看下HelloWorld.h:


 
  1. #include <android/hardware/helloworld/1.0/IHelloWorld.h>
  2. #include <hidl/MQDescriptor.h>
  3. #include <hidl/Status.h>
  4. namespace android {
  5. namespace hardware {
  6. namespace helloworld {
  7. namespace V1_0 {
  8. namespace implementation {
  9. using ::android::hardware::hidl_array;
  10. using ::android::hardware::hidl_memory;
  11. using ::android::hardware::hidl_string;
  12. using ::android::hardware::hidl_vec;
  13. using ::android::hardware::Return;
  14. using ::android::hardware::Void;
  15. using ::android::sp;
  16. struct HelloWorld : public IHelloWorld {
  17. // Methods from ::android::hardware::helloworld::V1_0::IHelloWorld follow.
  18. Return< void> justTest( const hidl_string& name, justTest_cb _hidl_cb) override;
  19. Return< void> justTest1(::android::hardware::helloworld::V1_0::HelloTest name) override;
  20. // Methods from ::android::hidl::base::V1_0::IBase follow.
  21. };
  22. // FIXME: most likely delete, this is only for passthrough implementations
  23. // extern "C" IHelloWorld* HIDL_FETCH_IHelloWorld(const char* name);
  24. } // namespace implementation
  25. } // namespace V1_0
  26. } // namespace helloworld
  27. } // namespace hardware
  28. } // namespace android

如果是实用passthrough 模式,则需要打开HIDL_FETCH_IHelloWorld() 函数的注释,并且在-impl 的C++文件中实现,例如这里如果使用passthrough 模式,需要在HelloWorld.cpp 中实现该函数,详细可以看nfc 或tv 等模块中实现。

 

再来看下HelloWorld.cpp:


 
  1. #define LOG_TAG "HelloWorldImpl"
  2. #include <log/log.h>
  3. #include "HelloWorld.h"
  4. namespace android {
  5. namespace hardware {
  6. namespace helloworld {
  7. namespace V1_0 {
  8. namespace implementation {
  9. // Methods from ::android::hardware::helloworld::V1_0::IHelloWorld follow.
  10. Return< void> HelloWorld::justTest( const hidl_string& name, justTest_cb _hidl_cb) {
  11. ALOGD( "justTest, name = %s", name.c_str());
  12. _hidl_cb(name, HelloTest::V_TEST2);
  13. ALOGD( "justTest end.");
  14. return Void();
  15. }
  16. Return< void> HelloWorld::justTest1(::android::hardware::helloworld::V1_0::HelloTest name) {
  17. ALOGD( "justTest1, name = %hhu", name);
  18. return Void();
  19. }
  20. // Methods from ::android::hidl::base::V1_0::IBase follow.
  21. //IHelloWorld* HIDL_FETCH_IHelloWorld(const char* /* name */) {
  22. //return new HelloWorld();
  23. //}
  24. //
  25. } // namespace implementation
  26. } // namespace V1_0
  27. } // namespace helloworld
  28. } // namespace hardware
  29. } // namespace android

这里就是实现的地方,其中使用passthrough 的时候需要使能HIDL_FETCH_IHelloWorld() 函数。

关于HIDL 相关的数据类型详细看:Android HIDL 中的数据类型

 

再来看下service.cpp:


 
  1. #define LOG_TAG "android.hardware.helloworld@1.0-service"
  2. #include <android/hardware/helloworld/1.0/IHelloWorld.h>
  3. #include <hidl/LegacySupport.h>
  4. #include "HelloWorld.h"
  5. // Generated HIDL files
  6. using android::hardware::helloworld::V1_0::IHelloWorld;
  7. using android::hardware::helloworld::V1_0::implementation::HelloWorld;
  8. using android::hardware::defaultPassthroughServiceImplementation;
  9. using android::hardware::configureRpcThreadpool;
  10. using android::hardware::joinRpcThreadpool;
  11. int main() {
  12. #if 0
  13. return defaultPassthroughServiceImplementation<IHelloWorld>();
  14. #else
  15. sp<IHelloWorld> service = new HelloWorld();
  16. configureRpcThreadpool( 1, true /*callerWillJoin*/);
  17. if(android::OK != service->registerAsService())
  18. return 1;
  19. joinRpcThreadpool();
  20. #endif
  21. }

 

Android.bp 是为了编译HIDL 实现部分的代码生成的默认编译文件(详细看Android HIDL 中 hidl-gen使用),可以根据实际的情况修改:


 
  1. cc_library_shared {
  2. name: "android.hardware.helloworld@1.0-impl",
  3. relative_install_path: "hw",
  4. proprietary: true,
  5. srcs: [
  6. "HelloWorld.cpp",
  7. ],
  8. shared_libs: [
  9. "libhidlbase",
  10. "libhidltransport",
  11. "libutils",
  12. "android.hardware.helloworld@1.0",
  13. ],
  14. }

如果其他模块需要so 则需要share lib,如果不需要刻意直接编译service,如下:


 
  1. cc_binary {
  2. name: "android.hardware.helloworld@1.0-service",
  3. defaults: [ "hidl_defaults"],
  4. relative_install_path: "hw",
  5. vendor: true,
  6. init_rc: [ "android.hardware.helloworld@1.0-service.rc"],
  7. srcs: [
  8. "HelloWorld.cpp",
  9. "service.cpp"
  10. ],
  11. shared_libs: [
  12. "liblog",
  13. "libhidlbase",
  14. "libhidltransport",
  15. "libutils",
  16. "libhardware",
  17. "android.hardware.helloworld@1.0",
  18. ],
  19. }

注意:

  • name:为变成生成的库文件名称,-impl 为实现的库文件,-service为服务端的库文件;
  • init_rc:指定启动service 的rc 名称;
  • relative_install_path:为生成库文件路径,通常与proprietary 和vendor 属性配套,一般都设为hw;
  • proprietary:标记默认生成路径,设为true 代表为系统默认路径(system/lib64下),通常与relative_install_path 属性配套,缺省时默认为system/lib64 下;
  • vendor:与proprietary 相同,设为true代表路径在vendor 下,默认OEM 都会设置在vendor 下;

 

Step 6 添加rc 文件

在实现了serivce 和impl 代码后需要添加rc 文件,文件名为android.hardware.helloworld@1.0-service.rc:


 
  1. service helloworld-hal- 1- 0 /vendor/bin/hw/android.hardware.helloworld@1. 0-service
  2. class hal
  3. user system
  4. group system

 

Step 7 启动service

需要注意的是,在应用起来之前需要使能service,一般有两种方式,一个是通过rc 中的service name,直接start;另外一种是通过selinux 中添加te 文件,设置domain 信息。对于selinux 配置,这里暂不分析,详细看 SELINUX 中文章。

 

Step 8 实现client 端

App 其他的代码这里不做展示,主要来看调用的地方:


 
  1. private void test() {
  2. IHelloWorld service = null;
  3. try {
  4. service = IHelloWorld.getService( true);
  5. } catch (RemoteException e) {
  6. // TODO Auto-generated catch block
  7. e.printStackTrace();
  8. }
  9. if (service == null) {
  10. Log.e(TAG, "test failed, service is null...");
  11. return;
  12. }
  13. try {
  14. service.justTest1(( byte) 123);
  15. } catch (RemoteException e) {
  16. // TODO Auto-generated catch block
  17. e.printStackTrace();
  18. }
  19. }

Android.mk 为:


 
  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_CERTIFICATE := platform
  4. LOCAL_MODULE_TAGS := optional
  5. LOCAL_SRC_FILES := $(call all-java-files-under, src)
  6. LOCAL_PACKAGE_NAME := TestHIDLClient
  7. #LOCAL_PROGUARD_FLAG_FILES := proguard.flags
  8. LOCAL_PRIVATE_PLATFORM_APIS := true
  9. LOCAL_STATIC_JAVA_LIBRARIES := \
  10. android.hardware.helloworld-V1.0-java
  11. include $(BUILD_PACKAGE)

 

打印log 如下:


 
  1. 01-23 13:57:20.424 6498 6498 I android_os_HwBinder: HwBinder: Starting thread pool for default::android.hardware.helloworld@1.0::IHelloWorld
  2. 01-23 13:57:20.424 572 3344 D audio_hw_primary: start_output_stream: enter: stream(0xedde7000)usecase(1: low-latency-playback) devices(0x2)
  3. 01-23 13:57:20.424 572 3344 E audio_hw_extn: audio_extn_perf_lock_acquire: Failed to acquire perf lock, err: -1
  4. 01 -23 13: 57: 20.424 572 3344 D audio_hw_primary: select_devices for use case ( low-latency-playback)
  5. 01 -23 13: 57: 20.424 572 3344 D audio_hw_primary: select_devices: out_snd_device( 2: speaker) in_snd_device( 0: )
  6. 01 -23 13: 57: 20.424 572 3344 I msm8916_platform: platform_check_and_set_codec_backend_cfg:becf: afe: bitwidth 16, samplerate 48000 channels 2, backend_idx 0 usecase = 1 device (speaker)
  7. 01 -23 13: 57: 20.424 572 3344 D msm8916_platform: platform_split_snd_device: snd_device( 2) num devices( 1) new_snd_devices( 0)
  8. 01 -23 13: 57: 20.424 6607 6607 D HelloWorldImpl: justTest1, name = 123

 

 

 

 

 

相关文章:

Android Treble 简介

Android HIDL 详解

Android HIDL 中 hidl-gen使用

Android HIDL 编程规范

Android HIDL 接口和软件包使用

Android HIDL 中的函数

Android HIDL 中的数据类型

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值