文章目錄
- 概念介紹
- Treble專案---HIDL產生背景,獨立升級framework需求
- HIDL概念---HAL 介面定義語言,目標framework和HAL解耦
- HIDL的兩種模式---Passthrough直通 / Binderized系結
- HIDL設計分析
- HAL模塊與package軟體包(包含.hal檔案)
- .hal檔案 語法簡介
- .hal檔案 (hidl-gen工具)編譯轉換.h/.cpp檔案
- .hal檔案hash值與多版本匹配
- HIDL設計舉例---hwc composer的分析
- composer相關原始碼分析
- composer相關權限配置分析
- composer相關編譯檔案分析
- composer啟動流程分析
- 新增基于HIDL的HAL模塊流程
- 1. 建立模塊包目錄、撰寫.hal檔案創建HIDL介面
- 2. 執行構建腳本,生成對應makefile和default目錄與檔案
- 3. 在xml檔案添加介面宣告、在current.txt添加版本hash值
- 4. 實作service服務端hal注冊與功能實作代碼
- 5. 實作client客戶端呼叫hal模塊代碼
- 6. 配置新增hal模塊Selinux配置權限
- 7. 在mk檔案添加編譯關系
- 參考
概念介紹
Treble專案—HIDL產生背景,獨立升級framework需求
Android O(8.0) 重新設計了 Android 作業系統框架,在一個名為Treble的專案中,以便讓制造商能夠以更低的成本更輕松、更快速地將設備更新到新版 Android 系統,在這種新架構中,HAL 介面定義語言HIDL指定了 HAL 和其用戶之間的介面,讓用戶能夠替換 android framework,而無需重新編譯 HAL,

HIDL概念—HAL 介面定義語言,目標framework和HAL解耦
HIDL: HAL 介面定義語言HAL interface definition language,在 Android 8.0 中被全面使用,用于在可以獨立編譯的代碼庫之間進行通信的系統,定義Android Framework與Android HAL實作之間的介面,把framework和hal隔離,目的是使 Android 可以在不重新編譯 HAL 的情況下對 Framework 進行 OTA 升級(OTA:Over The Air,一種遠程無線升級技術,在線升級系統),

HIDL與AIDL比較類似,底層也是基于binder機制,但是也有稍微不一樣的地方,為了支持HIDL,Android 對BInder做了一定程度的修改,/dev/hwbinder,
HIDL的兩種模式—Passthrough直通 / Binderized系結
為了將以往設備的 Android 版本更新到 Android O,開發者需要將傳統的 HAL 封裝到新的 HIDL 介面中,這個介面為 HAL 提供了 Binder 模式以及 Passthrough 模式,
(1)Passthrough直通模式:對原先HAL的包裝,老版本HAL的兼容,最終的binder service 跟binder client都是活在同一個行程當中,僅對 C++ 客戶端與實作適用,
(2)Binderized系結模式:Binder化的HAL,HAL都被寫成了binder service,Android framework是binder client,兩者再不同的背景關系,
推出時即搭載了 Android 8.0 或后續版本的設備都必須只支持系結式 HAL,如下圖:

HIDL設計分析
基于HIDL介面的HAL模塊整體流程如下:呼叫者需要包含Client端對應so,通過getService獲取服務端實體,通過對client的操作對服務端Service行程發出申請,而Serive端是開機時init行程 通過rc啟動腳本執行service對應可執行檔案 創建行程,并在啟動程序中向HwServiceManager注冊服務,來回應client的訪問,而實際專案程序,往往service可執行檔案并不會包含過多的業務實作,而是通過鏈接impl.so共享庫來實作,impl.so中為真正的業務實作代碼,并呼叫Vender底層SDK,涉及硬體驅動就通過kernel中內核驅動模塊提供支持,

