Android 12图像显示系统简述Allocator/Mapper HAL服务的获取过程

2022年6月1日08:38:18

必读:

Android 12(S) 图像显示系统 - 开篇


一、基本概念

在上一篇文章中,我们有简单介绍过 Gralloc Allocator/Mapper HAL的内容。Gralloc HAL 分为了两部分:一个是 allocator ,一个是 mapper。Android系统定义了标准的 Gralloc HAL interface,具体实现有OEM/芯片厂商完成。

1.1 allocator HAL interface 的定义

Allocator 实现为一个 Binderized HAL Service(绑定式HAL),运行在独立的进程中,使用者通过 HwBinder 与之建立连接,类似与AIDL获取服务的方式。

/hardware/interfaces/graphics/allocator/4.0/IAllocator.hal

1.2 mapper HAL interface 的定义

Mapper 实现为一个Passthrough HAL Service(直通式HAL), 运行在调用它的进程中。本质上 Mode of HIDL in which the server is a shared library, dlopened by the client. In passthrough mode, client and server are the same process but separate codebases.

/hardware/interfaces/graphics/mapper/4.0/IMapper.hal

1.3 Binderized HALs(绑定式HAL)

Indicates HIDL is being used for remote procedure calls between processes, implemented over a Binder-like mechanism. See also passthrough.

android.hardware.graphics.allocator@x.0 ==> Required to be binderized in Android 8.0 so file descriptors don't have to be shared between trusted and untrusted processes.

Android 8.0之后的版本要求,allocator实现为 Binderized HAL,用于分配图形缓存,即为GraphicBuffer分配实际内存。

Mode of HIDL in which the server is a shared library, dlopened by the client. In passthrough mode, client and server are the same process but separate codebases. Used only to bring legacy codebases into the HIDL model. See also Binderized.

android.hardware.graphics.mapper@x.0 ==> Maps memory into the process it lives in.

二、GraphicBufferMapper的创建流程

创建GraphicBufferMapper对象时,其构造函数中会去创建GrallocMapper对象,系统中会有不同版本的 grolloc-mapper,优先使用高版本,所以创建 Gralloc4Mapper 对象

[/frameworks/native/libs/ui/GraphicBufferMapper.cpp]

GraphicBufferMapper::GraphicBufferMapper() {
    // 优先选择高版本的 gralloc-mapper
    mMapper = std::make_unique<const Gralloc4Mapper>();
    if (mMapper->isLoaded()) {
        mMapperVersion = Version::GRALLOC_4;
        return;
    }
    ...
}

Gralloc4Mapper的构造函数中去获取 gralloc-mapper hal service,这是一个 passthrough hal service

Gralloc4Mapper::Gralloc4Mapper() {
    mMapper = IMapper::getService(); // 去获取服务,
    ...
}

IMaper::getService() 是编译系统根据 IMapper HIDL interface 自动生成的。

IMapper HIDL interface 的定义位于:

/hardware/interfaces/graphics/mapper/4.0/IMapper.hal

编译完源码后,out目录下会生成根据 IMapperHDIL interface 生成的头文件和源码文件

android/out/soong/.intermediates/hardware/interfaces/graphics/mapper/4.0

IMaper::getService()可以在 android.hardware.graphics.mapper@4.0_genc++ 目录下的 MapperAll.cpp 中看到其定义,如下:

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

其中还有一个标识字符串:

const char* IMapper::descriptor("android.hardware.graphics.mapper@4.0::IMapper");

流程开始进入到Android系统原生的获取服务的流程::android::hardware::details::getServiceInternal 这个函数定义在:

/system/libhidl/transport/include/hidl/HidlTransportSupport.h

template <typename BpType, typename IType = typename BpType::Pure,
          typename = std::enable_if_t<std::is_same<i_tag, typename IType::_hidl_tag>::value>,
          typename = std::enable_if_t<std::is_same<bphw_tag, typename BpType::_hidl_tag>::value>>
sp<IType> getServiceInternal(const std::string& instance, bool retry, bool getStub) {
    using ::android::hidl::base::V1_0::IBase;
    // getRawServiceInternal 去获取服务
    sp<IBase> base = getRawServiceInternal(IType::descriptor, instance, retry, getStub);
    ...// 省略一些检查过程
    return IType::castFrom(base);
}

紧接着往下走,就进入到getRawServiceInternal方法中

