Hidl直通式Passthrough分析案例-Mapper模块

背景知识

hi,粉丝朋友们大家好:
今天带大家分析一下mapper模块的Hidl相关内容,主要目的就是带大家看看直通式passthrough的方式是怎么样的,有哪些明显特点,及系统源码具体是怎么实现的直通式。

1、首先知道直通式和绑定式最重要区别就是hal相关代码是否运行在客户端进程
在这里插入图片描述

如上图展示直通式情况就是无论经过多少hidl包装始终hal代码都是运行在客户端systemserver进程
绑定式这个时候就涉及从systemserver跨进程到对应的hal进程

2、那么具体看hal代码啥的怎么区分呢,怎么知道他是直通还是绑定式呢?
manifest中看看hal注册的情况
可以去以下路径查看,这个就是最后编译出来的vendor下面的manifest文件
out/target/product/nx563j/vendor/etc/vintf/manifest.xml
比如我们要看mapper模块,可以看到这里transport写的是passthrough所以他就在直通式

<hal format="hidl">
<name>android.hardware.graphics.mapper</name>
<transport arch="32+64">passthrough</transport>
<version>2.1</version>
<interface>
<name>IMapper</name>
<instance>default</instance>
</interface>
<fqname>@2.1::IMapper/default</fqname>
</hal>

再看看其他模块比如android.hardware.bluetooth模块,明显看到这里的transport就是hwbinder他就是绑定式

<hal format="hidl">
<name>android.hardware.bluetooth</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>IBluetoothHci</name>
<instance>default</instance>
</interface>
<fqname>@1.0::IBluetoothHci/default</fqname>
</hal>

还有也可以通过源码分析方法:
比如mapper模块
在这里插入图片描述
明显看到这里mapper最后的编译只是一个so文件即cc_library_shared标签,没见到有其他cc_binary标签,所以最终产物只有一个android.hardware.graphics.mapper@2.0-impl.so
大家想想本身mapper就该在客户端进程运行,没有自己的进程,所以自然要以so形式存在,而不是可执行文件存在

再看看蓝牙模块android.hardware.bluetooth,这里明显看出编译的是一个cc_binary可执行文件android.hardware.bluetooth@1.0-servicee而且还是开机自启动有对于的rc文件的
在这里插入图片描述

源码分析

从应用层面构造Gralloc2Mapper开始分析

Gralloc2Mapper::Gralloc2Mapper() {
    ATRACE_CALL();
    mMapper = hardware::graphics::mapper::V2_0::IMapper::getService();
    if (mMapper == nullptr) {
        ALOGW("mapper 2.x is not supported");
        return;
    }
    if (mMapper->isRemote()) {
        LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
    }
    ALOGW("mapper 2.x is  supported");
    // IMapper 2.1 is optional
    mMapperV2_1 = IMapper::castFrom(mMapper);
}

明显看到这里关键地方其实就是调用了hardware::graphics::mapper::V2_0::IMapper::getService()获取的,这里的大家注意了客户端调用hardware::graphics::mapper::V…就代表这个代码不会在应用业务代码里面,一般是在这个hal文件模块生成的相关文件中去看。

到了hal生成文件的方法:
out/soong/.intermediates/hardware/interfaces/graphics/mapper/2.0/android.hardware.graphics.mapper@2.0_genc++/gen/android/hardware/graphics/mapper/2.0/MapperAll.cpp

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

接下来看看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;

    sp<IBase> base = getRawServiceInternal(IType::descriptor, instance, retry, getStub);

    if (base == nullptr) {
        return nullptr;
    }

    if (base->isRemote()) {
        // getRawServiceInternal guarantees we get the proper class
        return sp<IType>(new BpType(getOrCreateCachedBinder(base.get())));
    }

    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;

        sm = defaultServiceManager1_1();
     	//获取transport类型,其实就是获取一下是PASSTHROUGH还是HWBINDER,即直通式还是绑定式
     	//这里其实本质是访问一下写hidl注册的mainfest为依据
        Return<Transport> transportRet = sm->getTransport(descriptor, instance);

        transport = transportRet;
    const bool vintfHwbinder = (transport == Transport::HWBINDER);
    const bool vintfPassthru = (transport == Transport::PASSTHROUGH);
    const bool trebleTestingOverride = isTrebleTestingOverride();
    const bool allowLegacy = !kEnforceVintfManifest || (trebleTestingOverride && isDebuggable());
    const bool vintfLegacy = (transport == Transport::EMPTY) && allowLegacy;
	//vintfHwbinder为true则会进入下面
    for (int tries = 0; !getStub && (vintfHwbinder || vintfLegacy); tries++) {

        Return<sp<IBase>> ret = sm->get(descriptor, instance);

    
    }
    	//vintfPassthru为true则会进入下面
    if (getStub || vintfPassthru || vintfLegacy) {
    //获取相应的getPassthroughServiceManager
        const sp<IServiceManager1_0> pm = getPassthroughServiceManager();
        if (pm != nullptr) {
             //调用对应的getPassthroughServiceManager的get方法
            sp<IBase> base = pm->get(descriptor, instance).withDefault(nullptr);
            return base;
        }
    }
    ALOGE(" getRawServiceInternal  end 2");
    return nullptr;
}

这里看看manifest中mapper的是hwbinder还是passthrough

<hal format="hidl">
<name>android.hardware.graphics.mapper</name>
<transport arch="32+64">passthrough</transport>
<version>2.1</version>
<interface>
<name>IMapper</name>
<instance>default</instance>
</interface>
<fqname>@2.1::IMapper/default</fqname>
</hal>

