Android HIDL 之 hal 進程啓動及服務註冊過程

1. 引言

前面章節 HAL 接口定義語言詳解 中介紹了 HIDL 接口的設計架構,下面一節通過詳解 Android 9.0 圖形顯示合成 Composer HAL 的啓動與服務註冊過程。

我們知道,在 HIDL 的設計理念中,HAL 服務端進程與 frameworks 客戶端調用是分離開的,每個 HAL 進程獨立運行在自己的地址空間中,客戶端通過 binder IPC 與 HAL 進程請求交互,完成接口調用。

我們首先來看看 Composer HAL 進程是如何啓動。

2. HAL 進程啓動過程分析

2.1 rc 啓動腳本

console:/ $ ps -A | grep composer
system         239     1   29084   8928 0                   0 S [email protected]

平臺 HAL 進程以 “android.hardware” 打頭,我們很容易在系統進程表中找到 Composer HAL 服務進程。

在 Composer 模塊 HAL 接口軟件包目錄,能找到這個進程 Android 編譯 bp 文件以及源文件 service.cpp:

file path: hardware/interfaces/graphics/composer/2.1/default/Android.bp

cc_binary {
    name: "[email protected]",
    defaults: ["hidl_defaults"],
    vendor: true,
    relative_install_path: "hw",
    srcs: ["service.cpp"],
    init_rc: ["[email protected]"],
    shared_libs: [
        "[email protected]",
        "libbinder",
        "libhidlbase",
        "libhidltransport",
        "liblog",
        "libsync",
        "libutils",
    ],  
}

從編譯 bp 文件中可以看出,編譯的二進制 HAL bin 文件安裝目錄爲 /vendor/lib/hw,使用下面 rc 腳本啓動進程。

file path: hardware/interfaces/graphics/composer/2.1/default/
[email protected]

service vendor.hwcomposer-2-1 /vendor/bin/hw/[email protected]
    class hal animation                                                            
    user system
    group graphics drmrpc
    capabilities SYS_NICE
    writepid /dev/cpuset/system-background/tasks

# Restart HWC when SurfaceFlinger stops. This turns off the display and prpares
# a new HWC instance for when SurfaceFlinger gets started again
on property:init.svc.surfaceflinger=stopped
    restart vendor.hwcomposer-2-1

Android 編譯時會將該腳本文件拷貝到 /vendor/etc/init 目錄,Android 啓動 init 進程會讀取並解析這個腳本文件,啓動一個名爲 <vendor.hwcomposer-2-1> 的服務,二進制文件路徑在系統 /vendor/bin/hw/[email protected]

rc 啓動腳本最後指明當 SurfaceFlinger 關閉時會重啓 Composer HAL 進程。

2.2 初始化進程 Binder IPC 通信

通過 Android.bp 文件,我們知道該二進制可執行文件對應源文件爲 service.cpp。

int main() {
    // the conventional HAL might start binder services
    android::ProcessState::initWithDriver("/dev/vndbinder");
    android::ProcessState::self()->setThreadPoolMaxThreadCount(4);
    android::ProcessState::self()->startThreadPool();

    // same as SF main thread
    struct sched_param param = {0};
    param.sched_priority = 2;
    if (sched_setscheduler(0, SCHED_FIFO | SCHED_RESET_ON_FORK,
                &param) != 0) {
        ALOGE("Couldn't set SCHED_FIFO: %d", errno);
    }

    return defaultPassthroughServiceImplementation<IComposer>(4);
}

從 Android O 開始,Android 框架和 HAL 都開始使用 Binder 相互通信,這就導致之前的驅動設備節點 /dev/binder 可能因流量過大而導致 IPC 速度降低。

在 HIDL 架構中。爲了明確地拆分框架(設備無關)和供應商(設備相關)代碼之間的 Binder 流量,引入了 “Binder 上下文” 這一概念,每個 Binder 上下文都有自己的設備節點和上下文(服務)管理器。進程只能通過上下文管理器對所屬的設備節點對其進行訪問。

Android O 支持供應商服務增加的 Binder 域:

IPC 域 說明
/dev/binder 框架/應用進程之間的 IPC,使用 AIDL 接口
/dev/hwbinder 框架/供應商進程之間的 IPC,使用 HIDL 接口供應商進程之間的 IPC,使用 HIDL 接口
/dev/vndbinder 供應商/供應商進程之間的 IPC,使用 AIDL 接口

HIDL 框架爲了支持供應商 HAL 進程,提供了 libbinder 用戶空間庫用於操作 Binder 設備節點(hwbinder & vndbinder)。其中,::android::ProcessState() 方法爲 libbinder 選擇 Binder 驅動程序。如下面的初始化語句爲進程選擇 vndbinder Binder 驅動程序,並且指定 Binder 線程個數爲 4,同時啓動線程池。

android::ProcessState::initWithDriver("/dev/vndbinder");
android::ProcessState::self()->setThreadPoolMaxThreadCount(4);
android::ProcessState::self()->startThreadPool();

初始化完 Binder IPC 機制,就有了處理客戶端請求的能力,纔可以開始啓動 Composer HAL。

defaultPassthroughServiceImplementation<IComposer>(4);

2.3 Passthrough 方式啓動 HAL

Google HIDL 設計的目的是讓 HAL 服務與用戶調用在不同的進程空間,HAL 被寫成 Binder Service,而用戶調用如 frameworks 作爲 Binder Clinet 通過 IPC 機制實現跨進程接口調用。但是很多廠商還停留在 Android 老版本 HAL,爲了給廠商改變時間,同時保持 Android 向前兼容性,Google 另外設計了 Passthrough 類型的 HAL 框架。

Passthrough Hal 模式是爲了兼容舊版的 HAL,舊版 HAL 實現仍以動態庫的方式提供,只是 binder service 鏈接了動態庫 HAL 實現,即 binder service 通過 hw_get_module 鏈接了舊版的 hal 實現,而用戶端通過與 binder service IPC 通信,間接實現了與舊版 HAL 的交互。

Android 9.0 代碼 HAL Module 目前都是採用 Passthrough 方式創建 HAL 服務進程。

file path: /system/libhidl/transport/include/hidl/LegacySupport.h

template<class Interface>
__attribute__((warn_unused_result))
status_t defaultPassthroughServiceImplementation(std::string name,
                                            size_t maxThreads = 1) {
    // 配置 Binder IPC 最大線程個數
    // ProcessState::self()->setThreadPoolConfiguration(maxThreads, callerWillJoin /*callerJoinsPool*/)
    configureRpcThreadpool(maxThreads, true);
    status_t result = registerPassthroughServiceImplementation<Interface>(name);

    if (result != OK) {
        return result;
    }

    // IPCThreadState::self()->joinThreadPool()
    joinRpcThreadpool();
    return UNKNOWN_ERROR;
}

registerPassthroughServiceImplementation 函數是 HAL 服務的註冊過程,在 Treble 框架中,HAL Module 服務統一由 hwservicemanager 管理,所以如果 HAL 進程需要對外提供接口,首先需要向 hwservicemanager 註冊一個服務。

file path: /system/libhidl/transport/include/hidl/LegacySupport.h

template<class Interface>
__attribute__((warn_unused_result))
status_t registerPassthroughServiceImplementation(
        std::string name = "default") {
    // Interface 傳遞進來的是 IComposer
    // 1. 獲取 IComposer 接口類對象
    sp<Interface> service = Interface::getService(name, true /* getStub */);

    if (service == nullptr) {
        ALOGE("Could not get passthrough implementation for %s/%s.",
            Interface::descriptor, name.c_str());
        return EXIT_FAILURE;
    }

    LOG_FATAL_IF(service->isRemote(), "Implementation of %s/%s is remote!",
            Interface::descriptor, name.c_str());

    // 2. 將 IComposer 服務註冊到 hwservicemanager 中
    status_t status = service->registerAsService(name);

    if (status == OK) {
        ALOGI("Registration complete for %s/%s.",
            Interface::descriptor, name.c_str());
    } else {
        ALOGE("Could not register service %s/%s (%d).",
            Interface::descriptor, name.c_str(), status);
    }

    return status;
}

整個註冊過程只有兩步,首先實例化傳入的 IComposer 接口對象,然後通過 registerAsService 將服務註冊到 hwservicemanager,下面具體分析一下。

2.4 HAL 進程獲取 IComposer 接口對象

在 Composer HAL 進程啓動時,首先調用 IComposer 的 getService(“default”, true) 來獲取 IComposer 的接口對象。

這個 IComposer 的實現類通過 hidl-gen 工具生成。最終生成路徑在:
out/soong/.intermediates/hardware/interfaces/graphics/composer/2.1/[email protected]_genc++/gen/android/hardware/graphics/composer/2.1\ComposerAll.cpp。

// static
::android::sp<IComposer> IComposer::getService(const std::string &serviceName, const bool getStub) {
    return ::android::hardware::details::getServiceInternal<BpHwComposer>(serviceName, true, getStub);
}

details::getServiceInternal 是 libhidl 提供的方法。getServiceInternal 最終調用到 getRawServiceInternal。

file path: system/libhidl/transport/ServiceManagement.cpp

sp<::android::hidl::base::V1_0::IBase> getRawServiceInternal(const std::string& descriptor,
                                                             const std::string& instance,
                                                             bool retry, bool getStub) {
    using Transport = ::android::hidl::manager::V1_0::IServiceManager::Transport;
    using ::android::hidl::base::V1_0::IBase;
    using ::android::hidl::manager::V1_0::IServiceManager;
    sp<Waiter> waiter;

    const sp<IServiceManager1_1> sm = defaultServiceManager1_1();
    if (sm == nullptr) {
        ALOGE("getService: defaultServiceManager() is null");
        return nullptr;
    }

    Return<Transport> transportRet = sm->getTransport(descriptor, instance);

    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);

#ifdef ENFORCE_VINTF_MANIFEST

#ifdef LIBHIDL_TARGET_DEBUGGABLE
    const char* env = std::getenv("TREBLE_TESTING_OVERRIDE");
    const bool trebleTestingOverride = env && !strcmp(env, "true");
    const bool vintfLegacy = (transport == Transport::EMPTY) && trebleTestingOverride;
#else   // ENFORCE_VINTF_MANIFEST but not LIBHIDL_TARGET_DEBUGGABLE
    const bool trebleTestingOverride = false;
    const bool vintfLegacy = false;
#endif  // LIBHIDL_TARGET_DEBUGGABLE

#else   // not ENFORCE_VINTF_MANIFEST
    const char* env = std::getenv("TREBLE_TESTING_OVERRIDE");
    const bool trebleTestingOverride = env && !strcmp(env, "true");
    const bool vintfLegacy = (transport == Transport::EMPTY);
#endif  // ENFORCE_VINTF_MANIFEST

    for (int tries = 0; !getStub && (vintfHwbinder || vintfLegacy); tries++) {
        if (waiter == nullptr && tries > 0) {
            waiter = new Waiter(descriptor, instance, sm);
        }
        if (waiter != nullptr) {
            waiter->reset();  // don't reorder this -- see comments on reset()
        }
        Return<sp<IBase>> ret = sm->get(descriptor, instance);
        if (!ret.isOk()) {
            ALOGE("getService: defaultServiceManager()->get returns %s for %s/%s.",
                  ret.description().c_str(), descriptor.c_str(), instance.c_str());
            break;
        }
        sp<IBase> base = ret;
        if (base != nullptr) {
            Return<bool> canCastRet =
                details::canCastInterface(base.get(), descriptor.c_str(), true /* emitError */);

            if (canCastRet.isOk() && canCastRet) {
                if (waiter != nullptr) {
                    waiter->done();
                }
                return base; // still needs to be wrapped by Bp class.
            }

            if (!handleCastError(canCastRet, descriptor, instance)) break;
        }

        // In case of legacy or we were not asked to retry, don't.
        if (vintfLegacy || !retry) break;

        if (waiter != nullptr) {
            ALOGI("getService: Trying again for %s/%s...", descriptor.c_str(), instance.c_str());
            waiter->wait();
        }
    }

    if (waiter != nullptr) {
        waiter->done();
    }

    if (getStub || vintfPassthru || vintfLegacy) {
        const sp<IServiceManager> pm = getPassthroughServiceManager();
        if (pm != nullptr) {
            sp<IBase> base = pm->get(descriptor, instance).withDefault(nullptr);
            if (!getStub || trebleTestingOverride) {
                base = wrapPassthrough(base);
            }
            return base;
        }
    }

    return nullptr;
}

}; // namespace details
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章