1. 程式人生 > >Android Treble架構解析--

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/目錄。
  • -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.mkAndroid.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.bpAndroid.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.mkAndroid.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.mkAndroid.bp檔案。這樣以後我們就可以執行正常的編譯命令進行編譯了。比如mmm hardware/interfaces/power/,預設情況下,在原始碼中,Android.mkAndroid.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.cppPowerAll.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介面有兩種通訊模式PassthroughBinderized。接下來我們介紹兩種模式下的互動原理。建立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的時候,會生成:

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.cppmain方法。程式碼如下:
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::registerPassthroughClientfqName和服務名,註冊進hwservicemanagermServiceMap物件裡面。

    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。
    • 收到HwBinder驅動的 BR_TRANSACTION 訊息,然後執行 BHwBinder::transact
    • BnHwDumpstateDevice::onTransact
    • joinRpcThreadpool(); 把當前的通訊加入HwBinder的執行緒池進行迴圈。

    至此,registerAsService 建立HAL Service就完成了。

    3.2 Binderized 模式 client和服務端的互動

    服務註冊成功之後,客戶端就可以呼叫相關服務提供的功能。

    以點選螢幕為例項說明,當我們點選螢幕的時候,會呼叫com_android_server_power_PowerManagerService.cppandroid_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,服務名是defaulthidlservice的引用。
    • 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的模式,呼叫PassthroughServiceManagerget(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