可以看到这里显示2.1版本,实际如果是2.0版本其实也没有关系,一样可以正常返回哈

识别为passthrough模式后,那么接下来就要获取相关的hal对象这个hal对象是直接调用的getPassthroughServiceManager获取的manager,这个manager就是个本地的,不需要进行跨进程获取获取
看看是怎么获取的

system/libhidl/transport/ServiceManagement.cpp

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

        openLibs(fqName, [&](void* handle, const std::string &lib, const std::string &sym) {
            IBase* (*generator)(const char* name);
            *(void **)(&generator) = dlsym(handle, sym.c_str());
         
			//最重要调用到了HIDL_FETCH_IMapper
            ret = (*generator)(name.c_str());


            return false;
        });

        return ret;
    }

先看看openLibs方法

system/libhidl/transport/ServiceManagement.cpp
```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
        size_t idx = fqName.find("::");

        if (idx == std::string::npos ||
                idx + strlen("::") + 1 >= fqName.size()) {
            LOG(ERROR) << "Invalid interface name passthrough lookup: " << fqName;
            return;
        }

        std::string packageAndVersion = fqName.substr(0, idx);
        std::string ifaceName = fqName.substr(idx + strlen("::"));

        const std::string prefix = packageAndVersion + "-impl";
        const std::string sym = "HIDL_FETCH_" + ifaceName;
     //主要为了拼出相关的的so文件名字,prefix: android.hardware.graphics.mapper@2.0-impl sym HIDL_FETCH_IMapper

        for (const std::string& path : paths) { //开始相关的odm,vendor目录寻找so
            std::vector<std::string> libs = findFiles(path, prefix, ".so");
            LOG(ERROR) << "findFiles path " << path;
            for (const std::string &lib : libs) {
                const std::string fullPath = path + lib;
				//dlopen加载对于的so
                if (kIsRecovery || path == HAL_LIBRARY_PATH_SYSTEM) {
                    handle = dlopen(fullPath.c_str(), dlMode);
                } else {
                    handle = android_load_sphal_library(fullPath.c_str(), dlMode);
                }
                //加载后,回调eachLib方法
                if (!eachLib(handle, lib, sym)) {
                    return;
                }
            }
        }
    }

这个地方openLibs干的关键事情:
1、通过descriptor把so名字拼出来:so名字:android.hardware.graphics.mapper@2.0-impl ,并且进行dlopen,变成handle
2、同时也拼出一个so里面含有的符号:HIDL_FETCH_IMapper
3、回调eachLib方法

下面来分析eachLib方法,就是一个lamada表达式

openLibs(fqName, [&](void* handle, const std::string &lib, const std::string &sym) {
            IBase* (*generator)(const char* name);
            *(void **)(&generator) = dlsym(handle, sym.c_str());
         
			//最重要调用到了HIDL_FETCH_IMapper
            ret = (*generator)(name.c_str());


            return false;
        });

这个HIDL_FETCH_IMapper一调用就到了正式的hidl的mapper模块了

hardware/interfaces/graphics/mapper/2.0/default/passthrough.cpp

extern "C" IMapper* HIDL_FETCH_IMapper(const char* /*name*/) {
    return GrallocLoader::load();
}

看看GrallocLoader::load()方法

  static IMapper* load() {
        const hw_module_t* module = loadModule();//调用了loadModule获取hw_module_t
        if (!module) {
            return nullptr;
        }
        auto hal = createHal(module);
        if (!hal) {
            return nullptr;
        }
        return createMapper(std::move(hal));
    }

来看看loadModule

// load the gralloc module
static const hw_module_t* loadModule() {
    const hw_module_t* module;
    int error = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
    return module;
}

这里其实就是开始打开一个id为GRALLOC_HARDWARE_MODULE_ID的硬件抽象模块

再看看createHal方法

  // create a MapperHal instance
    static std::unique_ptr<hal::MapperHal> createHal(const hw_module_t* module) {
        int major = getModuleMajorApiVersion(module);
        //根据主版本号来创建初始化不一样的Gralloc
        switch (major) {
            case 1: {
                auto hal = std::make_unique<Gralloc1Hal>();
                return hal->initWithModule(module) ? std::move(hal) : nullptr;
            }
            case 0: {
                auto hal = std::make_unique<Gralloc0Hal>();
                return hal->initWithModule(module) ? std::move(hal) : nullptr;
            }
            default:
                ALOGE("unknown gralloc module major version %d", major);
                return nullptr;
        }
    }

再看看createMapper

 //主要就是返回GrallocMapper指针
    static IMapper* createMapper(std::unique_ptr<hal::MapperHal> hal) {
        auto mapper = std::make_unique<GrallocMapper<hal::Mapper>>();
        return mapper->init(std::move(hal)) ? mapper.release() : nullptr;
    }
};

哪里触发的创建Mapper

这个其实是在第一次dequeueBuffer时候会有这个触发
具体trace如下:
在这里插入图片描述
在dequeueBuffer时候会构造GraphicBuffer对象
在这里插入图片描述

这里的GraphicBuffer构造调用了GraphicBufferMapper方法
在这里插入图片描述

流程总结图

在这里插入图片描述
本文章对应视频手把手教你学framework:
https://mp.weixin.qq.com/s/LbVLnu1udqExHVKxd74ILg
私聊作者+v(androidframework007)
点击这里 https://mp.weixin.qq.com/s/Qv8zjgQ0CkalKmvi8tMGaw
视频:https://www.bilibili.com/video/BV1wc41117L4/
在这里插入图片描述

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值