AndroidQ 打通應用層到HAL層—(HAL模塊實現)這篇文章中我們已經實現了自己的HAL,本篇我們實現一個HIDL服務,通過這個服務來調用HAL模塊的函數
什么是HIDL
HIDL 全稱為HAL interface definition language(發音為“hide-l”)是用于指定 HAL 和其用戶之間的接口的一種接口描述語言 (IDL),Android O開始引入了HIDL這個概念。
HIDL和應用層AIDL差不多,AIDL常用于連接App和Framework,HIDL則是用來連接Framework和HAL,AIDL使用Binder通信,HIDL則使用HwBinder通信,他們都是通過Binder驅動完成通信,只不過兩個Binder域不一樣
為什么需要HIDL
目前Android系統生態是幾乎每年google都會出一個Android大版本,而普通手機用戶一部手機一般要用兩三年,所以你會發現盡管Android系統已經升級到了10,馬上11出來了,然后還是有很多用戶依然使用的是Android 5,6,7等版本。
對普通用戶來說如果不更換手機就很難跟上Android版本,這是因為OEM廠商在同一設備上進行系統升級需要花費時間金錢成本很高,導致他們不愿意升級,成本高的原因是Android O之前Android Framework的升級需要OEM將HAL也進行對應升級,Framework和HAL是一起被編譯成system.img,它們存在高耦合。
針對這種情況google在Android O中引入了Treble計劃,Treble的目的就是解耦Framework和HAL,就是通過HIDL來實現,Framework不再直接調用HAL,而是通過HIDL來間接使用HAL模塊,每個HAL模塊都可以對應一個HIDL服務,Framework層通過HwBinder創建HIDL服務。
通過HIDL服務來獲取HAL相關模塊繼而打開HAL下的設備,而最終HAL也從system.img中分離,被編進一個單獨的分區vendor.img,從而簡化了Android系統升級的影響與難度。
HIDL的使用
HIDL可以分為:HIDL C++(C++實現)、HIDL Java(Java 實現),并且還主要分為直通式和綁定式,本篇文章使用的C++和直通式的HIDL,HIDL用起來非常簡單,AOSP的hardware/interfaces/目錄下有很多的HIDL,我們仿照其他HIDL創建自己的HIDL目錄:hardware/interfaces/hello_hidl/1.0
并在此目錄下創建一個IHello.hal文件:
packageandroid.hardware.hello_hidl@1.0; interfaceIHello{ addition_hidl(uint32_ta,uint32_tb)generates(uint32_ttotal); };
這個文件定義了一個addition_hidl函數,這個函數用來調用HAL的加法函數
然后就可以使用Android提供的工具hidl-gen來生成HIDL框架,執行如下命令:
PACKAGE=android.hardware.hello_hidl@1.0 LOC=hardware/interfaces/hello_hidl/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
執行命令成功之后我們會發現hardware/interfaces/hello_hidl/1.0下多了一個default目錄,進入default目錄,里面有三個文件Android.bp,Hello.cpp,Hello.h
在這里插入圖片描述
之后再在執行./hardware/interfaces/update-makefiles.sh這個命令,update-makefiles.sh這個腳本目的是為HIDL生成對應Android.bp文件
最后目錄結構為:
在這里插入圖片描述
接著我們還需要在default目錄下增加一個空文件service.cpp,用作注冊HIDL服務,我們采用直通式的HIDL,所以service.cpp的內容為:
#include#include //GeneratedHIDLfiles usingandroid::IHello; usingandroid::defaultPassthroughServiceImplementation; intmain(){ returndefaultPassthroughServiceImplementation (); }
defaultPassthroughServiceImplementation函數最終會向HwServiceManager注冊HIDL服務
接著我們來看看之前生成的文件,首先看Hello.h
//FIXME:yourfilelicenseifyouhaveone #pragmaonce #include#include #include namespaceandroid{ namespacehardware{ namespacehello_hidl{ namespaceV1_0{ namespaceimplementation{ using::hidl_array; using::hidl_memory; using::hidl_string; using::hidl_vec; using::Return; using::Void; using::sp; structHello:publicIHello{ //Methodsfrom::IHellofollow. Return addition_hidl(uint32_ta,uint32_tb)override; //Methodsfrom::IBasefollow. }; //FIXME:mostlikelydelete,thisisonlyforpassthroughimplementations //去掉注釋 extern"C"IHello*HIDL_FETCH_IHello(constchar*name); }//namespaceimplementation }//namespaceV1_0 }//namespacehello_hidl }//namespacehardware }//namespaceandroid
系統自動生成了Hello結構體(當然也可以自己改為class),繼承IHello接口,addition_hidl函數就需要在Hello.cpp中去實現了,因為我們采用直通式HIDL,所以需要將// extern “C” IHello* HIDL_FETCH_IHello(const char* name);的注釋去掉
接著來看看Hello.cpp:
//FIXME:yourfilelicenseifyouhaveone #include"Hello.h" #includenamespaceandroid{ namespacehardware{ namespacehello_hidl{ namespaceV1_0{ namespaceimplementation{ //Methodsfrom::IHellofollow. Return Hello::addition_hidl(uint32_ta,uint32_tb){ //TODOimplement ALOGE("hello_hidlserviceisinitsuccess....a:%d,b:%d",a,b); returnuint32_t{}; } //Methodsfrom::IBasefollow. IHello*HIDL_FETCH_IHello(constchar*/*name*/){ returnnewHello(); } }//namespaceimplementation }//namespaceV1_0 }//namespacehello_hidl }//namespacehardware }//namespaceandroid
同樣需要去掉HIDL_FETCH_IHello函數的注釋,采用直通式HIDL時,通過前面service.cpp中的defaultPassthroughServiceImplementation函數注冊HIDL服務時,內部原理就是通過“HIDL_FETCH_”字串拼接defaultPassthroughServiceImplementation傳遞的IHello,找到HIDL_FETCH_IHello函數并獲取IHello對象,我們可以看到HIDL_FETCH_IHello初始代碼就是創建了一個Hello對象
在接著看default目錄下的Android.bp:
cc_library_shared{ name:"android.hardware.hello_hidl@1.0-impl", relative_install_path:"hw", proprietary:true, srcs:[ "Hello.cpp", ], shared_libs:[ "libhidlbase", "libhidltransport", "libutils", "android.hardware.hello_hidl@1.0", "liblog", "libutils", ], }
這個Android.bp會將Hello這個HIDL服務編譯成一個android.hardware.hello_hidl@1.0-impl.so,它還依賴一個android.hardware.hello_hidl@1.0.so,這個so哪來的呢?
再接著看1.0目錄下的Android.bp:
//Thisfileisautogeneratedbyhidl-gen-Landroidbp. hidl_interface{ name:"android.hardware.hello_hidl@1.0", root:"android.hardware", vndk:{ enabled:true, }, srcs:[ "IHello.hal", ], interfaces:[ "android.hidl.base@1.0", ], gen_java:true, }
這個Android.bp會將hardware/interfaces/hello_hidl/1.0這個HIDL編譯成一個android.hardware.hello_hidl@1.0.so,到這里我們發現service.cpp沒有用到,所以我們還需要修改default目錄下的Android.bp:
//FIXME:yourfilelicenseifyouhaveone cc_library_shared{ name:"android.hardware.hello_hidl@1.0-impl", relative_install_path:"hw", proprietary:true, srcs:[ "Hello.cpp", ], shared_libs:[ "libhidlbase", "libhidltransport", "libutils", "liblog", "libhardware", "android.hardware.hello_hidl@1.0", "liblog", "libutils", ], } cc_binary{ name:"android.hardware.hello_hidl@1.0-service", defaults:["hidl_defaults"], relative_install_path:"hw", vendor:true, srcs:["service.cpp"], shared_libs:[ "android.hardware.hello_hidl@1.0", "libhardware", "libhidlbase", "libhidltransport", "libutils", "liblog", ], }
新增加對service.cpp的編譯,我們將service.cpp編譯成一個二進制可執行文件android.hardware.hello_hidl@1.0-service.so,用來啟動HIDL服務,好了,最終我們這個HIDL會編譯出來如下三個so:
android.hardware.hello_hidl@1.0-impl.so
android.hardware.hello_hidl@1.0.so
android.hardware.hello_hidl@1.0-service.so
還有一點需要注意的是,這個HIDL想要被Framework獲取使用還需要在manifest.xml中注冊,
manifest.xml在手機vendor/etc/vintf/manifest.xml下,我們將這個文件pull出來然后添加如下代碼:
android.hardware.hello_hidl hwbinder 1.0 IHello default @1.0::IHello/default
然后在Hello.cpp中添加一行log,之后進行編譯
IHello*HIDL_FETCH_IHello(constchar*/*name*/){ ALOGE("hello_hidlserviceisinitsuccess...."); returnnewHello(); }
執行mmm hardware/interfaces/hello_hidl/1.0/
在這里插入圖片描述
編譯成功后我們將生成的三個so分別push到手機vendor/lib64/hw/,vendor/lib64/,vendor/bin/hw/目錄下
adb push vendor/lib64/hw/android.hardware.hello_hidl@1.0-impl.so vendor/lib64/hw/
adb push system/lib64/android.hardware.hello_hidl@1.0.so vendor/lib64/
adb push vendor/bin/hw/android.hardware.hello_hidl@1.0-service vendor/bin/hw/
接著我們到手機vendor/bin/hw/目錄下去執行android.hardware.hello_hidl@1.0-service這個二進制可執行文件,這個文件就會執行service.cpp的代碼,調用defaultPassthroughServiceImplementation注冊我們的HIDL服務
在這里插入圖片描述
再看看log輸出:
在這里插入圖片描述
在執行android.hardware.hello_hidl@1.0-service時就會輸入這句log,代表我們這個HIDL服務已經實現,其實通常的HIDL服務都是通過rc文件來開機啟動的,我這里為了方便演示就沒有寫
再執行adb shell ps -A|grep -i --color "hello_hidl"命令看下這個服務狀態
我們發現HIDL服務啟動之后就會一直在后臺,這個其實和AMS,WMS這種服務是類似的,啟動之后在后臺會等待client端訪問
在這里插入圖片描述
HIDL這個服務已經能夠正常啟動了,接著寫一個測試程序看能否獲取這個服務,并且調用該服務的函數,我在Hello.cpp的addition_hidl函數中添加了一句log:
ReturnHello::addition_hidl(uint32_ta,uint32_tb){ //TODOimplement ALOGD("dongjiao...Hello::addition_hidla=%d,b=%d",a,b); returnuint32_t{}; }
測試程序寫在hardware/interfaces/hello_hidl/1.0/default目錄下:
在這里插入圖片描述
Hello_hidl_test.cpp:
#include#include #include usingandroid::sp; usingandroid::IHello; usingandroid::Return; intmain(){ android::sp hw_device=IHello::getService(); if(hw_device==nullptr){ ALOGD("dongjiao...failedtogethello-hidl"); return-1; } ALOGD("dongjiao...successtogethello-hidl...."); Return total=hw_device->addition_hidl(3,4); return0; }
測試程序代碼也比較簡單,獲取IHello的服務,然后調用addition_hidl函數
看一下Android.bp:
cc_binary{ name:"Hello_hidl_test", srcs:["Hello_hidl_test.cpp"], shared_libs:[ "liblog", "android.hardware.hello_hidl@1.0", "libhidlbase", "libhidltransport", "libhwbinder", "libutils", ], }
我們再編譯這個測試程序,它會被編譯成一個可執行二進制文件Hello_hidl_test
編譯命令:
mmm hardware/interfaces/hello_hidl/1.0/default/test/
在這里插入圖片描述
編譯成功了,將這個可執行文件push到手機/system/bin/目錄下
在執行Hello_hidl_test之前別忘了把HIDL服務啟動起來
在這里插入圖片描述
接著執行Hello_hidl_test
在這里插入圖片描述
然后看log輸出
在這里插入圖片描述
可以看到成功了,成功獲取到了HIDL服務并且調用了該服務的addition_hidl函數,將我們的參數傳遞了過去,實際開發中就可以在獲取HIDL服務時打開HAL模塊,然后可以直接調用HAL的函數。 上一篇文章其實也寫了測試程序測自定義的HAL模塊,我們只需要將上一篇文章中的測試程序中的代碼copy到HIDL初始化代碼中就能夠調用HAL的那個加法函數了,具體就不測試了。
到這里HIDL服務已經成功實現并且能夠正常使用,HIDL這個框架用起來還是比較簡單的,大部分代碼都是通過工具生成的。
審核編輯:湯梓紅
-
Android
+關注
關注
12文章
3923瀏覽量
127133 -
函數
+關注
關注
3文章
4304瀏覽量
62429 -
C++
+關注
關注
22文章
2104瀏覽量
73489 -
應用層
+關注
關注
0文章
46瀏覽量
11493 -
HAL
+關注
關注
2文章
70瀏覽量
12593
原文標題:Android Q打通應用層到HAL層(HIDL服務實現)
文章出處:【微信號:哆啦安全,微信公眾號:哆啦安全】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論