[/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 = IServiceManager1_0::Transport;
    sp<Waiter> waiter;

    sp<IServiceManager1_1> sm;
    Transport transport = Transport::EMPTY;
    // 判断要获取这个service是那种模式:passthroght mode(直通式HAL) or binderized mode(绑定式HAL)
    if (kIsRecovery) {
        transport = Transport::PASSTHROUGH;
    } else {
        sm = defaultServiceManager1_1();
        Return<Transport> transportRet = sm->getTransport(descriptor, instance);
        transport = transportRet;
    }
    ...
    for (int tries = 0; !getStub && (vintfHwbinder || vintfLegacy); tries++) {
        //  binderized mode(绑定式HAL),使用 defaultServiceManager 去检索、获取服务
        Return<sp<IBase>> ret = sm->get(descriptor, instance);
        sp<IBase> base = ret;
        if (base != nullptr) {
            Return<bool> canCastRet =
                details::canCastInterface(base.get(), descriptor.c_str(), true /* emitError */);
                ...
            if (canCastRet.isOk() && canCastRet) {
                ...
                return base; // still needs to be wrapped by Bp class.
            }
        }
    }
    ...
    if (getStub || vintfPassthru || vintfLegacy) {
        //  passthroght mode(直通式HAL),使用 defaultServiceManager 去检索、获取服务
        const sp<IServiceManager1_0> pm = getPassthroughServiceManager();
        if (pm != nullptr) {
            sp<IBase> base = pm->get(descriptor, instance).withDefault(nullptr);
            if (!getStub || trebleTestingOverride) {
                // 封装
                base = wrapPassthrough(base);
            }
            return base;
        }
    }

    return nullptr;
}

大概讲一下它的做的事情:

1. 判断要获取的HAL服务的类型,是直通式HAL--Transport::PASSTHROUGH,还是绑定式HAL --Transport::HWBINDER

2. 如果是binderized mode绑定式HAL --Transport::HWBINDER,则会通过defaultServiceManager 去获取这个服务的代理,这个过程类似与框架层的binder service,比如 gralloc-allocator hal

3. 如果是passthrough mode直通式HAL--Transport::PASSTHROUGH,则会通过 PassthroughServiceManager 去获取这个服务,这个过程本质上是在当前进程中使用dlopen/dlsym加载HAL动态库并获取函数HIDL_FETCH_interfaceName  的地址,HAL library中会去实现这个FETCH方法,比如  gralloc-mapper hal ,下面 arm gralloc 的实现,可以自行去官网下载源码:Open Source Mali GPUs Android Gralloc Module

extern "C" IMapper *HIDL_FETCH_IMapper(const char * /* name */)
{
	MALI_GRALLOC_LOGV("Arm Module IMapper %d.%d , pid = %d ppid = %d ", GRALLOC_VERSION_MAJOR,
	                  (HIDL_MAPPER_VERSION_SCALED - (GRALLOC_VERSION_MAJOR * 100)) / 10, getpid(), getppid());

	return new arm::mapper::GrallocMapper();
}

4. 不管是binderized还是passthrough ,最后得到都是一个进程了::android::hidl::base::V1_0::IBase的HIDL Interface对象,经过适当处理,就可以返回给使用者了

对于 gralloc-mapper hal 因为采用的是passthrough mode,我们继续看看PassthroughServiceManager 去获取服务是大概是做了什么事情。

主要是去看PassthroughServiceManager::get() 方法

[/system/libhidl/transport/ServiceManagement.cpp]

Return<sp<IBase>> get(const hidl_string& fqName,
                      const hidl_string& name) override {
    sp<IBase> ret = nullptr;
    // 加载动态库,主要openLibs第二个参数是函数指针
    openLibs(fqName, [&](void* handle, const std::string &lib, const std::string &sym) {
        IBase* (*generator)(const char* name);
        // 去已加载的动态库中检索指定函数的地址,sym是HIDL_FETCH_xxx,比如HIDL_FETCH_IMapper
        *(void **)(&generator) = dlsym(handle, sym.c_str());
        ...
        // 调用找到的函数,比如 HIDL_FETCH_IMapper
        ret = (*generator)(name.c_str());
        ...
        return false;
    });

    return ret;
}

PassthroughServiceManager::get() 方法中首先就是去调用openLibs去寻找和加载动态库,openLibs定义如下:

[/system/libhidl/transport/ServiceManagement.cpp]

