Android Treble架構解析--
本文主要介紹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/[email protected]_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
- 1
引數含義:
- -L: 語言型別,包括c++, c++-headers, c++-sources, export-header, c++-impl, java, java-constants, vts, makefile, androidbp, androidbp-impl, hash等。
hidl-gen
可根據傳入的語言型別產生不同的檔案。 - fqname: 完全限定名稱的輸入檔案。比如本例中
[email protected]
,要求在原始碼目錄下必須有hardware/interfaces/power/1.0/
目錄。
- 對於單個檔案來說,格式如下:
[email protected]::fileName
,比如[email protected]::types.Feature。 - 對於目錄來說。格式如下
[email protected]
,比如[email protected]。
- 對於單個檔案來說,格式如下:
- -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/[email protected]_genc++/gen
,如果是c++-headers
,那麼就是out/soong/.intermediates/hardware/interfaces/power/1.0/[email protected]_genc++_headers/gen
。
對於例項來說,fqname是:[email protected]
,包名是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"
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
這個指令碼的主要作用:根據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裡面的斜線轉換成點,最終獲取的包名就是 [email protected],依次類推獲取所有的包名。
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 [email protected]Android.bp
: hidl-gen -Landroidbp -r android.hardware:hardware/interfaces -r android.hidl:system/libhidl/transport [email protected]
關於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",
]
- 1
- 2
- 3
- 4
- 5
- 6
意思就是說,編譯的時候,需要編譯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: "[email protected]_hal",
srcs: [
"types.hal",
"IPower.hal",
],
}
genrule {
name: "[email protected]_genc++",
tools: ["hidl-gen"],
cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport [email protected]",
srcs: [
":[email protected]_hal",
],
out: [
"android/hardware/power/1.0/types.cpp",
"android/hardware/power/1.0/PowerAll.cpp",
],
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
可以看到在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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
經過以上步驟,.hal
檔案就轉換成了對應的程式碼,而且具備了Binder通訊的能力。
HIDL整個流程如圖所示:
3. HAL通訊機制(c++)
在Treble架構中,framework/vendor之間的通訊通過HIDL
介面和dev/hwbinder
的IPC域來完成。而且HIDL介面有兩種通訊模式Passthrough
和Binderized
。接下來我們介紹兩種模式下的互動原理。建立HAL伺服器有兩種模式:
- defaultPassthroughServiceImplementation
int main() {
return defaultPassthroughServiceImplementation<IPower>();
}
- 1
- 2
- 3
- 4
- 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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
接下來我們分別介紹兩種型別的詳細過程。
3.1 defaultPassthroughServiceImplementation
首先介紹Passthrough模式的HIDL實現機制。以hardware/interfaces/power/1.0
作為例子。當編譯hardware/interfaces/power/1.0
的時候,會生成:
- 中間檔案
PowerAll.cpp
/vendor/bin/hw/[email protected]
的可執行檔案/vendor/lib/hw/[email protected]
的庫檔案[email protected]
會被拷貝到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
- 1
- 2
- 3
- 4
接下來我們就一步步分析,power Server
是如何初始化的。
- 對於init的解析機制,本文不做描述,在開機過程的某一個階段,系統會啟動class是hal的服務,會執行
/vendor/bin/hw/[email protected]
,從而呼叫hardware/interfaces/power/1.0/default/service.cpp
的main
方法。程式碼如下:
int main() {
return defaultPassthroughServiceImplementation<IPower>();
}
- 1
- 2
- 3
- 4
接下來會呼叫
@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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 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::[email protected]/hwservicemanager/ServiceManager.cpp
- getTransport @ system/hwservicemanager/Vintf.cpp 根據framework hal和device hal配置的manifest.xml裡面的定義,來判斷當前的傳輸型別是HwBinder還是Passthrough模式。在
vendor/manifest.xml
裡面,power配置的是hwbinder,所以最終就是hwBinder模式。(後續會講解manifest.xml的原理)
由於我們採取的是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=([email protected]::IPower")
,獲取當前的介面名IPower
,拼接出後面需要載入的函式名HIDL_FETCH_IPower
和庫名字[email protected]
,接著通過dlopen
載入/vendor/lib/hw/[email protected]
,然後通過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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
通過hw_get_module
就和Android O
以前的Hal模式一致,這正是Passthrough複用原有hal的原理,測試用的是模擬器,所以最終獲取的庫檔案是/system/lib/hw/power.ranchu.so
,後續所有的和Power有關的介面呼叫,最終都是通過power.ranchu.so
來實現功能。
接下來會呼叫registerReference("[email protected]::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();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
返回
android::hidl::base::V1_0::IBase
例項。new BsPower:首先會通過interfaceChain判斷當前的interface是否支援轉換,然後傳入包名和介面名
"[email protected]", "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, "[email protected]", "IPower") {
_hidl_mImpl = _hidl_impl;
auto prio = ::android::hardware::details::gServicePrioMap.get(_hidl_impl, {SCHED_NORMAL, 0});
mSchedPolicy = prio.sched_policy;
mSchedPriority = prio.prio;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
然後呼叫如下步驟,將當前通訊加入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
至此,[email protected]::IPower
服務就啟動成功了,可以響應客戶端的請求了。
總結,通過defaultPassthroughServiceImplementation
把當前的服務註冊進HwServiceManager
,每個服務都是一個HidlService
。然後就可以等待客戶端的呼叫。
3.2 registerAsService 建立HAL
根據Android原始碼網站介紹,[email protected]是屬於繫結式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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- IDumpstateDevice::registerAsService
- android::hardware::details::onRegistration(“[email protected]”, “IDumpstateDevice”, serviceName)
- tryShortenProcessName 設定當前程序的名字,長度最多為16。[email protected]
- 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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
在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裡面獲取描述是
[email protected]::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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
以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");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
// 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;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
}
- 步驟和前面的一致,由於是passthrough的模式,呼叫
PassthroughServiceManager
的get(const hidl_string& fqName, const hidl_string& name)
函式@ServiceManagement.cpp
, 根據傳入的fqName=([email protected]::IMapper")
,獲取當前的介面名IMapper
,拼接出後面需要載入的函式名HIDL_FETCH_IMapper
和庫名字[email protected]
,接著通過dlopen載入[email protected]
,然後通過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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
接下來我們看一下使用的地方。
@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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
首先會直接呼叫IRadio.getService
來獲取相關服務。
@IRadio.java
public static IRadio getService(String serviceName) throws android.os.RemoteException {
return IRadio.asInterface(android.os.HwBinder.getService("[e