Android HIDL

2018/07/30 android architecture

Android HIDL

本篇仅简单介绍 hidl ,关于 hidl 的更为详细的介绍,请阅读 Google 官方文档 HIDL (虽然写的很乱)。

简介

HAL 接口定义语言(简称 HIDL,发音为“hide-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。HIDL 允许指定类型和方法调用(会汇集到接口和软件包中)。从更广泛的意义上来说,HIDL 是用于在可以独立编译的代码库之间进行通信的系统。 HIDL 旨在用于进程间通信 (IPC)。进程之间的通信经过 Binder 化。对于必须与进程相关联的代码库,还可以使用直通模式(在 Java 中不受支持)。 说白了,就是为了实现进程间通信。在说通俗点,其实就是对 binder 进行进一步封装,好吧,又是 binder,所以在学习 hidl 流程时,需要先去了解 binder 的流程

例子1(支持直通式 passthrough)

这里用 bootcontrol 举例,代码路径在 hardware/interfaces/boot/1.0 ,这里分析的是 c++ 的 hidl。另外 passthrough 和 Binderized 的模式只是表示的注册时候的方式。

├── Android.bp
├── Android.mk
├── default
│   ├── android.hardware.boot@1.0-service.rc
│   ├── Android.mk
│   ├── BootControl.cpp
│   ├── BootControl.h
│   └── service.cpp
├── IBootControl.hal
├── types.hal
└── vts
    ├── Android.bp
    ├── Android.mk
    └── functional
        ├── Android.bp
        └── VtsHalBootV1_0TargetTest.cpp

vts 那部分先不看,主要用来 vts 测试的,主要是用来保证接口和 google 要求的保证一致。所以我们先来看一下 types.hal 和 IBootControl.hal。

//申明包
package android.hardware.boot@1.0;
//定义数据类型
struct CommandResult {
    bool success;
    string errMsg;
};

typedef uint32_t Slot;

enum BoolResult : int32_t {
    FALSE = 0,
    TRUE = 1,
    INVALID_SLOT = -1
};

package android.hardware.boot@1.0;

interface IBootControl {

  getNumberSlots() generates (uint32_t numSlots);

  getCurrentSlot() generates (Slot slot);

  markBootSuccessful() generates (CommandResult error);

  setActiveBootSlot(Slot slot) generates (CommandResult error);

  setSlotAsUnbootable(Slot slot) generates (CommandResult error);

  isSlotBootable(Slot slot) generates (BoolResult bootable);

  isSlotMarkedSuccessful(Slot slot) generates (BoolResult successful);

  getSuffix(Slot slot) generates (string slotSuffix);
};

注册服务

#define LOG_TAG "android.hardware.boot@1.0-service"

#include <android/hardware/boot/1.0/IBootControl.h>
#include <hidl/LegacySupport.h>

using ::android::hardware::boot::V1_0::IBootControl;
using android::hardware::defaultPassthroughServiceImplementation;

int main (int /* argc */, char * /* argv */ []) {
    return defaultPassthroughServiceImplementation<IBootControl>();
}

IBootControl 这个类是在哪实现的?其实是由 .hal 文件经过 update-makefiles.sh 脚本(hardware/interface 下有,framework 里面也有)编译生成的,它会生成 Android.bp Android.mk 文件,然后在 out 目录你可以查找一下 IBootControl.h ,里面定义了相关的结构体。

namespace android {
namespace hardware {
namespace boot {
namespace V1_0 {

struct IBootControl : public ::android::hidl::base::V1_0::IBase {
    virtual bool isRemote() const override { return false; }
.......

defaultPassthroughServiceImplementation 是一个模板函数,最终调用的是 IBootControl::getService() ,这个也是脚本编译生成的,在out/soong/下搜 IBootControlAll.cpp.看函数名称是不是很熟悉,都是在 binder 中出现过的,这边关注的 getTransport 函数。

::android::sp<IBootControl> IBootControl::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<IBootControl> 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(IBootControl::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);

代码路径在 system/hwservicemanager/Vintf.cpp ,注意到代码中的 GetFrameworkHalManifest 以及 GetDeviceHalManifest,分别从 /system/manifest.xml 以及 /vendor/manifest.xml 文件读取相关的 hidl 类型。system 下的 xml 文件由 Google 提供,路径在system/libhidl/manifest.xml,vendor 则由 soc 厂商提供,例如高通平台 /device/qcom/xxx/manifest.xml

vintf::Transport getTransport(const std::string &interfaceName, const std::string &instanceName) {
    FQName fqName(interfaceName);
..................

    vintf::Transport tr = getTransportFromManifest(fqName, instanceName,
            vintf::VintfObject::GetFrameworkHalManifest());
    if (tr != vintf::Transport::EMPTY) {
        return tr;
    }
    tr = getTransportFromManifest(fqName, instanceName,
            vintf::VintfObject::GetDeviceHalManifest());
    if (tr != vintf::Transport::EMPTY) {
        return tr;
    }

    LOG(WARNING) << __FUNCTION__ << ": Cannot find entry "
                 << fqName.string() << "/" << instanceName
                 << " in either framework or device manifest.";
    return vintf::Transport::EMPTY;
}

Android N 时,加载 hal 模块的时候,大都在 framework 中显式调用,引入 hidl 后,调用放在了 hwservicemanager 中。

    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(IBootControl::descriptor, serviceName);
            if (ret.isOk()) {
                sp<::android::hidl::base::V1_0::IBase> baseInterface = ret;
                if (baseInterface != nullptr) {
                    iface = IBootControl::castFrom(baseInterface);
                    if (!getStub || trebleTestingOverride) {
                        iface = new BsBootControl(iface);
                    }
                }
            }
        }
    }

ServiceManagement.cpp 中的 get 函数实现了加载。看一下 openLibs 函数,会根据参数通过函数 dlopen 加载 /vendor/lib/hw/android.hardware.boot@1.0-impl.so

    Return<sp<IBase>> get(const hidl_string& fqName,
                          const hidl_string& name) override {
        sp<IBase> ret = nullptr;

        openLibs(fqName, [&](void* handle, const std::string &lib, const std::string &sym) {
            IBase* (*generator)(const char* name);
            *(void **)(&generator) = dlsym(handle, sym.c_str());
            if(!generator) {
                const char* error = dlerror();
                LOG(ERROR) << "Passthrough lookup opened " << lib
                           << " but could not find symbol " << sym << ": "
                           << (error == nullptr ? "unknown error" : error);
                dlclose(handle);
                return true;
            }

            ret = (*generator)(name.c_str());

            if (ret == nullptr) {
                dlclose(handle);
                return true; // this module doesn't provide this instance name
            }

            registerReference(fqName, name);
            return false;
        });

        return ret;
    }

通过 dlsym 函数加载 HIDL_FETCH_IBootControl,HIDL_FETCH_IBootControl 结构体终于找到了我们熟悉的 hw_get_module ,这样子 hidl 就和 hal 紧紧联系在了一起。

IBootControl* HIDL_FETCH_IBootControl(const char* /* hal */) {
    int ret = 0;
    boot_control_module_t* module = NULL;
    hw_module_t **hwm = reinterpret_cast<hw_module_t**>(&module);
    ret = hw_get_module(BOOT_CONTROL_HARDWARE_MODULE_ID, const_cast<const hw_module_t**>(hwm));
    if (ret)
    {
        ALOGE("hw_get_module %s failed: %d", BOOT_CONTROL_HARDWARE_MODULE_ID, ret);
        return nullptr;
    }
    module->init(module);
    return new BootControl(module);
}

hal 层细节实现,通过该结构体进行绑定。代码路径在 hardware/qcom/bootctrl

boot_control_module_t HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .module_api_version = 1,
        .hal_api_version = 0,
        .id = BOOT_CONTROL_HARDWARE_MODULE_ID,
        .name = "Boot control HAL",
        .author = "Code Aurora Forum",
        .methods = &boot_control_module_methods,
    },
    .init = boot_control_init,
    .getNumberSlots = get_number_slots,
    .getCurrentSlot = get_current_slot,
    .markBootSuccessful = mark_boot_successful,
    .setActiveBootSlot = set_active_boot_slot,
    .setSlotAsUnbootable = set_slot_as_unbootable,
    .isSlotBootable = is_slot_bootable,
    .getSuffix = get_suffix,
    .isSlotMarkedSuccessful = is_slot_marked_successful,
};

framework 调用

bool BootControlAndroid::Init() {
  module_ = IBootControl::getService();
  if (module_ == nullptr) {
    LOG(ERROR) << "Error getting bootctrl HIDL module.";
    return false;
  }

  LOG(INFO) << "Loaded boot control hidl hal.";

  return true;
}

小结: goole 官方对于直通和绑定式服务介绍的都不是很清晰,但是可以肯定的是,是 hwbinder 还是 passthrough 都是在 mainfrest.xml 里面控制的。这里有一个疑问,那 defaultPassthroughServiceImplementation 存在的意义是什么?为何不用默认的纯绑定式进行注册服务?

例子2 (绑定式 )

这里分析的是纯绑定式的,这里以 fingerprint 为例

int main() {
    android::sp<IBiometricsFingerprint> bio = BiometricsFingerprint::getInstance();

    configureRpcThreadpool(1, true /*callerWillJoin*/);

    if (bio != nullptr) {
        bio->registerAsService();
    } else {
        ALOGE("Can't create instance of BiometricsFingerprint, nullptr");
    }

    joinRpcThreadpool();

    return 0; // should never get here
}

总结

hidl 的存在,并不是把整个 hal 层进行了打包封装,而是通过定义这种结构语言,将 hal 层操作放在了 hidl 中,而 hidl 又通过 binder 实现了进程间通信机制,framework 需要调用 hal 的函数,直接通过 hwservicemanager 实现,这样保证了 framework 和 hal 运行在不同进程,在进行 OTA 时,便可实现只升级 framework 。

对于码农来说,我们需要掌握 hidl 的东西在这篇内容已经够用了。包括启动 (android.hardware.boot@1.0-service.rc),注册(service.cpp hwservicemanager),client获取(IBootControl::getService)。

Search

    Table of Contents