天天看點

Android HIDL 執行個體

前言:

從Android HIDL 詳解 一文得知HIDL 使用passthrough 模式,為了與原來的HAL 版本相容。除了passthrough 模式,還有一種binderized模式。本文通過HIDL 中的Helloworld 進行執行個體分析,進一步了解HIDL 使用。

本文source code 基于Android P。

Step 1 确定環境

Android HIDL 程式設計規範一文中得知需要确定HIDL 的目錄結構和軟體包名稱

Android HIDL 執行個體

下表列出了軟體包字首和位置:

軟體包字首 位置

android.hardware.*

hardware/interfaces/*

android.frameworks.*

frameworks/hardware/interfaces/*

android.system.*

system/hardware/interfaces/*

android.hidl.*

system/libhidl/transport/*

如上表,hardware/interfaces 目錄一般放的是源生hal 相關的接口,如下圖所示:

Android HIDL 執行個體

除了上表中提到這幾個源生的目錄,一般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,代碼如下:

  1. package android.hardware.helloworld@ ;
  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@ ;
  2. enum HelloTest : uint8_t {
  3. V_TEST1 = ,
  4. V_TEST2 = ,
  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: "[email protected]",
  4. root: "android.hardware",
  5. vndk: {
  6. enabled: true,
  7. },
  8. srcs: [
  9. "types.hal",
  10. "IHelloWorld.hal",
  11. ],
  12. interfaces: [
  13. "[email protected]",
  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 HIDL 執行個體

其中:

  • [email protected] 就是子產品對應的庫檔案;
  • and[email protected]_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
  • [email protected]_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 "[email protected]"
  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( , true /*callerWillJoin*/);
  17. if(android::OK != service->registerAsService())
  18. return ;
  19. joinRpcThreadpool();
  20. #endif
  21. }

Android.bp 是為了編譯HIDL 實作部分的代碼生成的預設編譯檔案(詳細看Android HIDL 中 hidl-gen使用),可以根據實際的情況修改:

  1. cc_library_shared {
  2. name: "[email protected]",
  3. relative_install_path: "hw",
  4. proprietary: true,
  5. srcs: [
  6. "HelloWorld.cpp",
  7. ],
  8. shared_libs: [
  9. "libhidlbase",
  10. "libhidltransport",
  11. "libutils",
  12. "[email protected]",
  13. ],
  14. }

如果其他子產品需要so 則需要share lib,如果不需要刻意直接編譯service,如下:

  1. cc_binary {
  2. name: "[email protected]",
  3. defaults: [ "hidl_defaults"],
  4. relative_install_path: "hw",
  5. vendor: true,
  6. init_rc: [ "[email protected]"],
  7. srcs: [
  8. "HelloWorld.cpp",
  9. "service.cpp"
  10. ],
  11. shared_libs: [
  12. "liblog",
  13. "libhidlbase",
  14. "libhidltransport",
  15. "libutils",
  16. "libhardware",
  17. "[email protected]",
  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 檔案,檔案名為[email protected]:

  1. service helloworld-hal- - /vendor/bin/hw/[email protected]. -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) );
  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::[email protected]::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:
  4. : : D audio_hw_primary: select_devices for use case ( low-latency-playback)
  5. : : D audio_hw_primary: select_devices: out_snd_device( : speaker) in_snd_device( : )
  6. : : I msm8916_platform: platform_check_and_set_codec_backend_cfg:becf: afe: bitwidth , samplerate channels , backend_idx usecase = device (speaker)
  7. : : D msm8916_platform: platform_split_snd_device: snd_device( ) num devices( ) new_snd_devices( )
  8. : : D HelloWorldImpl: justTest1, name =

相關文章:

Android Treble 簡介

Android HIDL 詳解

Android HIDL 中 hidl-gen使用

Android HIDL 程式設計規範

Android HIDL 接口和軟體包使用

Android HIDL 中的函數

Android HIDL 中的資料類型

前言: