前面章节 HAL 接口定义语言详解 中介绍了 HIDL 接口的设计架构,下面一节通过详解 Android 9.0 图形显示合成 Composer HAL 的启动与服务注册过程。
我们知道,在 HIDL 的设计理念中,HAL 服务端进程与 frameworks 客户端调用是分离开的,每个 HAL 进程独立运行在自己的地址空间中,客户端通过 binder IPC 与 HAL 进程请求交互,完成接口调用。
我们首先来看看 Composer HAL 进程是如何启动。
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 进程会读取并解析这个脚本文件,启动一个名为
rc 启动脚本最后指明当 SurfaceFlinger 关闭时会重启 Composer HAL 进程。
通过 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,
¶m) != 0) {
ALOGE("Couldn't set SCHED_FIFO: %d", errno);
}
return defaultPassthroughServiceImplementation(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(4);
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
__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(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
__attribute__((warn_unused_result))
status_t registerPassthroughServiceImplementation(
std::string name = "default") {
// Interface 传递进来的是 IComposer
// 1. 获取 IComposer 接口类对象
sp 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,下面具体分析一下。
在 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::getService(const std::string &serviceName, const bool getStub) {
return ::android::hardware::details::getServiceInternal(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;
const sp sm = defaultServiceManager1_1();
if (sm == nullptr) {
ALOGE("getService: defaultServiceManager() is null");
return nullptr;
}
Return 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> 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 base = ret;
if (base != nullptr) {
Return 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 pm = getPassthroughServiceManager();
if (pm != nullptr) {
sp base = pm->get(descriptor, instance).withDefault(nullptr);
if (!getStub || trebleTestingOverride) {
base = wrapPassthrough(base);
}
return base;
}
}
return nullptr;
}
}; // namespace details