本文主要介紹Treble架構下的HAL&HIDL&Binder相關技術原理。Treble的詳細資料文檔,請參考Treble 官方文檔。
1. Treble 簡介
Android 8.0 版本的一項新元素是 Project Treble。這是 Android 操作系統框架在架構方面的一項重大改變,旨在讓制造商以更低的成本更輕松、更快速地將設備更新到新版 Android 系統。Project Treble 適用於搭載 Android 8.0 及后續版本的所有新設備(這種新的架構已經在 Pixel 手機的開發者預覽版中投入使用)。
1.1 系統更新
圖 1. Treble 推出前的 Android 更新環境
Android 7.x 及更早版本中沒有正式的供應商接口,因此設備制造商必須更新大量 Android 代碼才能將設備更新到新版 Android 系統:
圖 2. Treble 推出后的 Android 更新環境
Treble 提供了一個穩定的新供應商接口,供設備制造商訪問 Android 代碼中特定於硬件的部分,這樣一來,設備制造商只需更新 Android 操作系統框架,即可跳過芯片制造商直接提供新的 Android 版本:
1.2 Android 經典架構
為了更好的了解Treble 架構里面的HAL,首先了解一下Android的經典架構。
在Android O之前,HAL是一個個的.so庫,通過dlopen來進行打開,庫和framework位於同一個進程。如圖所示:
1.3 Trebe 架構
為了能夠讓Android O之前的版本升級到Android O,Android設計了Passthrough模式,經過轉換,可以方便的使用已經存在代碼,不需要重新編寫相關的HAL。HIDL分為兩種模式:Passthrough和Binderized。
- Binderized: Google官方翻譯成綁定試HAL。
- Passthrough:Google官方翻譯成直通式HAL。
大致框架圖如下,對於Android O之前的設備,對應圖1,對於從之前的設備升級到O的版本,對應圖2、圖3. 對於直接基於Android O開發的設備,對應圖4。
新的架構之下,framework和hal運行於不同的進程,所有的HAL采用新的HIDL技術來完成。
2. HIDL 深入理解
HIDL是一種接口定義語言,描述了HAL和它的用戶之間的接口。接下來深入分析一下HIDL相關實現。
2.1 hidl-gen工具
在Treble架構中,經常會提到HIDL
,首先介紹和HIDL
相關的一個工具hidl-gen
,系統定義的所有的.hal
接口,都是通過hidl-gen
工具轉換成對應的代碼。比如hardware/interfaces/power/1.0/IPower.hal
,會通過hidl-gen
轉換成out/soong/.intermediates/hardware/interfaces/power/1.0/android.hardware.power@1.0_genc++/gen/android/hardware/power/1.0/PowerAll.cpp
文件,為了深入了解,介紹相關原理,首先分析hidl-gen
。
hidl-gen
源碼路徑:system/tools/hidl,是在ubuntu上可執行的二進制文件。
使用方法:hidl-gen -o output-path -L language (-r interface-root) fqname
例子:
hidl-gen -Lmakefile -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport android.hardware.power@1.0
參數含義:
- -L: 語言類型,包括c++, c++-headers, c++-sources, export-header, c++-impl, java, java-constants, vts, makefile, androidbp, androidbp-impl, hash等。
hidl-gen
可根據傳入的語言類型產生不同的文件。 - fqname: 完全限定名稱的輸入文件。比如本例中
android.hardware.power@1.0
,要求在源碼目錄下必須有hardware/interfaces/power/1.0/
目錄。
- 對於單個文件來說,格式如下:
package@version::fileName
,比如android.hardware.power@1.0::types.Feature。 - 對於目錄來說。格式如下
package@version
,比如android.hardware.power@1.0。
- 對於單個文件來說,格式如下:
- -r: 格式package:path,可選,對fqname對應的文件來說,用來指定包名和文件所在的目錄到Android系統源碼根目錄的路徑。如果沒有制定,前綴默認是:android.hardware,目錄是
Android
源碼的根目錄。 - -o : 存放hidl-gen產生的中間文件的路徑。我們查看hardware/interfaces/power/1.0/Android.bp,可以看到,-o參數都是寫的
$(genDir)
,一般都是在out/soong/.intermediates/hardware/interfaces/power/1.0/
下面,根據-L
的不同,后面產生的路徑可能不太一樣,比如c++
,那么就會就是out/soong/.intermediates/hardware/interfaces/power/1.0/android.hardware.power@1.0_genc++/gen
,如果是c++-headers
,那么就是out/soong/.intermediates/hardware/interfaces/power/1.0/android.hardware.power@1.0_genc++_headers/gen
。
對於實例來說,fqname是:android.hardware.power@1.0
,包名是android.hardware
,文件所在的目錄是hardware/interfaces
。例子中的命令會在out/soong/.intermediates/hardware/interfaces/power/1.0/
下面產生對應的c++文件。
2.2 生成子hal的Android.mk
和Android.bp
文件
正如我們所知,所有的HIDL Interface
都是通過一個.hal
文件來描述,為了方便編譯生成每一個子hal。Google在系統默認提供了一個腳本update-makefiles.sh
,位於hardware/interfaces/
、frameworks/hardware/interfaces/
、system/hardware/interfaces/
、system/libhidl/
。以hardware/interfaces/
里面的代碼為實例做介紹。
#!/bin/bash source system/tools/hidl/update-makefiles-helper.sh do_makefiles_update \ "android.hardware:hardware/interfaces" \ "android.hidl:system/libhidl/transport"
這個腳本的主要作用:根據hal文件生成Android.mk(makefile)
和Android.bp(blueprint)
文件。在hardware/interfaces
的子目錄里面,存在.hal文件的目錄,都會產生Android.bp
和Android.mk
文件。詳細分析如下:
a. source system/tools下面的update-makefiles-helper.sh,然后執行do_makefiles_update
b. 解析傳入進去的參數。參數android.hardware:hardware/interfaces:
- android.hardware: android.hardware表示包名。
- hardware/interfaces:表示相對於根目錄的文件路徑。
會輸出如下LOG:
Updating makefiles for android.hardware in hardware/interfaces.
Updating ….
c. 獲取所有的包名。通過function get_packages()
函數,獲取hardware/interfaces
路徑下面的所有hal文件
所在的目錄路徑,比如子目錄power里面的hal文件的路徑是power/1.0,加上當前的參數包名hardware/interfaces
,通過點的方式連接,將nfc/1.0+hardware/interfaces里面的斜線轉換成點,最終獲取的包名就是 android.hardware.power@1.0,依次類推獲取所有的包名。
d. 執行hidl-gen命令.將c步驟里面獲取的參數和包名還有類名傳入hidl-gen命令,在hardware/interfaces/power/1.0目錄下產生Android.mk
和Android.bp
文件。
Android.mk
: hidl-gen -Lmakefile -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport android.hardware.power@1.0Android.bp
: hidl-gen -Landroidbp -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport android.hardware.power@1.0
關於hidl-gen,后續章節會介紹。
e. 在hardware/interfaces的每個子目錄下面產生Android.bp
文件,文件內容主要是subdirs的初始化
,存放當前目錄需要包含的子目錄。比如hardware/interfaces/power/
下面的Android.bp
文件。
@hardware/interfaces/power/Android.bp
// This is an autogenerated file, do not edit.
subdirs = [ "1.0", "1.0/default", "1.0/vts/functional", ]
意思就是說,編譯的時候,需要編譯hardware/interfaces/power
目錄下面的三個子目錄。
經過以上步驟,就會在對應的子目錄產生Android.mk
和Android.bp
文件。這樣以后我們就可以執行正常的編譯命令進行編譯了。比如mmm hardware/interfaces/power/
,默認情況下,在源碼中,Android.mk
和Android.bp
文件已經存在。
2.3 轉換.hal 文件為代碼
如前面所示,每個接口都是定義在.hal文件里面,比如hardware/interfaces/power/1.0/IPower.hal
,通過hidl-gen
生成的android.bp
文件里面會定義
filegroup { name: "android.hardware.power@1.0_hal", srcs: [ "types.hal", "IPower.hal", ], } genrule { name: "android.hardware.power@1.0_genc++", tools: ["hidl-gen"], cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.power@1.0", srcs: [ ":android.hardware.power@1.0_hal", ], out: [ "android/hardware/power/1.0/types.cpp", "android/hardware/power/1.0/PowerAll.cpp", ], }
可以看到在Android.bp
里面,通過hidl-gen
在out下面產生了types.cpp
和PowerAll.cpp
. 實際例子很多,不做詳細介紹。
對於生成的PowerAll.cpp來說,我們可以看到,除了IPower.hal
里面定義的函數之外,還生成了很多其他的方法,這個是hidl-gen
默認產生,為了能夠支持binder
通信。在IPower.hal
里面定義的setInteractive(bool interactive);
,在PowerAll.cpp
里面對應的是BpHwPower::setInteractive(bool interactive)
。通過命名就可以知道,這個和Binder機制里面的命名一致。代碼如下:
::android::hardware::Return<void> BpHwPower::setInteractive(bool interactive) { atrace_begin(ATRACE_TAG_HAL, "HIDL::IPower::setInteractive::client"); #ifdef __ANDROID_DEBUGGABLE__ if (UNLIKELY(mEnableInstrumentation)) { std::vector<void *> _hidl_args; _hidl_args.push_back((void *)&interactive); for (const auto &callback: mInstrumentationCallbacks) { callback(InstrumentationEvent::CLIENT_API_ENTRY, "android.hardware.power", "1.0", "IPower", "setInteractive", &_hidl_args); } } #endif // __ANDROID_DEBUGGABLE__ ::android::hardware::Parcel _hidl_data; ::android::hardware::Parcel _hidl_reply; ::android::status_t _hidl_err; ::android::hardware::Status _hidl_status; _hidl_err = _hidl_data.writeInterfaceToken(IPower::descriptor); if (_hidl_err != ::android::OK) { goto _hidl_error; } _hidl_err = _hidl_data.writeBool(interactive); if (_hidl_err != ::android::OK) { goto _hidl_error; } _hidl_err = remote()->transact(1 /* setInteractive */, _hidl_data, &_hidl_reply); if (_hidl_err != ::android::OK) { goto _hidl_error; } _hidl_err = ::android::hardware::readFromParcel(&_hidl_status, _hidl_reply); if (_hidl_err != ::android::OK) { goto _hidl_error; } if (!_hidl_status.isOk()) { return _hidl_status; } atrace_end(ATRACE_TAG_HAL); #ifdef __ANDROID_DEBUGGABLE__ if (UNLIKELY(mEnableInstrumentation)) { std::vector<void *> _hidl_args; for (const auto &callback: mInstrumentationCallbacks) { callback(InstrumentationEvent::CLIENT_API_EXIT, "android.hardware.power", "1.0", "IPower", "setInteractive", &_hidl_args); } } #endif // __ANDROID_DEBUGGABLE__ _hidl_status.setFromStatusT(_hidl_err); return ::android::hardware::Return<void>(); _hidl_error: _hidl_status.setFromStatusT(_hidl_err); return ::android::hardware::Return<void>(_hidl_status); }
經過以上步驟,.hal
文件就轉換成了對應的代碼,而且具備了Binder通信的能力。
HIDL整個流程如圖所示:
本文主要介紹Treble架構下的HAL&HIDL&Binder相關技術原理。Treble的詳細資料文檔,請參考Treble 官方文檔。
1. Treble 簡介
Android 8.0 版本的一項新元素是 Project Treble。這是 Android 操作系統框架在架構方面的一項重大改變,旨在讓制造商以更低的成本更輕松、更快速地將設備更新到新版 Android 系統。Project Treble 適用於搭載 Android 8.0 及后續版本的所有新設備(這種新的架構已經在 Pixel 手機的開發者預覽版中投入使用)。
1.1 系統更新
圖 1. Treble 推出前的 Android 更新環境
Android 7.x 及更早版本中沒有正式的供應商接口,因此設備制造商必須更新大量 Android 代碼才能將設備更新到新版 Android 系統:
圖 2. Treble 推出后的 Android 更新環境
Treble 提供了一個穩定的新供應商接口,供設備制造商訪問 Android 代碼中特定於硬件的部分,這樣一來,設備制造商只需更新 Android 操作系統框架,即可跳過芯片制造商直接提供新的 Android 版本:
1.2 Android 經典架構
為了更好的了解Treble 架構里面的HAL,首先了解一下Android的經典架構。
在Android O之前,HAL是一個個的.so庫,通過dlopen來進行打開,庫和framework位於同一個進程。如圖所示:
1.3 Trebe 架構
為了能夠讓Android O之前的版本升級到Android O,Android設計了Passthrough模式,經過轉換,可以方便的使用已經存在代碼,不需要重新編寫相關的HAL。HIDL分為兩種模式:Passthrough和Binderized。
- Binderized: Google官方翻譯成綁定試HAL。
- Passthrough:Google官方翻譯成直通式HAL。
大致框架圖如下,對於Android O之前的設備,對應圖1,對於從之前的設備升級到O的版本,對應圖2、圖3. 對於直接基於Android O開發的設備,對應圖4。
新的架構之下,framework和hal運行於不同的進程,所有的HAL采用新的HIDL技術來完成。
2. HIDL 深入理解
HIDL是一種接口定義語言,描述了HAL和它的用戶之間的接口。接下來深入分析一下HIDL相關實現。
2.1 hidl-gen工具
在Treble架構中,經常會提到HIDL
,首先介紹和HIDL
相關的一個工具hidl-gen
,系統定義的所有的.hal
接口,都是通過hidl-gen
工具轉換成對應的代碼。比如hardware/interfaces/power/1.0/IPower.hal
,會通過hidl-gen
轉換成out/soong/.intermediates/hardware/interfaces/power/1.0/android.hardware.power@1.0_genc++/gen/android/hardware/power/1.0/PowerAll.cpp
文件,為了深入了解,介紹相關原理,首先分析hidl-gen
。
hidl-gen
源碼路徑:system/tools/hidl,是在ubuntu上可執行的二進制文件。
使用方法:hidl-gen -o output-path -L language (-r interface-root) fqname
例子:
hidl-gen -Lmakefile -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport android.hardware.power@1.0
參數含義:
- -L: 語言類型,包括c++, c++-headers, c++-sources, export-header, c++-impl, java, java-constants, vts, makefile, androidbp, androidbp-impl, hash等。
hidl-gen
可根據傳入的語言類型產生不同的文件。 - fqname: 完全限定名稱的輸入文件。比如本例中
android.hardware.power@1.0
,要求在源碼目錄下必須有hardware/interfaces/power/1.0/
目錄。
- 對於單個文件來說,格式如下:
package@version::fileName
,比如android.hardware.power@1.0::types.Feature。 - 對於目錄來說。格式如下
package@version
,比如android.hardware.power@1.0。
- 對於單個文件來說,格式如下:
- -r: 格式package:path,可選,對fqname對應的文件來說,用來指定包名和文件所在的目錄到Android系統源碼根目錄的路徑。如果沒有制定,前綴默認是:android.hardware,目錄是
Android
源碼的根目錄。 - -o : 存放hidl-gen產生的中間文件的路徑。我們查看hardware/interfaces/power/1.0/Android.bp,可以看到,-o參數都是寫的
$(genDir)
,一般都是在out/soong/.intermediates/hardware/interfaces/power/1.0/
下面,根據-L
的不同,后面產生的路徑可能不太一樣,比如c++
,那么就會就是out/soong/.intermediates/hardware/interfaces/power/1.0/android.hardware.power@1.0_genc++/gen
,如果是c++-headers
,那么就是out/soong/.intermediates/hardware/interfaces/power/1.0/android.hardware.power@1.0_genc++_headers/gen
。
對於實例來說,fqname是:android.hardware.power@1.0
,包名是android.hardware
,文件所在的目錄是hardware/interfaces
。例子中的命令會在out/soong/.intermediates/hardware/interfaces/power/1.0/
下面產生對應的c++文件。
2.2 生成子hal的Android.mk
和Android.bp
文件
正如我們所知,所有的HIDL Interface
都是通過一個.hal
文件來描述,為了方便編譯生成每一個子hal。Google在系統默認提供了一個腳本update-makefiles.sh
,位於hardware/interfaces/
、frameworks/hardware/interfaces/
、system/hardware/interfaces/
、system/libhidl/
。以hardware/interfaces/
里面的代碼為實例做介紹。
#!/bin/bash source system/tools/hidl/update-makefiles-helper.sh do_makefiles_update \ "android.hardware:hardware/interfaces" \ "android.hidl:system/libhidl/transport"
這個腳本的主要作用:根據hal文件生成Android.mk(makefile)
和Android.bp(blueprint)
文件。在hardware/interfaces
的子目錄里面,存在.hal文件的目錄,都會產生Android.bp
和Android.mk
文件。詳細分析如下:
a. source system/tools下面的update-makefiles-helper.sh,然后執行do_makefiles_update
b. 解析傳入進去的參數。參數android.hardware:hardware/interfaces:
- android.hardware: android.hardware表示包名。
- hardware/interfaces:表示相對於根目錄的文件路徑。
會輸出如下LOG:
Updating makefiles for android.hardware in hardware/interfaces.
Updating ….
c. 獲取所有的包名。通過function get_packages()
函數,獲取hardware/interfaces
路徑下面的所有hal文件
所在的目錄路徑,比如子目錄power里面的hal文件的路徑是power/1.0,加上當前的參數包名hardware/interfaces
,通過點的方式連接,將nfc/1.0+hardware/interfaces里面的斜線轉換成點,最終獲取的包名就是 android.hardware.power@1.0,依次類推獲取所有的包名。
d. 執行hidl-gen命令.將c步驟里面獲取的參數和包名還有類名傳入hidl-gen命令,在hardware/interfaces/power/1.0目錄下產生Android.mk
和Android.bp
文件。
Android.mk
: hidl-gen -Lmakefile -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport android.hardware.power@1.0Android.bp
: hidl-gen -Landroidbp -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport android.hardware.power@1.0
關於hidl-gen,后續章節會介紹。
e. 在hardware/interfaces的每個子目錄下面產生Android.bp
文件,文件內容主要是subdirs的初始化
,存放當前目錄需要包含的子目錄。比如hardware/interfaces/power/
下面的Android.bp
文件。
@hardware/interfaces/power/Android.bp
// This is an autogenerated file, do not edit.
subdirs = [ "1.0", "1.0/default", "1.0/vts/functional", ]
意思就是說,編譯的時候,需要編譯hardware/interfaces/power
目錄下面的三個子目錄。
經過以上步驟,就會在對應的子目錄產生Android.mk
和Android.bp
文件。這樣以后我們就可以執行正常的編譯命令進行編譯了。比如mmm hardware/interfaces/power/
,默認情況下,在源碼中,Android.mk
和Android.bp
文件已經存在。
2.3 轉換.hal 文件為代碼
如前面所示,每個接口都是定義在.hal文件里面,比如hardware/interfaces/power/1.0/IPower.hal
,通過hidl-gen
生成的android.bp
文件里面會定義
filegroup { name: "android.hardware.power@1.0_hal", srcs: [ "types.hal", "IPower.hal", ], } genrule { name: "android.hardware.power@1.0_genc++", tools: ["hidl-gen"], cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.power@1.0", srcs: [ ":android.hardware.power@1.0_hal", ], out: [ "android/hardware/power/1.0/types.cpp", "android/hardware/power/1.0/PowerAll.cpp", ], }
可以看到在Android.bp
里面,通過hidl-gen
在out下面產生了types.cpp
和PowerAll.cpp
. 實際例子很多,不做詳細介紹。
對於生成的PowerAll.cpp來說,我們可以看到,除了IPower.hal
里面定義的函數之外,還生成了很多其他的方法,這個是hidl-gen
默認產生,為了能夠支持binder
通信。在IPower.hal
里面定義的setInteractive(bool interactive);
,在PowerAll.cpp
里面對應的是BpHwPower::setInteractive(bool interactive)
。通過命名就可以知道,這個和Binder機制里面的命名一致。代碼如下:
::android::hardware::Return<void> BpHwPower::setInteractive(bool interactive) { atrace_begin(ATRACE_TAG_HAL, "HIDL::IPower::setInteractive::client"); #ifdef __ANDROID_DEBUGGABLE__ if (UNLIKELY(mEnableInstrumentation)) { std::vector<void *> _hidl_args; _hidl_args.push_back((void *)&interactive); for (const auto &callback: mInstrumentationCallbacks) { callback(InstrumentationEvent::CLIENT_API_ENTRY, "android.hardware.power", "1.0", "IPower", "setInteractive", &_hidl_args); } } #endif // __ANDROID_DEBUGGABLE__ ::android::hardware::Parcel _hidl_data; ::android::hardware::Parcel _hidl_reply; ::android::status_t _hidl_err; ::android::hardware::Status _hidl_status; _hidl_err = _hidl_data.writeInterfaceToken(IPower::descriptor); if (_hidl_err != ::android::OK) { goto _hidl_error; } _hidl_err = _hidl_data.writeBool(interactive); if (_hidl_err != ::android::OK) { goto _hidl_error; } _hidl_err = remote()->transact(1 /* setInteractive */, _hidl_data, &_hidl_reply); if (_hidl_err != ::android::OK) { goto _hidl_error; } _hidl_err = ::android::hardware::readFromParcel(&_hidl_status, _hidl_reply); if (_hidl_err != ::android::OK) { goto _hidl_error; } if (!_hidl_status.isOk()) { return _hidl_status; } atrace_end(ATRACE_TAG_HAL); #ifdef __ANDROID_DEBUGGABLE__ if (UNLIKELY(mEnableInstrumentation)) { std::vector<void *> _hidl_args; for (const auto &callback: mInstrumentationCallbacks) { callback(InstrumentationEvent::CLIENT_API_EXIT, "android.hardware.power", "1.0", "IPower", "setInteractive", &_hidl_args); } } #endif // __ANDROID_DEBUGGABLE__ _hidl_status.setFromStatusT(_hidl_err); return ::android::hardware::Return<void>(); _hidl_error: _hidl_status.setFromStatusT(_hidl_err); return ::android::hardware::Return<void>(_hidl_status); }
經過以上步驟,.hal
文件就轉換成了對應的代碼,而且具備了Binder通信的能力。
HIDL整個流程如圖所示:
3. HAL通信機制(c++)
在Treble架構中,framework/vendor之間的通信通過HIDL
接口和dev/hwbinder
的IPC域來完成。而且HIDL接口有兩種通信模式Passthrough
和Binderized
。接下來我們介紹兩種模式下的交互原理。創建HAL服務器有兩種模式:
- defaultPassthroughServiceImplementation
int main() { return defaultPassthroughServiceImplementation<IPower>(); }
- registerAsService
int main(int /* argc */, char* /* argv */ []) { sp<IDumpstateDevice> dumpstate = new DumpstateDevice; configureRpcThreadpool(1, true /* will join */); if (dumpstate->registerAsService() != OK) { ALOGE("Could not register service."); return 1; } joinRpcThreadpool(); ALOGE("Service exited!"); return 1; }
接下來我們分別介紹兩種類型的詳細過程。
3.1 defaultPassthroughServiceImplementation
首先介紹Passthrough模式的HIDL實現機制。以hardware/interfaces/power/1.0
作為例子。當編譯hardware/interfaces/power/1.0
的時候,會生成:
- 中間文件
PowerAll.cpp
/vendor/bin/hw/android.hardware.power@1.0-service
的可執行文件/vendor/lib/hw/android.hardware.power@1.0-impl.so
的庫文件android.hardware.power@1.0-service.rc
會被拷貝到vendor.img里面的vendor/etc/init
目錄。rc文件的內容如下:
service power-hal-1-0 /vendor/bin/hw/android.hardware.power@1.0-service class hal user system group system
接下來我們就一步步分析,power Server
是如何初始化的。
- 對於init的解析機制,本文不做描述,在開機過程的某一個階段,系統會啟動class是hal的服務,會執行
/vendor/bin/hw/android.hardware.power@1.0-service
,從而調用hardware/interfaces/power/1.0/default/service.cpp
的main
方法。代碼如下:
int main() { return defaultPassthroughServiceImplementation<IPower>(); }
接下來會調用
@PowerAll.cpp
:android::sp<IPower> IPower::getService(const std::string &serviceName, const bool getStub) { using ::android::hardware::defaultServiceManager; using ::android::hardware::details::waitForHwService; using ::android::hardware::getPassthroughServiceManager; using ::android::hardware::Return; using ::android::sp; using Transport = ::android::hidl::manager::V1_0::IServiceManager::Transport; sp<IPower> iface = nullptr; // 獲取HwServiceManager const sp<::android::hidl::manager::V1_0::IServiceManager> sm = defaultServiceManager(); if (sm == nullptr) { ALOGE("getService: defaultServiceManager() is null"); return nullptr; } // 獲取當前Tranport類型,passthrough或者binderized Return<Transport> transportRet = sm->getTransport(IPower::descriptor, serviceName); if (!transportRet.isOk()) { ALOGE("getService: defaultServiceManager()->getTransport returns %s", transportRet.description().c_str()); return nullptr; } Transport transport = transportRet; const bool vintfHwbinder = (transport == Transport::HWBINDER); const bool vintfPassthru = (transport == Transport::PASSTHROUGH); // 返回當前的接口類 for (int tries = 0; !getStub && (vintfHwbinder || (vintfLegacy && tries == 0)); tries++) { if (tries > 1) { ALOGI("getService: Will do try %d for %s/%s in 1s...", tries, IPower::descriptor, serviceName.c_str()); sleep(1); } if (vintfHwbinder && tries > 0) { waitForHwService(IPower::descriptor, serviceName); } Return<sp<::android::hidl::base::V1_0::IBase>> ret = sm->get(IPower::descriptor, serviceName); if (!ret.isOk()) { ALOGE("IPower: defaultServiceManager()->get returns %s", ret.description().c_str()); break; } sp<::android::hidl::base::V1_0::IBase> base = ret; if (base == nullptr) { if (tries > 0) { ALOGW("IPower: found null hwbinder interface"); }continue; } Return<sp<IPower>> castRet = IPower::castFrom(base, true /* emitError */); // ... iface = castRet; if (iface == nullptr) { ALOGW("IPower: received incompatible service; bug in hwservicemanager?"); break; } return iface; } // 獲取passthrough模式的類。 if (getStub || vintfPassthru || vintfLegacy) { const sp<::android::hidl::manager::V1_0::IServiceManager> pm = getPassthroughServiceManager(); if (pm != nullptr) { Return<sp<::android::hidl::base::V1_0::IBase>> ret = pm->get(IPower::descriptor, serviceName); if (ret.isOk()) { sp<::android::hidl::base::V1_0::IBase> baseInterface = ret; if (baseInterface != nullptr) { iface = new BsPower(IPower::castFrom(baseInterface)); } } } } return iface; }
- defaultPassthroughServiceImplementation(); @hardware/interfaces/power/1.0/default/service.cpp
- IPower::getService @PowerAll.cpp 從HwServiceManager里面獲取注冊的服務。默認情況下是沒有注冊這個服務的。
- defaultServiceManager @system/libhidl/transport/ServiceManagement.cpp 打開
/dev/hwbinder
,通過binder通信,獲取HwServiceManager服務端。 -
sm->getTransport 基本就是按照Binder通信的機制來實現相關的流程。通過
HwBinder
調用服務端的getTransPort
方法。- BpHwServiceManager::getTransport @ServiceManagerAll.cpp
- BpHwBinder::transact
- IPCThreadState::self()->transact
- IPCThreadState::transact writeTransactionData waitForResponse
- IPCThreadState::executeCommand
-
ServiceManager::getTransport@system/hwservicemanager/ServiceManager.cpp
- getTransport @ system/hwservicemanager/Vintf.cpp 根據framework hal和device hal配置的manifest.xml里面的定義,來判斷當前的傳輸類型是HwBinder還是Passthrough模式。在
vendor/manifest.xml
里面,power配置的是hwbinder,所以最終就是hwBinder模式。(后續會講解manifest.xml的原理)
- getTransport @ system/hwservicemanager/Vintf.cpp 根據framework hal和device hal配置的manifest.xml里面的定義,來判斷當前的傳輸類型是HwBinder還是Passthrough模式。在
由於我們采取的是defaultPassthroughServiceImplementation<IPower>();
進行注冊,所以getStub=true
.所以會走到const sp<::android::hidl::manager::V1_0::IServiceManager> pm = getPassthroughServiceManager();
- getPassthroughServiceManager @ PowerAll.cpp
獲取passthrough服務管理。
- 調用PassthroughServiceManager的get(const hidl_string& fqName, const hidl_string& name)函數 @ServiceManagement.cpp
, 根據傳入的fqName=(android.hardware.power@1.0::IPower")
,獲取當前的接口名IPower
,拼接出后面需要載入的函數名HIDL_FETCH_IPower
和庫名字android.hardware.power@1.0-impl
,接着通過dlopen
載入/vendor/lib/hw/android.hardware.power@1.0-impl.so
,然后通過dlsym
載入HIDL_FETCH_IPower
函數。 代碼如下:
@hardware/interfaces/power/1.0/default/Power.cpp
IPower* HIDL_FETCH_IPower(const char* /* name */) { const hw_module_t* hw_module = nullptr; power_module_t* power_module = nullptr; int err = hw_get_module(POWER_HARDWARE_MODULE_ID, &hw_module); if (err) { ALOGE("hw_get_module %s failed: %d", POWER_HARDWARE_MODULE_ID, err); return nullptr; } if (!hw_module->methods || !hw_module->methods->open) { power_module = reinterpret_cast<power_module_t*>( const_cast<hw_module_t*>(hw_module)); } else { err = hw_module->methods->open( hw_module, POWER_HARDWARE_MODULE_ID, reinterpret_cast<hw_device_t**>(&power_module)); if (err) { ALOGE("Passthrough failed to load legacy HAL."); return nullptr; } } return new Power(power_module); }
通過hw_get_module
就和Android O
以前的Hal模式一致,這正是Passthrough復用原有hal的原理,測試用的是模擬器,所以最終獲取的庫文件是/system/lib/hw/power.ranchu.so
,后續所有的和Power有關的接口調用,最終都是通過power.ranchu.so
來實現功能。
接下來會調用registerReference("android.hardware.power@1.0::IPower","default")
,接着調用BpHwServiceManager::registerPassthroughClient
將fqName
和服務名,注冊進hwservicemanager
的mServiceMap
對象里面。
Return<void> ServiceManager::registerPassthroughClient(const hidl_string &fqName,
const hidl_string &name) { pid_t pid = IPCThreadState::self()->getCallingPid(); if (!mAcl.canGet(fqName, pid)) { /* We guard this function with "get", because it's typically used in * the getService() path, albeit for a passthrough service in this * case */ return Void(); } PackageInterfaceMap &ifaceMap = mServiceMap[fqName]; if (name.empty()) { LOG(WARNING) << "registerPassthroughClient encounters empty instance name for " << fqName.c_str(); return Void(); } HidlService *service = ifaceMap.lookup(name); if (service == nullptr) { auto adding = std::make_unique<HidlService>(fqName, name); adding->registerPassthroughClient(pid); ifaceMap.insertService(std::move(adding)); } else { service->registerPassthroughClient(pid); } return Void(); }
-
返回
android::hidl::base::V1_0::IBase
實例。 -
new BsPower:首先會通過interfaceChain判斷當前的interface是否支持轉換,然后傳入包名和接口名
"android.hardware.power@1.0", "IPower"
構造出一個new BsPower
的實例。 -
IPower::registerAsService 接下來,調用
status_t status = service->registerAsService(name)
,首先會創建BnHwPower
對象,然后將當前的service 添加進hwservicemanager
里面。初始化BnHwPower
的過程中, _hidl_mImpl實際上就是BsPower
的引用。代碼如下。 。
BnHwPower::BnHwPower(const ::android::sp<IPower> &_hidl_impl) : ::android::hidl::base::V1_0::BnHwBase(_hidl_impl, "android.hardware.power@1.0", "IPower") { _hidl_mImpl = _hidl_impl; auto prio = ::android::hardware::details::gServicePrioMap.get(_hidl_impl, {SCHED_NORMAL, 0}); mSchedPolicy = prio.sched_policy; mSchedPriority = prio.prio; }
然后調用如下步驟,將當前通信加入IPC Binder的線程池進行循環。
- android::hardware::joinRpcThreadpool at system/libhidl/transport/HidlTransportSupport.cpp:28 加入RpcThreadPool。
- android::hardware::joinBinderRpcThreadpool at system/libhidl/transport/HidlBinderSupport.cpp:188
- android::hardware::IPCThreadState::joinThreadPool at system/libhwbinder/IPCThreadState.cpp:497
- android::hardware::IPCThreadState::getAndExecuteCommand at system/libhwbinder/IPCThreadState.cpp:443
至此,android.hardware.power@1.0::IPower
服務就啟動成功了,可以響應客戶端的請求了。
總結,通過defaultPassthroughServiceImplementation
把當前的服務注冊進HwServiceManager
,每個服務都是一個HidlService
。然后就可以等待客戶端的調用。
3.2 registerAsService 創建HAL
根據Android源碼網站介紹,android.hardware.dumpstate@1.0是屬於綁定式HAL。接下來我們分析dumpstate服務初始化的流程。代碼位於:hardware/interfaces/dumpstate/1.0/default/
,查看service.cpp
,代碼如下:
int main(int /* argc */, char* /* argv */ []) { sp<IDumpstateDevice> dumpstate = new DumpstateDevice; configureRpcThreadpool(1, true /* will join */); if (dumpstate->registerAsService() != OK) { ALOGE("Could not register service."); return 1; } joinRpcThreadpool(); ALOGE("Service exited!"); return 1; }
- IDumpstateDevice::registerAsService
- android::hardware::details::onRegistration(“android.hardware.dumpstate@1.0”, “IDumpstateDevice”, serviceName)
- tryShortenProcessName 設置當前進程的名字,長度最多為16。android.hardware.dumpstate@1.0-service
- BpHwServiceManager::add
- ServiceManager::add @system/hwservicemanager/ServiceManager.cpp 注意和binder的區別。將當前的
service
添加進mInstanceMap。
- ServiceManager::add @system/hwservicemanager/ServiceManager.cpp 注意和binder的區別。將當前的
- 收到HwBinder驅動的 BR_TRANSACTION 消息,然后執行 BHwBinder::transact
- BnHwDumpstateDevice::onTransact
- joinRpcThreadpool(); 把當前的通信加入HwBinder的線程池進行循環。
至此,registerAsService 創建HAL Service就完成了。
3.2 Binderized 模式 client和服務端的交互
服務注冊成功之后,客戶端就可以調用相關服務提供的功能。
以點擊屏幕為實例說明,當我們點擊屏幕的時候,會調用com_android_server_power_PowerManagerService.cpp
的android_server_PowerManagerService_userActivity
函數,代碼如下:
void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
// Tell the power HAL when user activity occurs.
gPowerHalMutex.lock();
if (getPowerHal()) { Return<void> ret = gPowerHal->powerHint(PowerHint::INTERACTION, 0); processReturn(ret, "powerHint"); } // ... } } // Check validity of current handle to the power HAL service, and call getService() if necessary. // The caller must be holding gPowerHalMutex. bool getPowerHal() { if (gPowerHalExists && gPowerHal == nullptr) { gPowerHal = IPower::getService(); if (gPowerHal != nullptr) { ALOGI("Loaded power HAL service"); } else { ALOGI("Couldn't load power HAL service"); gPowerHalExists = false; } } return gPowerHal != nullptr; }
在getPowerHal
里面,通過IPower::getService();
方法經過HwBinder
通信,獲取服務端的引用。主要包含如下步驟:
-
IPower::getService() 獲取IPower的服務。返回遠程服務的代理
gPowerHal
,最終返回的是BpHwPower。IPower::getService(const std::string &serviceName, const bool getStub)@PowerApp.cpp
。- BpHwServiceManager::getTransport 獲取當前的傳輸類型,
passthrough
或者binderized
。Power是binderized
,返回對應的服務代理。 - sm->get(IPower::descriptor, serviceName) 從ServiceManager里面獲取描述是
android.hardware.power@1.0::IPower
,服務名是default
的hidlservice
的引用。 - IPower::castFrom(base, true /* emitError */)
- android::hardware::details::castInterface 將
hidlservice
服務的引用轉換成Binder對象。 - ::android::hardware::IInterface::asBinder(static_cast
3.4 pathrough 模式 client和服務端的交互
查詢manifest.xml
可以發現。android.hardware.graphics.mapper
是passthrough的模式。
<hal format="hidl"> <name>android.hardware.graphics.mapper</name> <transport arch="32+64">passthrough</transport> <version>2.0</version> <interface> <name>IMapper</name> <instance>default</instance> </interface> </hal>
以hardware/interfaces/graphics/mapper/2.0/
作為例子進行分析。
@frameworks/native/libs/ui/Gralloc2.cpp
Mapper::Mapper() { mMapper = IMapper::getService(); if (mMapper == nullptr || mMapper->isRemote()) { LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode"); } }
// static
::android::sp IMapper::getService(const std::string &serviceName, const bool getStub) {
using ::android::hardware::defaultServiceManager;
using ::android::hardware::details::waitForHwService;
using ::android::hardware::getPassthroughServiceManager;
using ::android::hardware::Return;
using ::android::sp;
using Transport = ::android::hidl::manager::V1_0::IServiceManager::Transport;
sp<IMapper> iface = nullptr;
const sp<::android::hidl::manager::V1_0::IServiceManager> sm = defaultServiceManager();
if (sm == nullptr) {
ALOGE("getService: defaultServiceManager() is null");
return nullptr;
}
Return<Transport> transportRet = sm->getTransport(IMapper::descriptor, serviceName);
if (!transportRet.isOk()) {
ALOGE("getService: defaultServiceManager()->getTransport returns %s", transportRet.description().c_str());
return nullptr;
}
Transport transport = transportRet;
const bool vintfHwbinder = (transport == Transport::HWBINDER);
const bool vintfPassthru = (transport == Transport::PASSTHROUGH);
// ...
if (getStub || vintfPassthru || vintfLegacy) {
const sp<::android::hidl::manager::V1_0::IServiceManager> pm = getPassthroughServiceManager();
if (pm != nullptr) {
Return<sp<::android::hidl::base::V1_0::IBase>> ret =
pm->get(IMapper::descriptor, serviceName);
if (ret.isOk()) {
sp<::android::hidl::base::V1_0::IBase> baseInterface = ret;
if (baseInterface != nullptr) {
iface = new BsMapper(IMapper::castFrom(baseInterface));
}
}
}
}
return iface;
}
- 步驟和前面的一致,由於是passthrough的模式,調用
PassthroughServiceManager
的get(const hidl_string& fqName, const hidl_string& name)
函數@ServiceManagement.cpp
, 根據傳入的fqName=(android.hardware.graphics.mapper@2.0::IMapper")
,獲取當前的接口名IMapper
,拼接出后面需要載入的函數名HIDL_FETCH_IMapper
和庫名字android.hardware.graphics.mapper@2.0-impl
,接着通過dlopen載入android.hardware.graphics.mapper@2.0-impl
,然后通過dlsym載入HIDL_FETCH_IMapper函數。
這樣就實現了passthrough模式下的通信了。
4. HAL 通信 (JAVA)
以hardware/interfaces/radio/1.0/
作為例子:
當我們編譯hardware/interfaces/radio/1.0/
的時候,會編譯出:
- android.hardware.radio-V1.0-java-static
- out/target/common/gen/JAVA_LIBRARIES/android.hardware.radio-V1.0-java-static_intermediates/android/hardware/radio/V1_0/IRadio.java
接下來我們以
@frameworks/opt/telephony/Android.mk 最為例子,直接引用android.hardware.radio-V1.0-java-static
,然后就可以使用里面的相關代碼。
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) // ... LOCAL_JAVA_LIBRARIES := voip-common ims-common LOCAL_STATIC_JAVA_LIBRARIES := android.hardware.radio-V1.0-java-static \ android.hardware.radio.deprecated-V1.0-java-static LOCAL_MODULE_TAGS := optional LOCAL_MODULE := telephony-common // ... include $(BUILD_JAVA_LIBRARY)
接下來我們看一下使用的地方。
@RIL.java
try { mRadioProxy = IRadio.getService(HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId]); if (mRadioProxy != null) { mRadioProxy.linkToDeath(mRadioProxyDeathRecipient, mRadioProxyCookie.incrementAndGet()); mRadioProxy.setResponseFunctions(mRadioResponse, mRadioIndication); } else { riljLoge("getRadioProxy: mRadioProxy == null"); } } catch (RemoteException | RuntimeException e) { mRadioProxy = null; riljLoge("RadioProxy getService/setResponseFunctions: " + e); }
首先會直接調用IRadio.getService
來獲取相關服務。
@IRadio.java
public static IRadio getService(String serviceName) throws android.os.RemoteException { return IRadio.asInterface(android.os.HwBinder.getService("android.hardware.radio@1.0::IRadio",serviceName)); }
android.os.HwBinder.getService(“android.hardware.radio@1.0::IRadio”,serviceName)
JNI
@frameworks/base/core/jni/android_os_HwBinder.cpp
static jobject JHwBinder_native_getService(
JNIEnv *env, jclass /* clazzObj */, jstring ifaceNameObj, jstring serviceNameObj) { ///... auto manager = hardware::defaultServiceManager(); // ... Return<IServiceManager::Transport> transportRet = manager->getTransport(ifaceNameHStr, serviceNameHStr); if (!transportRet.isOk()) { signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */); return NULL; } IServiceManager::Transport transport = transportRet; // ... java 類型的傳輸模式必須是HwBinder if (transport != IServiceManager::Transport::HWBINDER && !vintfLegacy) { LOG(ERROR) << "service " << ifaceName << " declares transport method " << toString(transport) << " but framework expects hwbinder."; signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */); return NULL; } // 獲取接口引用。 Return<sp<hidl::base::V1_0::IBase>> ret = manager->get(ifaceNameHStr, serviceNameHStr); if (!ret.isOk()) { signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */); return NULL; } // 轉換成Binder接口 sp<hardware::IBinder> service = hardware::toBinder< hidl::base::V1_0::IBase, hidl::base::V1_0::BpHwBase>(ret); if (service == NULL) { signalExceptionForError(env, NAME_NOT_FOUND); return NULL; } LOG(INFO) << "Starting thread pool."; ::android::hardware::ProcessState::self()->startThreadPool(); // 返回JHwRemoteBinder對象。 return JHwRemoteBinder::NewObject(env, service); }
以上步驟和C++里面的獲取服務步驟類似。通過IRadio.getService()
獲取相關的服務,進入JNI的相關接口,獲取HwServiceManager
服務,然后獲取當前HAL的類型(必須是Binderized),接下來獲取服務對應的接口引用,接着將當前接口轉換成Ibinder引用,然后創建JHwRemoteBinder
對象返回給java層。
IRadio.asInterface(android.os.HwBinder.getService("android.hardware.radio@1.0::IRadio",serviceName))
- 1
java層接着調用IRadio.asInterface
將Hwbinder
引用轉換成IRadio
對象。
這樣就可以通過IRadio
對象調用
5. Vendor Interface Object
5.1 manifest.xml 和 compatibility_matrix.xml
在system分區和vendor分區,分別存在manifest.xml和compatibility_matrix.xml。內容大致如下:
<manifest version="1.0" type="framework"> <hal format="hidl"> <name>android.frameworks.displayservice</name> <transport>hwbinder</transport> <version>1.0</version> <interface> <name>IDisplayService</name> <instance>default</instance> </interface> </hal> <hal format="hidl"> <name>android.frameworks.schedulerservice</name> <transport>hwbinder</transport> <version>1.0</version> <interface> <name>ISchedulingPolicyService</name> <instance>default</instance> </interface> </hal> ... </manifest>
分為兩類:
- framework相關的,Google默認定義完成。
- device相關,有設備廠商自定義。
device可以通過DEVICE_MANIFEST_FILE
和DEVICE_MATRIX_FILE
指定自己的manifest.xml文件。如高通平台的項目:
DEVICE_MANIFEST_FILE := device/qcom/msm8937_64/manifest.xml DEVICE_MATRIX_FILE := device/qcom/common/compatibility_matrix.xml
默認的framework manifest定義和兼容性文件定義如下:
@build/core/config.mk
FRAMEWORK_MANIFEST_FILE := system/libhidl/manifest.xml FRAMEWORK_COMPATIBILITY_MATRIX_FILE := hardware/interfaces/compatibility_matrix.xml
以上文件都是通過編譯生成到對應的分區,編譯腳本位於build/target/board/Android.mk
。
通過對比可以發現,out下面生成的和源碼里面存在的文件,並不是完全一致,在Android.mk
里面可以發現,這幾個文件都經過了out/host/linux-x86/bin/assemble_vintf
轉換,assemble_vintf
會判斷文件格式是否正確,並且會根據name
按字母順序排列。
以上兩個xml都是在,在system/libvintf/parse_string.cpp里面進行解析。
在前面的介紹中,我們都講到了一個重要的方法,就是transport
在system/libvintf/include/vintf/Transport.h定義
static const std::array<std::string, 3> gTransportStrings = { { "", "passthrough", "hwbinder", } };
我們獲取服務的時候,首先肯定要獲取當前的HAL是什么類型。
6 其他技巧
打印當前的manifest信息
- mmm system/libvintf/
- adb push out/target/product/(產品名)/system/bin/vintf /system/bin/vintf
- adb shell vintf
3. HAL通信機制(c++)
在Treble架構中,framework/vendor之間的通信通過HIDL
接口和dev/hwbinder
的IPC域來完成。而且HIDL接口有兩種通信模式Passthrough
和Binderized
。接下來我們介紹兩種模式下的交互原理。創建HAL服務器有兩種模式:
- defaultPassthroughServiceImplementation
int main() { return defaultPassthroughServiceImplementation<IPower>(); }
- registerAsService
int main(int /* argc */, char* /* argv */ []) { sp<IDumpstateDevice> dumpstate = new DumpstateDevice; configureRpcThreadpool(1, true /* will join */); if (dumpstate->registerAsService() != OK) { ALOGE("Could not register service."); return 1; } joinRpcThreadpool(); ALOGE("Service exited!"); return 1; }
接下來我們分別介紹兩種類型的詳細過程。
3.1 defaultPassthroughServiceImplementation
首先介紹Passthrough模式的HIDL實現機制。以hardware/interfaces/power/1.0
作為例子。當編譯hardware/interfaces/power/1.0
的時候,會生成:
- 中間文件
PowerAll.cpp
/vendor/bin/hw/android.hardware.power@1.0-service
的可執行文件/vendor/lib/hw/android.hardware.power@1.0-impl.so
的庫文件android.hardware.power@1.0-service.rc
會被拷貝到vendor.img里面的vendor/etc/init
目錄。rc文件的內容如下:
service power-hal-1-0 /vendor/bin/hw/android.hardware.power@1.0-service class hal user system group system
接下來我們就一步步分析,power Server
是如何初始化的。
- 對於init的解析機制,本文不做描述,在開機過程的某一個階段,系統會啟動class是hal的服務,會執行
/vendor/bin/hw/android.hardware.power@1.0-service
,從而調用hardware/interfaces/power/1.0/default/service.cpp
的main
方法。代碼如下:
int main() { return defaultPassthroughServiceImplementation<IPower>(); }
接下來會調用
@PowerAll.cpp
:android::sp<IPower> IPower::getService(const std::string &serviceName, const bool getStub) { using ::android::hardware::defaultServiceManager; using ::android::hardware::details::waitForHwService; using ::android::hardware::getPassthroughServiceManager; using ::android::hardware::Return; using ::android::sp; using Transport = ::android::hidl::manager::V1_0::IServiceManager::Transport; sp<IPower> iface = nullptr; // 獲取HwServiceManager const sp<::android::hidl::manager::V1_0::IServiceManager> sm = defaultServiceManager(); if (sm == nullptr) { ALOGE("getService: defaultServiceManager() is null"); return nullptr; } // 獲取當前Tranport類型,passthrough或者binderized Return<Transport> transportRet = sm->getTransport(IPower::descriptor, serviceName); if (!transportRet.isOk()) { ALOGE("getService: defaultServiceManager()->getTransport returns %s", transportRet.description().c_str()); return nullptr; } Transport transport = transportRet; const bool vintfHwbinder = (transport == Transport::HWBINDER); const bool vintfPassthru = (transport == Transport::PASSTHROUGH); // 返回當前的接口類 for (int tries = 0; !getStub && (vintfHwbinder || (vintfLegacy && tries == 0)); tries++) { if (tries > 1) { ALOGI("getService: Will do try %d for %s/%s in 1s...", tries, IPower::descriptor, serviceName.c_str()); sleep(1); } if (vintfHwbinder && tries > 0) { waitForHwService(IPower::descriptor, serviceName); } Return<sp<::android::hidl::base::V1_0::IBase>> ret = sm->get(IPower::descriptor, serviceName); if (!ret.isOk()) { ALOGE("IPower: defaultServiceManager()->get returns %s", ret.description().c_str()); break; } sp<::android::hidl::base::V1_0::IBase> base = ret; if (base == nullptr) { if (tries > 0) { ALOGW("IPower: found null hwbinder interface"); }continue; } Return<sp<IPower>> castRet = IPower::castFrom(base, true /* emitError */); // ... iface = castRet; if (iface == nullptr) { ALOGW("IPower: received incompatible service; bug in hwservicemanager?"); break; } return iface; } // 獲取passthrough模式的類。 if (getStub || vintfPassthru || vintfLegacy) { const sp<::android::hidl::manager::V1_0::IServiceManager> pm = getPassthroughServiceManager(); if (pm != nullptr) { Return<sp<::android::hidl::base::V1_0::IBase>> ret = pm->get(IPower::descriptor, serviceName); if (ret.isOk()) { sp<::android::hidl::base::V1_0::IBase> baseInterface = ret; if (baseInterface != nullptr) { iface = new BsPower(IPower::castFrom(baseInterface)); } } } } return iface; }
- defaultPassthroughServiceImplementation(); @hardware/interfaces/power/1.0/default/service.cpp
- IPower::getService @PowerAll.cpp 從HwServiceManager里面獲取注冊的服務。默認情況下是沒有注冊這個服務的。
- defaultServiceManager @system/libhidl/transport/ServiceManagement.cpp 打開
/dev/hwbinder
,通過binder通信,獲取HwServiceManager服務端。 -
sm->getTransport 基本就是按照Binder通信的機制來實現相關的流程。通過
HwBinder
調用服務端的getTransPort
方法。- BpHwServiceManager::getTransport @ServiceManagerAll.cpp
- BpHwBinder::transact
- IPCThreadState::self()->transact
- IPCThreadState::transact writeTransactionData waitForResponse
- IPCThreadState::executeCommand
-
ServiceManager::getTransport@system/hwservicemanager/ServiceManager.cpp
- getTransport @ system/hwservicemanager/Vintf.cpp 根據framework hal和device hal配置的manifest.xml里面的定義,來判斷當前的傳輸類型是HwBinder還是Passthrough模式。在
vendor/manifest.xml
里面,power配置的是hwbinder,所以最終就是hwBinder模式。(后續會講解manifest.xml的原理)
- getTransport @ system/hwservicemanager/Vintf.cpp 根據framework hal和device hal配置的manifest.xml里面的定義,來判斷當前的傳輸類型是HwBinder還是Passthrough模式。在
由於我們采取的是defaultPassthroughServiceImplementation<IPower>();
進行注冊,所以getStub=true
.所以會走到const sp<::android::hidl::manager::V1_0::IServiceManager> pm = getPassthroughServiceManager();
- getPassthroughServiceManager @ PowerAll.cpp
獲取passthrough服務管理。
- 調用PassthroughServiceManager的get(const hidl_string& fqName, const hidl_string& name)函數 @ServiceManagement.cpp
, 根據傳入的fqName=(android.hardware.power@1.0::IPower")
,獲取當前的接口名IPower
,拼接出后面需要載入的函數名HIDL_FETCH_IPower
和庫名字android.hardware.power@1.0-impl
,接着通過dlopen
載入/vendor/lib/hw/android.hardware.power@1.0-impl.so
,然后通過dlsym
載入HIDL_FETCH_IPower
函數。 代碼如下:
@hardware/interfaces/power/1.0/default/Power.cpp
IPower* HIDL_FETCH_IPower(const char* /* name */) { const hw_module_t* hw_module = nullptr; power_module_t* power_module = nullptr; int err = hw_get_module(POWER_HARDWARE_MODULE_ID, &hw_module); if (err) { ALOGE("hw_get_module %s failed: %d", POWER_HARDWARE_MODULE_ID, err); return nullptr; } if (!hw_module->methods || !hw_module->methods->open) { power_module = reinterpret_cast<power_module_t*>( const_cast<hw_module_t*>(hw_module)); } else { err = hw_module->methods->open( hw_module, POWER_HARDWARE_MODULE_ID, reinterpret_cast<hw_device_t**>(&power_module)); if (err) { ALOGE("Passthrough failed to load legacy HAL."); return nullptr; } } return new Power(power_module); }
通過hw_get_module
就和Android O
以前的Hal模式一致,這正是Passthrough復用原有hal的原理,測試用的是模擬器,所以最終獲取的庫文件是/system/lib/hw/power.ranchu.so
,后續所有的和Power有關的接口調用,最終都是通過power.ranchu.so
來實現功能。
接下來會調用registerReference("android.hardware.power@1.0::IPower","default")
,接着調用BpHwServiceManager::registerPassthroughClient
將fqName
和服務名,注冊進hwservicemanager
的mServiceMap
對象里面。
Return<void> ServiceManager::registerPassthroughClient(const hidl_string &fqName,
const hidl_string &name) { pid_t pid = IPCThreadState::self()->getCallingPid(); if (!mAcl.canGet(fqName, pid)) { /* We guard this function with "get", because it's typically used in * the getService() path, albeit for a passthrough service in this * case */ return Void(); } PackageInterfaceMap &ifaceMap = mServiceMap[fqName]; if (name.empty()) { LOG(WARNING) << "registerPassthroughClient encounters empty instance name for " << fqName.c_str(); return Void(); } HidlService *service = ifaceMap.lookup(name); if (service == nullptr) { auto adding = std::make_unique<HidlService>(fqName, name); adding->registerPassthroughClient(pid); ifaceMap.insertService(std::move(adding)); } else { service->registerPassthroughClient(pid); } return Void(); }
-
返回
android::hidl::base::V1_0::IBase
實例。 -
new BsPower:首先會通過interfaceChain判斷當前的interface是否支持轉換,然后傳入包名和接口名
"android.hardware.power@1.0", "IPower"
構造出一個new BsPower
的實例。 -
IPower::registerAsService 接下來,調用
status_t status = service->registerAsService(name)
,首先會創建BnHwPower
對象,然后將當前的service 添加進hwservicemanager
里面。初始化BnHwPower
的過程中, _hidl_mImpl實際上就是BsPower
的引用。代碼如下。 。
BnHwPower::BnHwPower(const ::android::sp<IPower> &_hidl_impl) : ::android::hidl::base::V1_0::BnHwBase(_hidl_impl, "android.hardware.power@1.0", "IPower") { _hidl_mImpl = _hidl_impl; auto prio = ::android::hardware::details::gServicePrioMap.get(_hidl_impl, {SCHED_NORMAL, 0}); mSchedPolicy = prio.sched_policy; mSchedPriority = prio.prio; }
然后調用如下步驟,將當前通信加入IPC Binder的線程池進行循環。
- android::hardware::joinRpcThreadpool at system/libhidl/transport/HidlTransportSupport.cpp:28 加入RpcThreadPool。
- android::hardware::joinBinderRpcThreadpool at system/libhidl/transport/HidlBinderSupport.cpp:188
- android::hardware::IPCThreadState::joinThreadPool at system/libhwbinder/IPCThreadState.cpp:497
- android::hardware::IPCThreadState::getAndExecuteCommand at system/libhwbinder/IPCThreadState.cpp:443
至此,android.hardware.power@1.0::IPower
服務就啟動成功了,可以響應客戶端的請求了。
總結,通過defaultPassthroughServiceImplementation
把當前的服務注冊進HwServiceManager
,每個服務都是一個HidlService
。然后就可以等待客戶端的調用。
3.2 registerAsService 創建HAL
根據Android源碼網站介紹,android.hardware.dumpstate@1.0是屬於綁定式HAL。接下來我們分析dumpstate服務初始化的流程。代碼位於:hardware/interfaces/dumpstate/1.0/default/
,查看service.cpp
,代碼如下:
int main(int /* argc */, char* /* argv */ []) { sp<IDumpstateDevice> dumpstate = new DumpstateDevice; configureRpcThreadpool(1, true /* will join */); if (dumpstate->registerAsService() != OK) { ALOGE("Could not register service."); return 1; } joinRpcThreadpool(); ALOGE("Service exited!"); return 1; }
- IDumpstateDevice::registerAsService
- android::hardware::details::onRegistration(“android.hardware.dumpstate@1.0”, “IDumpstateDevice”, serviceName)
- tryShortenProcessName 設置當前進程的名字,長度最多為16。android.hardware.dumpstate@1.0-service
- BpHwServiceManager::add
- ServiceManager::add @system/hwservicemanager/ServiceManager.cpp 注意和binder的區別。將當前的
service
添加進mInstanceMap。
- ServiceManager::add @system/hwservicemanager/ServiceManager.cpp 注意和binder的區別。將當前的
- 收到HwBinder驅動的 BR_TRANSACTION 消息,然后執行 BHwBinder::transact
- BnHwDumpstateDevice::onTransact
- joinRpcThreadpool(); 把當前的通信加入HwBinder的線程池進行循環。
至此,registerAsService 創建HAL Service就完成了。
3.2 Binderized 模式 client和服務端的交互
服務注冊成功之后,客戶端就可以調用相關服務提供的功能。
以點擊屏幕為實例說明,當我們點擊屏幕的時候,會調用com_android_server_power_PowerManagerService.cpp
的android_server_PowerManagerService_userActivity
函數,代碼如下:
void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
// Tell the power HAL when user activity occurs.
gPowerHalMutex.lock();
if (getPowerHal()) { Return<void> ret = gPowerHal->powerHint(PowerHint::INTERACTION, 0); processReturn(ret, "powerHint"); } // ... } } // Check validity of current handle to the power HAL service, and call getService() if necessary. // The caller must be holding gPowerHalMutex. bool getPowerHal() { if (gPowerHalExists && gPowerHal == nullptr) { gPowerHal = IPower::getService(); if (gPowerHal != nullptr) { ALOGI("Loaded power HAL service"); } else { ALOGI("Couldn't load power HAL service"); gPowerHalExists = false; } } return gPowerHal != nullptr; }
在getPowerHal
里面,通過IPower::getService();
方法經過HwBinder
通信,獲取服務端的引用。主要包含如下步驟:
-
IPower::getService() 獲取IPower的服務。返回遠程服務的代理
gPowerHal
,最終返回的是BpHwPower。IPower::getService(const std::string &serviceName, const bool getStub)@PowerApp.cpp
。- BpHwServiceManager::getTransport 獲取當前的傳輸類型,
passthrough
或者binderized
。Power是binderized
,返回對應的服務代理。 - sm->get(IPower::descriptor, serviceName) 從ServiceManager里面獲取描述是
android.hardware.power@1.0::IPower
,服務名是default
的hidlservice
的引用。 - IPower::castFrom(base, true /* emitError */)
- android::hardware::details::castInterface 將
hidlservice
服務的引用轉換成Binder對象。 - ::android::hardware::IInterface::asBinder(static_cast
3.4 pathrough 模式 client和服務端的交互
查詢manifest.xml
可以發現。android.hardware.graphics.mapper
是passthrough的模式。
<hal format="hidl"> <name>android.hardware.graphics.mapper</name> <transport arch="32+64">passthrough</transport> <version>2.0</version> <interface> <name>IMapper</name> <instance>default</instance> </interface> </hal>
以hardware/interfaces/graphics/mapper/2.0/
作為例子進行分析。
@frameworks/native/libs/ui/Gralloc2.cpp
Mapper::Mapper() { mMapper = IMapper::getService(); if (mMapper == nullptr || mMapper->isRemote()) { LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode"); } }
// static
::android::sp IMapper::getService(const std::string &serviceName, const bool getStub) {
using ::android::hardware::defaultServiceManager;
using ::android::hardware::details::waitForHwService;
using ::android::hardware::getPassthroughServiceManager;
using ::android::hardware::Return;
using ::android::sp;
using Transport = ::android::hidl::manager::V1_0::IServiceManager::Transport;
sp<IMapper> iface = nullptr;
const sp<::android::hidl::manager::V1_0::IServiceManager> sm = defaultServiceManager();
if (sm == nullptr) {
ALOGE("getService: defaultServiceManager() is null");
return nullptr;
}
Return<Transport> transportRet = sm->getTransport(IMapper::descriptor, serviceName);
if (!transportRet.isOk()) {
ALOGE("getService: defaultServiceManager()->getTransport returns %s", transportRet.description().c_str());
return nullptr;
}
Transport transport = transportRet;
const bool vintfHwbinder = (transport == Transport::HWBINDER);
const bool vintfPassthru = (transport == Transport::PASSTHROUGH);
// ...
if (getStub || vintfPassthru || vintfLegacy) {
const sp<::android::hidl::manager::V1_0::IServiceManager> pm = getPassthroughServiceManager();
if (pm != nullptr) {
Return<sp<::android::hidl::base::V1_0::IBase>> ret =
pm->get(IMapper::descriptor, serviceName);
if (ret.isOk()) {
sp<::android::hidl::base::V1_0::IBase> baseInterface = ret;
if (baseInterface != nullptr) {
iface = new BsMapper(IMapper::castFrom(baseInterface));
}
}
}
}
return iface;
}
- 步驟和前面的一致,由於是passthrough的模式,調用
PassthroughServiceManager
的get(const hidl_string& fqName, const hidl_string& name)
函數@ServiceManagement.cpp
, 根據傳入的fqName=(android.hardware.graphics.mapper@2.0::IMapper")
,獲取當前的接口名IMapper
,拼接出后面需要載入的函數名HIDL_FETCH_IMapper
和庫名字android.hardware.graphics.mapper@2.0-impl
,接着通過dlopen載入android.hardware.graphics.mapper@2.0-impl
,然后通過dlsym載入HIDL_FETCH_IMapper函數。
這樣就實現了passthrough模式下的通信了。
4. HAL 通信 (JAVA)
以hardware/interfaces/radio/1.0/
作為例子:
當我們編譯hardware/interfaces/radio/1.0/
的時候,會編譯出:
- android.hardware.radio-V1.0-java-static
- out/target/common/gen/JAVA_LIBRARIES/android.hardware.radio-V1.0-java-static_intermediates/android/hardware/radio/V1_0/IRadio.java
接下來我們以
@frameworks/opt/telephony/Android.mk 最為例子,直接引用android.hardware.radio-V1.0-java-static
,然后就可以使用里面的相關代碼。
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) // ... LOCAL_JAVA_LIBRARIES := voip-common ims-common LOCAL_STATIC_JAVA_LIBRARIES := android.hardware.radio-V1.0-java-static \ android.hardware.radio.deprecated-V1.0-java-static LOCAL_MODULE_TAGS := optional LOCAL_MODULE := telephony-common // ... include $(BUILD_JAVA_LIBRARY)
接下來我們看一下使用的地方。
@RIL.java
try { mRadioProxy = IRadio.getService(HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId]); if (mRadioProxy != null) { mRadioProxy.linkToDeath(mRadioProxyDeathRecipient, mRadioProxyCookie.incrementAndGet()); mRadioProxy.setResponseFunctions(mRadioResponse, mRadioIndication); } else { riljLoge("getRadioProxy: mRadioProxy == null"); } } catch (RemoteException | RuntimeException e) { mRadioProxy = null; riljLoge("RadioProxy getService/setResponseFunctions: " + e); }
首先會直接調用IRadio.getService
來獲取相關服務。
@IRadio.java
public static IRadio getService(String serviceName) throws android.os.RemoteException { return IRadio.asInterface(android.os.HwBinder.getService("android.hardware.radio@1.0::IRadio",serviceName)); }
android.os.HwBinder.getService(“android.hardware.radio@1.0::IRadio”,serviceName)
JNI
@frameworks/base/core/jni/android_os_HwBinder.cpp
static jobject JHwBinder_native_getService(
JNIEnv *env, jclass /* clazzObj */, jstring ifaceNameObj, jstring serviceNameObj) { ///... auto manager = hardware::defaultServiceManager(); // ... Return<IServiceManager::Transport> transportRet = manager->getTransport(ifaceNameHStr, serviceNameHStr); if (!transportRet.isOk()) { signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */); return NULL; } IServiceManager::Transport transport = transportRet; // ... java 類型的傳輸模式必須是HwBinder if (transport != IServiceManager::Transport::HWBINDER && !vintfLegacy) { LOG(ERROR) << "service " << ifaceName << " declares transport method " << toString(transport) << " but framework expects hwbinder."; signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */); return NULL; } // 獲取接口引用。 Return<sp<hidl::base::V1_0::IBase>> ret = manager->get(ifaceNameHStr, serviceNameHStr); if (!ret.isOk()) { signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */); return NULL; } // 轉換成Binder接口 sp<hardware::IBinder> service = hardware::toBinder< hidl::base::V1_0::IBase, hidl::base::V1_0::BpHwBase>(ret); if (service == NULL) { signalExceptionForError(env, NAME_NOT_FOUND); return NULL; } LOG(INFO) << "Starting thread pool."; ::android::hardware::ProcessState::self()->startThreadPool(); // 返回JHwRemoteBinder對象。 return JHwRemoteBinder::NewObject(env, service); }
以上步驟和C++里面的獲取服務步驟類似。通過IRadio.getService()
獲取相關的服務,進入JNI的相關接口,獲取HwServiceManager
服務,然后獲取當前HAL的類型(必須是Binderized),接下來獲取服務對應的接口引用,接着將當前接口轉換成Ibinder引用,然后創建JHwRemoteBinder
對象返回給java層。
IRadio.asInterface(android.os.HwBinder.getService("android.hardware.radio@1.0::IRadio",serviceName))
java層接着調用IRadio.asInterface
將Hwbinder
引用轉換成IRadio
對象。
這樣就可以通過IRadio
對象調用
5. Vendor Interface Object
5.1 manifest.xml 和 compatibility_matrix.xml
在system分區和vendor分區,分別存在manifest.xml和compatibility_matrix.xml。內容大致如下:
<manifest version="1.0" type="framework"> <hal format="hidl"> <name>android.frameworks.displayservice</name> <transport>hwbinder</transport> <version>1.0</version> <interface> <name>IDisplayService</name> <instance>default</instance> </interface> </hal> <hal format="hidl"> <name>android.frameworks.schedulerservice</name> <transport>hwbinder</transport> <version>1.0</version> <interface> <name>ISchedulingPolicyService</name> <instance>default</instance> </interface> </hal> ... </manifest>
分為兩類:
- framework相關的,Google默認定義完成。
- device相關,有設備廠商自定義。
device可以通過DEVICE_MANIFEST_FILE
和DEVICE_MATRIX_FILE
指定自己的manifest.xml文件。如高通平台的項目:
DEVICE_MANIFEST_FILE := device/qcom/msm8937_64/manifest.xml DEVICE_MATRIX_FILE := device/qcom/common/compatibility_matrix.xml
默認的framework manifest定義和兼容性文件定義如下:
@build/core/config.mk
FRAMEWORK_MANIFEST_FILE := system/libhidl/manifest.xml FRAMEWORK_COMPATIBILITY_MATRIX_FILE := hardware/interfaces/compatibility_matrix.xml
以上文件都是通過編譯生成到對應的分區,編譯腳本位於build/target/board/Android.mk
。
通過對比可以發現,out下面生成的和源碼里面存在的文件,並不是完全一致,在Android.mk
里面可以發現,這幾個文件都經過了out/host/linux-x86/bin/assemble_vintf
轉換,assemble_vintf
會判斷文件格式是否正確,並且會根據name
按字母順序排列。
以上兩個xml都是在,在system/libvintf/parse_string.cpp里面進行解析。
在前面的介紹中,我們都講到了一個重要的方法,就是transport
在system/libvintf/include/vintf/Transport.h定義
static const std::array<std::string, 3> gTransportStrings = { { "", "passthrough", "hwbinder", } };
我們獲取服務的時候,首先肯定要獲取當前的HAL是什么類型。
6 其他技巧
打印當前的manifest信息
- mmm system/libvintf/
- adb push out/target/product/(產品名)/system/bin/vintf /system/bin/vintf
- adb shell vintf