整個設計android的HIDL機制主要就是client與service之間的訪問介面設計, HIDL的原理大致如下:android提供了一種特定撰寫規則的.hal檔案(類似c++和java),然后通過特定的hidl-gen工具和一些構建腳本 把.hal轉換為可以編譯的.cpp/.h檔案、android.bp構建檔案,這些生成的檔案已經實作了client對應so,service可執行檔案(包含了注冊service程序),而接下來只需要在對應.cpp實作內部細節,然后添加對應權限配置,加入編譯程序等就完成了hal模塊新增,
HAL模塊與package軟體包(包含.hal檔案)
每個HAL模塊設計一個介面軟體包package,可以具有子級,如 package.subpackage,已發布的 HIDL 軟體包的根目錄為 hardware/interfaces 或 vendor/vendorName,軟體包名稱在根目錄下形成一個或多個子目錄;定義同一個軟體包的所有檔案都位于同一目錄下,
軟體包目擴展名為 .hal 的檔案,每個檔案均必須包含一個指定檔案所屬的軟體包和版本的 package 陳述句,types.hal 檔案(如果存在)并不定義介面,而是定義軟體包中每個介面可以訪問的資料型別,
.hal檔案 語法簡介
https://source.android.google.cn/devices/architecture/hidl/code-style
HIDL 語言與 C 語言類似,
注釋:/** */ 表示檔案注釋(只用于型別、方法、欄位和列舉值宣告);/* */ 表示多行注釋;// 表示注釋一直持續到行尾;
[empty] 表示該字詞可能為空,
? 跟在文本或字詞后,表示它是可選的,
... 表示包含零個或多個項、使用指定的分隔符號分隔的序列,HIDL 中不含可變引數,
逗號用于分隔序列元素,
分號用于終止各個元素,包括最后的元素,
大寫字母是非終止符,
italics 是一個令牌系列,如 integer 或 identifier(標準 C 決議規則),
constexpr 是 C 樣式的常量運算式(例如 1 + 1 和 1L << 3),
import_name 是軟體包或介面名稱,按 HIDL 版本控制中所述的方式加以限定,
小寫 words 是文本令牌,
.hal檔案 (hidl-gen工具)編譯轉換.h/.cpp檔案
hidl-gen 編譯器會將 .hal 檔案編譯成一組 .h 和 .cpp 檔案, 這些自動生成的檔案可用來構建客戶端/服務器實作鏈接到的共享庫,用于構建此共享庫的 Android.bp 檔案由 hardware/interfaces/update-makefiles.sh 腳本自動生成,

IFoo.h :描述 C++ 類中的純 IFoo 介面;它包含 IFoo.hal 檔案中的 IFoo 介面中所定義的方法和型別,類的命名空間包含軟體包名稱和版本號,客戶端和服務器都包含此標頭:客戶端用它來呼叫方法,服務器用它來實作這些方法,
IHwFoo.h :其中包含用于對介面中使用的資料型別進行序列化的函式的宣告,開發者不得直接包含其標頭(它不包含任何類),
BpFoo.h : 從 IFoo 繼承的類,可描述介面的 HwBinder 代理(客戶端)實作,開發者不得直接參考此類,
BnFoo.h: 保存對 IFoo 實作的參考的類,可描述介面的 HwBinder 服務器端實作,開發者不得直接參考此類,
FooAll.cpp :包含 HwBinder 客戶端和 HwBinder 服務器端的實作的類,當客戶端呼叫介面方法時,代理會自動從客戶端封送引數,并將事務發送到系結內核驅動程式,該內核驅動程式會將事務傳送到另一端的服務器端實作,
.hal檔案hash值與多版本匹配
HIDL的設計目標就是 把 framework 與 vender之間 的介面固定下來,HIDL用的方法就是給每個hal檔案生成一個hash值,記錄到current.txt檔案中,類似如下composer模塊的hash值,在編譯的時候就可以校驗,當需要新增介面的時候,就新增一個版本,繼承上一個版本的hal介面進行擴展,同時生成新版本對應的hash值,

HIDL設計舉例—hwc composer的分析
hwc composer是android里面對疊加器的加速,相關概念參考:https://blog.csdn.net/runafterhit/article/details/118884165
代碼:http://androidxref.com/9.0.0_r3/xref/hardware/interfaces/graphics/composer/2.2/
composer相關原始碼分析
/hardware/interfaces/graphics/composer/2.2/
IComposer.hal:定義了composer模塊的hal介面,
package android.hardware.graphics.composer@2.2;; // 指定檔案所屬的軟體包和版本
import @2.1::IComposer; // 匯入2.1的類
interface IComposer extends @2.1::IComposer {
// 完全繼承2.1包定義的IComposer
}
// 2.1包定義的IComposer 如下:
interface IComposer {
getCapabilities() generates (vec<Capability> capabilities); // 獲取能力級別
dumpDebugInfo() generates (string debugInfo); // 列印debug資訊
createClient() generates (Error error, IComposerClient client); // 創建clenit實體
}
IComposerClient.hal:定義了composer的client的hal介面:
package android.hardware.graphics.composer@2.1; // 指定檔案所屬的軟體包和版本
interface IComposerClient extends @2.2::IComposerClient {
// 繼承2.1包定義的IComposerClient ,同時專門新增了2.2相關功能介面,部分如下:
getReadbackBufferFence(Display display) generates (Error error,handle acquireFence); // 獲取buf的fence
}
// 2.1包定義的IComposerClient 如下:
interface IComposerClient {
// 重點相關函式如下:
registerCallback(IComposerCallback callback); // 回呼注冊介面
createVirtualDisplay(/* 省略*/); // 創建虛擬display通道
createLayer(Display display, uint32_t bufferSlotCount) generates (Error error,Layer layer); // 創建layer
destroyLayer(Display display, Layer layer) generates (Error error); // 銷毀layer
setVsyncEnabled(Display display, Vsync enabled) generates (Error error); // 使能通道vsync
setInputCommandQueue(fmq_sync<uint32_t> descriptor) generates (Error error);// 設定輸入命令到queue
executeCommands(/* 省略*/); // 直行命令queue中命令
// 還有各種設定屬性介面
}
IComposerCallback.hal:定義client注冊的回呼型別,定義在2.1包中
package android.hardware.graphics.composer@2.1;
interface IComposerCallback {
onHotplug(Display display, Connection connected); // display通路的拔插 回呼
oneway onRefresh(Display display); // display的重繪回呼,強制重繪
oneway onVsync(Display display, int64_t timestamp); // display的vsync信號回呼
}
composer相關權限配置分析
Selinux相關概念可以參考: https://blog.csdn.net/runafterhit/article/details/119920733
composer要能被訪問,或者要訪問其他資源,需要進行selinux相關權限配置,這里完全是更具具體產品專案的權限設定,往往需要設定:1、對應服務service行程的權限管理(可以訪問那些資源,包括binder);2、service可執行檔案的安全背景關系,誰能訪問它也需要添加allow權限;3、涉及硬體設備節點訪問,需要在service追加相關權限,
舉例如下:
/device/google/marlin/sepolicy/hal_graphics_composer_default.te :主要設定service的檔案訪問、binder訪問等
userdebug_or_eng(`
allow hal_graphics_composer_default diag_device:chr_file rw_file_perms;
')
dontaudit hal_graphics_composer_default diag_device:chr_file rw_file_perms;
# misc
typeattribute hal_graphics_composer_default data_between_core_and_vendor_violators;
allow hal_graphics_composer_default display_data_file:dir create_dir_perms;
allow hal_graphics_composer_default display_data_file:file create_file_perms;
# persist
allow hal_graphics_composer_default persist_file:dir search;
# persist/display
allow hal_graphics_composer_default persist_display_file:dir r_dir_perms;
allow hal_graphics_composer_default persist_display_file:file create_file_perms;
vndbinder_use(hal_graphics_composer_default);
add_service(hal_graphics_composer_default, qdisplay_service)
# HWC_UeventThread
allow hal_graphics_composer_default self:netlink_kobject_uevent_socket create_socket_perms_no_ioctl;
# Access /sys/devices/virtual/graphics/fb0/mdp/caps and maybe others
r_dir_file(hal_graphics_composer_default, sysfs_type)
composer相關編譯檔案分析
在/hardware/interfaces/graphics/composer/目錄下mm編譯,會生成:
1、android.hardware.graphics.composer@2.2-service. rc : 啟動配置腳本,直接從copy到vendor/etc/init,
2、android.hardware.graphics.composer@2.*. so:客戶端client使用時需要鏈接的客戶端so,
3、android.hardware.graphics.composer@2.2-service :service的可執行檔案,生成在vendor/bin/hw/,見2.2/default/Android.mk中編譯規則,service 服務的作用就是向 hwservicemanager 注冊 HAL,并鏈接impl .so呼叫實作,
4、 android.hardware.graphics.composer@2.1-impl .so :service編譯時鏈接的共享庫,在service行程加載時會鏈接進去,
composer啟動流程分析
1、開機init行程會讀取vendor/etc/init下的xxx_service. rc初始腳本檔案,執行vendor/bin/hw/xx_service可執行檔案,從而啟動service行程:
// 腳本內容:
service vendor.hwcomposer-2-2 /vendor/bin/hw/android.hardware.graphics.composer@2.2-service
class hal animation
user system
group graphics drmrpc
capabilities SYS_NICE
onrestart restart surfaceflinger
行程狀態:
1|HWRVL:/vendor/etc/init $ ps -A | grep composer
system 610 1 2248748 2824 0 0 S android.hardware.graphics.composer@2.2-service
2、行程啟動后,servie執行main函式,建立Binder IPC 通信,
/hardware/interfaces/graphics/composer/2.2/default/service.cpp,HIDL 框架為了支持供應商 HAL 行程,提供了 libbinder 用戶空間庫用于操作 Binder 設備節點(hwbinder & vndbinder),然后呼叫HwcLoader::load()進行hwc模塊加載,
int main() {
// the conventional HAL might start binder services
android::ProcessState::initWithDriver("/dev/vndbinder");
android::ProcessState::self()->setThreadPoolMaxThreadCount(4);
android::ProcessState::self()->startThreadPool();
// same as SF main thread
struct sched_param param = {0};
param.sched_priority = 2;
if (sched_setscheduler(0, SCHED_FIFO | SCHED_RESET_ON_FORK, ¶m) != 0) {
ALOGE("Couldn't set SCHED_FIFO: %d", errno);
}
android::hardware::configureRpcThreadpool(4, true /* will join */);
android::sp<IComposer> composer = HwcLoader::load();
if (composer == nullptr) {
return 1;
}
if (composer->registerAsService() != android::NO_ERROR) {
ALOGE("failed to register service");
return 1;
}
return 1;
}
3、HWC模塊load,這里最終呼叫到createComposer,其中主要實作呼叫hal::Composer::create(std::move(hal)) 來呼叫到ComposerHal 類中的
composer/2.2/utils/passthrough/include/composer-passthrough/2.2/HwcLoader.h
class HwcLoader : public V2_1::passthrough::HwcLoader {
public:
static IComposer* load() {
const hw_module_t* module = loadModule();
if (!module) {
return nullptr;
}
auto hal = createHalWithAdapter(module);
if (!hal) {
return nullptr;
}
return createComposer(std::move(hal)).release();
}
/* 省略 */
static std::unique_ptr<IComposer> createComposer(std::unique_ptr<hal::ComposerHal> hal) {
return hal::Composer::create(std::move(hal));
}
}
新增基于HIDL的HAL模塊流程
以下梳理添加一個基于hidl的hal模塊test的大致流程,作為開發參考,具體例子可以看參考中的實體,
整個流程大致為:寫.hal檔案通過工具和組態檔生成hidl相關編譯檔案,實作內部細節,添加xml的介面配置和current介面hash版本,按使用需求撰寫client訪問,設定selinux配置權限,添加包編譯構建關系,
1. 建立模塊包目錄、撰寫.hal檔案創建HIDL介面
在介面目錄添加對應模塊目錄,如hardware/interfaces/test/1.0/,表示新增test模塊,默認版本號1.0,創建檔案ITest.hal,參考內容如下(如果有必要,還可以types.hal定義復雜結構體,或者拆解為多個.hal檔案):
package android.hardware.test@1.0; // 宣告包的名稱test,版本1.0
interface ITest {
init(TestID id);
//無回傳值
helloWorld(string name) generates (string result);
};
2. 執行構建腳本,生成對應makefile和default目錄與檔案
使用hidl-gen生成default目錄 里的C++檔案
PACKAGE=android.hardware.test@1.0
LOC=hardware/interfaces/test/1.0/default
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
根目錄使用update-makefiles.sh生成1.0目錄下的Android.bp
./hardware/interfaces/update-makefiles.sh
生成檔案如下:
├── 1.0
│ ├── default
│ │ ├── Android.bp
│ │ ├── Test.cpp // 需要實作service的實作代碼
│ │ ├── Test.h
│ ├── Android.bp
│ ├── ITest.hal
再新增android.hardware.test@1.0-service. rc啟動腳本檔案
service test_service /vendor/bin/hw/android.hardware.test@1.0-service
class hal
user system
group system
3. 在xml檔案添加介面宣告、在current.txt添加版本hash值
在manifest.xml檔案中添加新增hal模塊的介面ITest宣告,
<hal format="hidl">
<name>android.hardware.test</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>ITest</name>
<instance>default</instance>
</interface>
</hal>
在current.txt中添加對應軟體版本的hash值,用hidl-gen -L hash命令生成,
// 參考命令,xxx為供應商vender名稱,和產品使用具體版本相關:
hidl-gen -L hash -r vendor.xxx.harware:vendor/xxx/hardware -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport vendor.xxx.harware.test@1.0
生成后寫入current.txt,類似如下composer模塊的hash值:

4. 實作service服務端hal注冊與功能實作代碼
在工具生成的Test.cpp中實作service的內部實作,實際專案中,經常再單獨實作一個impl .so,service本身是一個輕量級的可執行檔案,service編譯時鏈接的共享庫impl .so包含真正的實作,在service行程加載時會鏈接進去,
// 部分代碼如下
Return<void> Test::init(const ::android::hardware::test::V1_0::TestID& id) {
mExit = false;
mName = id.name;
mID = id.id;
ALOGD("init:");
return Void();
}
Return<void> Test::helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) {
ALOGD("helloWorld:");
char buf[100];
::memset(buf,0x00,100);
::snprintf(buf,100,"Hello World,%s",name.c_str());
hidl_string result(buf);
_hidl_cb(result);
return Void();
}
5. 實作client客戶端呼叫hal模塊代碼
從framework層 當需要呼叫的對應hal介面,Android.bp添加test編譯時鏈接對應.so的模塊,
然后通過getService獲取client實體,參考類似如下:
private ITest halService ;
halService = ITest.getService();//獲取service對應實體
halService.helloWorld(str); // 呼叫對應client方法,向service發出申請
6. 配置新增hal模塊Selinux配置權限
Selinux相關概念可以參考: https://blog.csdn.net/runafterhit/article/details/119920733
一般新增一個模塊,往往需要添加已下幾處典型selinux配置:
1)新增一個hal_xxx安全策略檔案,定義服務進行,追加服務對binder使用等權限等(若涉及property寫需要自己添加);
2)在file_contexts中,新增service可執行檔案的安全背景關系;
3)在hwservice_context中,新增服務相關安全背景關系,及其關聯type型別;
4)如果涉及設備節點的訪問,還需要對應hal_xxx安全策略檔案添加回應訪問權限;
7. 在mk檔案添加編譯關系
在對應的包(專案不同組態檔有差異)編譯檔案添加新增hal模塊相關包,舉例:/build/target/product/emulator.mk
PRODUCT_PACKAGES += \
/* 省略 */
android.hardware.test@1.0-impl \
android.hardware.test@1.0-service \
參考
官方檔案:
https://source.android.google.cn/devices/architecture/hidl
https://source.android.google.cn/devices/architecture/hidl/code-style
HIDL概述:https://blog.csdn.net/u013357557/article/details/84561457
AndroidO Treble架構下Hal行程啟動及HIDL服務注冊程序:https://blog.csdn.net/yangwen123/article/details/79854267
Android P HAL層添加HIDL實體: https://blog.csdn.net/sinat_18179367/article/details/95940030
https://blog.csdn.net/qq_19923217/article/details/88398660
https://blog.csdn.net/qq_19923217/article/details/89173162
https://blog.csdn.net/kuang_tian_you/article/details/86599869
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/297614.html
標籤:其他
上一篇:iOS底層學習——KVC