static void openLibs(
    const std::string& fqName,
    const std::function<bool /* continue */ (void* /* handle */, const std::string& /* lib */,
                                             const std::string& /* sym */)>& eachLib) {
    //fqName looks like android.hardware.foo@1.0::IFoo
    // fqName 就是要找的服务的标识符,android.hardware.graphics.mapper@4.0::IMapper
    size_t idx = fqName.find("::");
    // 分离出包名和版本 == android.hardware.graphics.mapper@4.0
    std::string packageAndVersion = fqName.substr(0, idx);
    // 分离出接口名字 == IMapper
    std::string ifaceName = fqName.substr(idx + strlen("::"));
    // 要找的动态库名字的前缀 == android.hardware.graphics.mapper@4.0-impl
    const std::string prefix = packageAndVersion + "-impl";
    // 要找的动态库中的函数名字 == HIDL_FETCH_IMapper
    const std::string sym = "HIDL_FETCH_" + ifaceName;
    // dlopen 选项
    constexpr int dlMode = RTLD_LAZY;
    void* handle = nullptr;

    dlerror(); // clear

    static std::string halLibPathVndkSp = details::getVndkSpHwPath();
    // 查找路径 "/odm/lib/hw/", "/vendor/lib/hw/", "/system/lib/hw/"
    std::vector<std::string> paths = {
        HAL_LIBRARY_PATH_ODM, HAL_LIBRARY_PATH_VENDOR, halLibPathVndkSp,
#ifndef __ANDROID_VNDK__
        HAL_LIBRARY_PATH_SYSTEM,
#endif
    };
    ...
    // 开始查找
    for (const std::string& path : paths) {
        // findFiles就是在path目录下,查找名字的前缀是prefix,后缀是.so的库
        std::vector<std::string> libs = findFiles(path, prefix, ".so");

        for (const std::string &lib : libs) {
            const std::string fullPath = path + lib;

            if (kIsRecovery || path == HAL_LIBRARY_PATH_SYSTEM) {
                handle = dlopen(fullPath.c_str(), dlMode);
            } else {
#if !defined(__ANDROID_RECOVERY__) && defined(__ANDROID__)
                // 加载动态库, 比如 /vendor/lib/hw/android.hardware.graphics.mapper@4.0-impl-arm.so
                handle = android_load_sphal_library(fullPath.c_str(), dlMode);
#endif
            }
            // 加载失败,继续加载找到的满足条件的其它库
            if (handle == nullptr) {
                const char* error = dlerror();
                LOG(ERROR) << "Failed to dlopen " << lib << ": "
                           << (error == nullptr ? "unknown error" : error);
                continue;
            }
            // 加载成功,函数eachLib中去找sym这个函数的地址,并执行
            if (!eachLib(handle, lib, sym)) {
                return;
            }
        }
    }
}

openLibs函数的处理逻辑也很清晰,主要工作就是:

1. 根据要检索的服务的 descriptor ,去指定的目录下(/odem/lib/hw or /vendor/lib/hw or /system/lib/hw)寻找对应的动态库;

2. 找到动态库后,加载 android_load_sphal_library / dlopen ;

3. 调用eachLib这个函数,去动态库中找到 HIDL_FETCH_xxx 这个函数的地址;

4. eachLib就是在PassthroughServiceManager::get() 方法中调用openLibs时设置的,其中就会调用 HIDL_FETCH_xxx 这个函数去创建服务对象。

经过上面的流程,加载了动态库,找到了HIDL_FETCH_xxx 这个函数,并且调用它创建了一个服务对象,然后再经过必要处理wrapPassthrough(base)就把这个服务的代理返回给了使用者。

三、GraphicBufferAllocator的创建流程

GraphicBufferAllocator 构造函数中会去创建一个Gralloc4Allocator对象,并且传递一个Gralloc4Mapper参数

[/frameworks/native/libs/ui/GraphicBufferAllocator.cpp]

GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()) {
    mAllocator = std::make_unique<const Gralloc4Allocator>(
            reinterpret_cast<const Gralloc4Mapper&>(mMapper.getGrallocMapper()));
    ...
}

Gralloc4Allocator的构造函数如下:

[/frameworks/native/libs/ui/Gralloc4.cpp]

Gralloc4Allocator::Gralloc4Allocator(const Gralloc4Mapper& mapper) : mMapper(mapper) {
    mAllocator = IAllocator::getService();
    if (mAllocator == nullptr) {
        ALOGW("allocator 3.x is not supported");
        return;
    }
}

上述代码的是不是和Gralloc4Mapper很相似,区别在于 gralloc-allocator 是 Binderied HAL,在获取服务时有所区别,前面的分析中也有提到,在这里就不继续深入讲解了。Binderied HAL 实现部分会作为一个service独立运行在一个进程中,非常类似 Binder IPC的机制,可以仅作高度概括的理解即可。

五、小结

本文只是一点关于 Gralloc Allocator/Mapper HAL的补充知识,简单分析获取 HAL service的一些流程。到这关于GraphicBuffer及Gralloc的大概的知识点就讲完了,后续学习中会再根据自己遇到的实际问题及思考,陆续补充心得及细节。

补充:

Android Graphic IMpper Interface 解读

  • 作者:二的次方
  • 原文链接:https://www.cnblogs.com/roger-yu/p/16041181.html
    更新时间:2022年6月1日08:38:18 ,共 9011 字。