前言:
從Android HIDL 詳解 一文得知HIDL 使用passthrough 模式,為了與原來的HAL 版本相容。除了passthrough 模式,還有一種binderized模式。本文通過HIDL 中的Helloworld 進行執行個體分析,進一步了解HIDL 使用。
本文source code 基于Android P。
Step 1 确定環境
Android HIDL 程式設計規範一文中得知需要确定HIDL 的目錄結構和軟體包名稱
下表列出了軟體包字首和位置:
軟體包字首 | 位置 |
---|---|
| |
| |
| |
| |
如上表,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 應該是 [email protected]。
Step 2 建立核心hal
确定HIDL 子產品的路徑和包名之後,需要建立一個hal 檔案,這個檔案包含了client 需要調用HAL 的入口api,這裡命名為IHelloWorld.hal,代碼如下:
- package android.hardware.helloworld@ ;
- interface IHelloWorld {
- justTest( string name) generates ( string result, HelloTest value);
- justTest1(HelloTest name);
- };
其中HelloTest 是使用者自定義類型,如果是子產品公共的類型,可以定義在types.hal 中,例如這裡:
- package android.hardware. helloworld@ ;
- enum HelloTest : uint8_t {
- V_TEST1 = ,
- V_TEST2 = ,
- };
詳細代碼格式和資料類型可以檢視:Android HIDL 程式設計規範 和 Android HIDL 中的資料類型。
Step 3 建立編譯的Android.bp
根據Step 2中的hal 檔案利用hidl-gen(詳細看 Android HIDL 中 hidl-gen使用) 生成對應的Android.bp:
- // This file is autogenerated by hidl-gen -Landroidbp.
- hidl_interface {
- name: "[email protected]",
- root: "android.hardware",
- vndk: {
- enabled: true,
- },
- srcs: [
- "types.hal",
- "IHelloWorld.hal",
- ],
- interfaces: [
- "[email protected]",
- ],
- types: [
- "HelloTest",
- ],
- gen_java: true,
- }
注意:
- name 需要與package name 相同,編譯的時候會根據需要生成對應的so 或jar
- root 即為與hidl 對應的root name,詳細看Step 1
- interfaces 為編譯過程中依賴的接口名稱,如c 中的shared library
- types 為子產品中所需要的自定義類型
- 如果有需要的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 設定有關系。詳細如下圖:
其中:
- [email protected] 就是子產品對應的庫檔案;
- and[email protected]_genc++ 為生成對應的C++臨時檔案,在使用的時候都是連結到這裡;
- -rw-rw-r-- 1 shift shift 26745 1月 22 14:20 HelloWorldAll.cpp
- -rw-rw-r-- 1 shift shift 281 1月 22 14:20 HelloWorldAll.cpp.d
- -rw-rw-r-- 1 shift shift 874 1月 22 14:20 types.cpp
- [email protected]_genc++_headers 為生成的C++ 所需的頭檔案;
- -rw-rw-r-- 1 shift shift 2264 1月 22 14:20 BnHwHelloWorld.h
- -rw-rw-r-- 1 shift shift 3008 1月 22 14:20 BpHwHelloWorld.h
- -rw-rw-r-- 1 shift shift 17785 1月 22 14:20 BsHelloWorld.h
- -rw-rw-r-- 1 shift shift 519 1月 22 14:20 hwtypes.h
- -rw-rw-r-- 1 shift shift 5296 1月 22 14:20 IHelloWorld.h
- -rw-rw-r-- 1 shift shift 279 1月 22 14:20 IHelloWorld.h.d
- -rw-rw-r-- 1 shift shift 684 1月 22 14:20 IHwHelloWorld.h
- -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 檔案
- -rw-rw-r-- 1 shift shift 922 1月 22 14:20 HelloTest.java
- -rw-rw-r-- 1 shift shift 24875 1月 22 14:20 IHelloWorld.java
- -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使用,最終生成的檔案為:
- -rw-rw-r-- 1 shift shift 973 1月 17 20:05 Android.bp
- -rw-rw-r-- 1 shift shift 605 1月 17 20:05 HelloWorld.cpp
- -rw-rw-r-- 1 shift shift 1159 1月 17 20:05 HelloWorld.h
先來看下HelloWorld.h:
- #include <android/hardware/helloworld/1.0/IHelloWorld.h>
- #include <hidl/MQDescriptor.h>
- #include <hidl/Status.h>
- namespace android {
- namespace hardware {
- namespace helloworld {
- 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 HelloWorld : public IHelloWorld {
- // Methods from ::android::hardware::helloworld::V1_0::IHelloWorld follow.
- Return< void> justTest( const hidl_string& name, justTest_cb _hidl_cb) override;
- Return< void> justTest1(::android::hardware::helloworld::V1_0::HelloTest name) override;
- // Methods from ::android::hidl::base::V1_0::IBase follow.
- };
- // FIXME: most likely delete, this is only for passthrough implementations
- // extern "C" IHelloWorld* HIDL_FETCH_IHelloWorld(const char* name);
- } // namespace implementation
- } // namespace V1_0
- } // namespace helloworld
- } // namespace hardware
- } // namespace android
如果是實用passthrough 模式,則需要打開HIDL_FETCH_IHelloWorld() 函數的注釋,并且在-impl 的C++檔案中實作,例如這裡如果使用passthrough 模式,需要在HelloWorld.cpp 中實作該函數,詳細可以看nfc 或tv 等子產品中實作。
再來看下HelloWorld.cpp:
- #define LOG_TAG "HelloWorldImpl"
- #include <log/log.h>
- #include "HelloWorld.h"
- namespace android {
- namespace hardware {
- namespace helloworld {
- namespace V1_0 {
- namespace implementation {
- // Methods from ::android::hardware::helloworld::V1_0::IHelloWorld follow.
- Return< void> HelloWorld::justTest( const hidl_string& name, justTest_cb _hidl_cb) {
- ALOGD( "justTest, name = %s", name.c_str());
- _hidl_cb(name, HelloTest::V_TEST2);
- ALOGD( "justTest end.");
- return Void();
- }
- Return< void> HelloWorld::justTest1(::android::hardware::helloworld::V1_0::HelloTest name) {
- ALOGD( "justTest1, name = %hhu", name);
- return Void();
- }
- // Methods from ::android::hidl::base::V1_0::IBase follow.
- //IHelloWorld* HIDL_FETCH_IHelloWorld(const char* /* name */) {
- //return new HelloWorld();
- //}
- //
- } // namespace implementation
- } // namespace V1_0
- } // namespace helloworld
- } // namespace hardware
- } // namespace android
這裡就是實作的地方,其中使用passthrough 的時候需要使能HIDL_FETCH_IHelloWorld() 函數。
關于HIDL 相關的資料類型詳細看:Android HIDL 中的資料類型
再來看下service.cpp:
- #define LOG_TAG "[email protected]"
- #include <android/hardware/helloworld/1.0/IHelloWorld.h>
- #include <hidl/LegacySupport.h>
- #include "HelloWorld.h"
- // Generated HIDL files
- using android::hardware::helloworld::V1_0::IHelloWorld;
- using android::hardware::helloworld::V1_0::implementation::HelloWorld;
- using android::hardware::defaultPassthroughServiceImplementation;
- using android::hardware::configureRpcThreadpool;
- using android::hardware::joinRpcThreadpool;
- int main() {
- #if 0
- return defaultPassthroughServiceImplementation<IHelloWorld>();
- #else
- sp<IHelloWorld> service = new HelloWorld();
- configureRpcThreadpool( , true /*callerWillJoin*/);
- if(android::OK != service->registerAsService())
- return ;
- joinRpcThreadpool();
- #endif
- }
Android.bp 是為了編譯HIDL 實作部分的代碼生成的預設編譯檔案(詳細看Android HIDL 中 hidl-gen使用),可以根據實際的情況修改:
- cc_library_shared {
- name: "[email protected]",
- relative_install_path: "hw",
- proprietary: true,
- srcs: [
- "HelloWorld.cpp",
- ],
- shared_libs: [
- "libhidlbase",
- "libhidltransport",
- "libutils",
- "[email protected]",
- ],
- }
如果其他子產品需要so 則需要share lib,如果不需要刻意直接編譯service,如下:
- cc_binary {
- name: "[email protected]",
- defaults: [ "hidl_defaults"],
- relative_install_path: "hw",
- vendor: true,
- init_rc: [ "[email protected]"],
- srcs: [
- "HelloWorld.cpp",
- "service.cpp"
- ],
- shared_libs: [
- "liblog",
- "libhidlbase",
- "libhidltransport",
- "libutils",
- "libhardware",
- "[email protected]",
- ],
- }
注意:
- 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 檔案,檔案名為[email protected]:
- service helloworld-hal- - /vendor/bin/hw/[email protected]. -service
- class hal
- user system
- group system
Step 7 啟動service
需要注意的是,在應用起來之前需要使能service,一般有兩種方式,一個是通過rc 中的service name,直接start;另外一種是通過selinux 中添加te 檔案,設定domain 資訊。對于selinux 配置,這裡暫不分析,詳細看 SELINUX 中文章。
Step 8 實作client 端
App 其他的代碼這裡不做展示,主要來看調用的地方:
- private void test() {
- IHelloWorld service = null;
- try {
- service = IHelloWorld.getService( true);
- } catch (RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- if (service == null) {
- Log.e(TAG, "test failed, service is null...");
- return;
- }
- try {
- service.justTest1(( byte) );
- } catch (RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
Android.mk 為:
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_CERTIFICATE := platform
- LOCAL_MODULE_TAGS := optional
- LOCAL_SRC_FILES := $(call all-java-files-under, src)
- LOCAL_PACKAGE_NAME := TestHIDLClient
- #LOCAL_PROGUARD_FLAG_FILES := proguard.flags
- LOCAL_PRIVATE_PLATFORM_APIS := true
- LOCAL_STATIC_JAVA_LIBRARIES := \
- android.hardware.helloworld-V1.0-java
- include $(BUILD_PACKAGE)
列印log 如下:
- 01-23 13:57:20.424 6498 6498 I android_os_HwBinder: HwBinder: Starting thread pool for default::[email protected]::IHelloWorld
- 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)
- 01-23 13:57:20.424 572 3344 E audio_hw_extn: audio_extn_perf_lock_acquire: Failed to acquire perf lock, err:
- : : D audio_hw_primary: select_devices for use case ( low-latency-playback)
- : : D audio_hw_primary: select_devices: out_snd_device( : speaker) in_snd_device( : )
- : : I msm8916_platform: platform_check_and_set_codec_backend_cfg:becf: afe: bitwidth , samplerate channels , backend_idx usecase = device (speaker)
- : : D msm8916_platform: platform_split_snd_device: snd_device( ) num devices( ) new_snd_devices( )
- : : D HelloWorldImpl: justTest1, name =
相關文章:
Android Treble 簡介
Android HIDL 詳解
Android HIDL 中 hidl-gen使用
Android HIDL 程式設計規範
Android HIDL 接口和軟體包使用
Android HIDL 中的函數
Android HIDL 中的資料類型