Android SurfaceFlinger——屏幕热插拔回调(九)

news2024/11/24 6:46:00

        上一篇文章分析了回调注册监听的调用流程,对于数据的回调正好是注册监听的逆向调用。首先前面提到过在 HWC2On1Adapter 中就会直接转型为每一个回调到上层,这里我们就看一下屏幕热插拔回调(hotplugHook)的调用流程。

一、硬件回调

1、HWC2On1Adapter.cpp

源码位置:/hardware/interfaces/graphics/composer/2.1/utils/hwc2on1adapter/HWC2On1Adapter.cpp

class HWC2On1Adapter::Callbacks : public hwc_procs_t {
    public:
        explicit Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) {
            invalidate = &invalidateHook;
            vsync = &vsyncHook;
            hotplug = &hotplugHook;
        }

        ……
        static void hotplugHook(const hwc_procs_t* procs, int display, int connected) {
            auto callbacks = static_cast<const Callbacks*>(procs);
            callbacks->mAdapter.hwc1Hotplug(display, connected);
        }

    private:
        HWC2On1Adapter& mAdapter;
};

        这是我们传入底层的 Callback,当屏幕出现热插拔时会调用到 hotplugHook() 函数,这里又调用了自身的 hwc1Hotplug() 方法。

hwc1Hotplug

void HWC2On1Adapter::hwc1Hotplug(int hwc1DisplayId, int connected) {
    ALOGV("Received hwc1Hotplug(%d, %d)", hwc1DisplayId, connected);

    if (hwc1DisplayId != HWC_DISPLAY_EXTERNAL) {
        ALOGE("hwc1Hotplug: Received hotplug for non-external display");
        return;
    }

    std::unique_lock<std::recursive_timed_mutex> lock(mStateMutex);

    hwc2_display_t displayId = UINT64_MAX;
    if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
        if (connected == 0) {
            ALOGW("hwc1Hotplug: Received disconnect for unconnected display");
            return;
        }

        // 在connect上创建一个新显示
        auto display = std::make_shared<HWC2On1Adapter::Display>(*this, HWC2::DisplayType::Physical);
        display->setHwc1Id(HWC_DISPLAY_EXTERNAL);
        display->populateConfigs();
        displayId = display->getId();
        mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL] = displayId;
        mDisplays.emplace(displayId, std::move(display));
    } else {
        if (connected != 0) {
            ALOGW("hwc1Hotplug: Received connect for previously connected " "display");
            return;
        }

        // 断开现有显示器
        displayId = mHwc1DisplayMap[hwc1DisplayId];
        mHwc1DisplayMap.erase(HWC_DISPLAY_EXTERNAL);
        mDisplays.erase(displayId);
    }

    // 如果hwc2端回调还没有被注册,缓冲它直到它被注册
    if (mCallbacks.count(Callback::Hotplug) == 0) {
        mPendingHotplugs.emplace_back(hwc1DisplayId, connected);
        return;
    }

    const auto& callbackInfo = mCallbacks[Callback::Hotplug];

    // 未持有状态锁的情况下回调
    lock.unlock();

    auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(callbackInfo.pointer);
    auto hwc2Connected = (connected == 0) ? HWC2::Connection::Disconnected : HWC2::Connection::Connected;
    hotplug(callbackInfo.data, displayId, static_cast<int32_t>(hwc2Connected));
}

        如果没有注册热插拔的监听,则会保存到 mPendingHotplugs 集合中,等待回调监听。该回调只处理物理屏幕的逻辑。如果 connect 是 0 且 mHwc1DisplayMap 大于 0 说明有屏幕链接过,现在断开链接了,则从 mHwc1DisplayMap 中销毁对应 id,销毁 mDisplays 对应的屏幕对象对象。如果 connect 为 1,说明有屏幕链接进来了。此时会生成一个 HWC2On1Adapter::Display 对象保存 mDisplays。最后调用了 hotplug() 方法。

2、HalImpl.cpp

源码位置:/hardware/google/graphics/common/hwc3/impl/HalImpl.cpp

void hotplug(hwc2_callback_data_t callbackData, hwc2_display_t hwcDisplay, int32_t connected) {
    auto hal = static_cast<HalImpl*>(callbackData);
    int64_t display;

    h2a::translate(hwcDisplay, display);
    hal->getEventCallback()->onHotplug(display, connected == HWC2_CONNECTION_CONNECTED);
}

        这里的 hotplug() 方法对应的就是 Hal 层 HwcHal.h 中的 hotplugHook() 方法,对于如何实现的就要使用的 Hook 技术了。对于该技术在前面 PMS 专栏中有过介绍,这里就不重复介绍了。

二、Hal层回调

 1、HwcHal.h

源码位置:/hardware/interfaces/graphics/composer/2.1/utils/passthrough/include/composer-passthrough/2.1/HwcHal.h

static void hotplugHook(hwc2_callback_data_t callbackData, hwc2_display_t display, int32_t connected) {
    auto hal = static_cast<HwcHalImpl*>(callbackData);
    hal->mEventCallback->onHotplug(display, static_cast<IComposerCallback::Connection>(connected));
}

        这里的 hotplugHook 方法会回调到前面啊文章提到 HalEventCallback 中的对应方法。  

2、ComposerClient.h

源码位置:/hardware/interfaces/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h

HalEventCallback

        前面文章提到过,HalEventCallback 是对 IComposerCallback 的进一步封装。

class HalEventCallback : public Hal::EventCallback {
    public:
        HalEventCallback(Hal* hal, const sp<IComposerCallback> callback, ComposerResources* resources)
            : mHal(hal), mCallback(callback), mResources(resources) {}

        void onHotplug(Display display, IComposerCallback::Connection connected) {
            if (connected == IComposerCallback::Connection::CONNECTED) {
                if (mResources->hasDisplay(display)) {
                    // 这是一个后续的热插拔“连接”显示器。
                    // 表明显示发生了变化,因此需要重新分配缓冲区。
                    cleanDisplayResources(display, mResources, mHal);
                    mResources->removeDisplay(display);
                }
                mResources->addPhysicalDisplay(display);
            } else if (connected == IComposerCallback::Connection::DISCONNECTED) {
                mResources->removeDisplay(display);
            }

            auto ret = mCallback->onHotplug(display, connected);
            ALOGE_IF(!ret.isOk(), "failed to send onHotplug: %s", ret.description().c_str());
        }

    protected:
        Hal* const mHal;
        const sp<IComposerCallback> mCallback;
        ComposerResources* const mResources;
};

        这个方法本质上就是为了集中 IComposerCallback 和 mResources 处理。在 HalEventCallback 中对应每一种回调都有自己的方法,他除了会调用 IComposerCallback 对应的回调之外,还会特别处理屏幕的热插拔,如果是插入则将一个屏幕对象(hwc2_display_t)添加到 ComposerResources 中进行管理。 

        而这里的 Callback 就是上层的 ComposerCallbackBridge,位于 HidlComposerHal.cpp 中。这里我们先来看一下 ComposerResources 管理的 addPhysicalDisplay() 方法。

3、ComposerResources.cpp

源码位置:/hardware/interfaces/graphics/composer/2.1/utils/resources/ComposerResources.cpp

Error ComposerResources::addPhysicalDisplay(Display display) {
    auto displayResource = createDisplayResource(ComposerDisplayResource::DisplayType::PHYSICAL, 0);

    std::lock_guard<std::mutex> lock(mDisplayResourcesMutex);
    auto result = mDisplayResources.emplace(display, std::move(displayResource));
    return result.second ? Error::NONE : Error::BAD_DISPLAY;
}

        这里调用了 createDisplayResource() 方法。

createDisplayResource

std::unique_ptr<ComposerDisplayResource> ComposerResources::createDisplayResource(
        ComposerDisplayResource::DisplayType type, uint32_t outputBufferCacheSize) {
    return std::make_unique<ComposerDisplayResource>(type, mImporter, outputBufferCacheSize);
}

        在这个过程中,Composer 会把每一个 hwc2_display_t 最终封装成一个个 ComposerDisplayResource,加入到集合当中。 

ComposerHandleCache

 ComposerDisplayResource::ComposerDisplayResource(DisplayType type, ComposerHandleImporter& importer, uint32_t outputBufferCacheSize)
    : mType(type),
      mClientTargetCache(importer),
      mOutputBufferCache(importer, ComposerHandleCache::HandleType::BUFFER, outputBufferCacheSize),
      mMustValidate(true) {}

        可以看到,此时就是把 ComposerResources 中的图元申请服务交给 ComposerDisplayResource 持有,让 Display 自己拥有控制图元的能力,同时初始化缓存为 0。当缓存好 ComposerDisplayResource 之后就会继续回调到顶层。

3、HidlComposerHal.cpp

Return<void> onHotplug(Display display, Connection connection) override {
    mCallback.onComposerHalHotplug(display, connection);
    return Void();
}

private:
    ComposerCallback& mCallback;

        而这个 Callback 就是 SurfaceFlinger。所以这里调用的是 SurfaceFlinger 中的对应方法。

4、SurfaceFlinger

源码位置:/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

onComposerHalHotplug

void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, hal::Connection connection) {
    const bool connected = connection == hal::Connection::CONNECTED;
    ALOGI("%s HAL display %" PRIu64, connected ? "Connecting" : "Disconnecting", hwcDisplayId);

    // 只有不在主线程时才锁。
    // 这个函数通常是在hwbinder线程中调用的,但是对于主显示,它是在已经持有状态锁的主线程中调用的,所以不要试图在这里获取它。
    ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);

    // 将接收到的热插拔事件添加到mPendingHotplugEvents队列中
    mPendingHotplugEvents.emplace_back(HotplugEvent{hwcDisplayId, connection});

    if (std::this_thread::get_id() == mMainThreadId) {
        // 如果我们在主线程上,立即处理所有挂起的热插拔事件。            
        processDisplayHotplugEventsLocked();
    }
    // 标记需要进行一次显示相关的事务处理
    setTransactionFlags(eDisplayTransactionNeeded);
}

        该函数响应外部硬件事件(如显示器连接或断开),并高效、安全地安排这些事件的处理逻辑,确保了系统UI的实时性和一致性。此时终于来到了前面文章中解析的 SurfaceFlinger 层中屏幕的初始化流程中。

总结

  • Composer 其实最重要的行为就是 createClient,创建一个 ComposerClient。
  • ComposerClient 会持有 HwcHalImpl,该类因为持有 hw2_device_t,所以拥有了和硬件通信的能力。ComposerResource 中持有 ComposerInputHandler 这个对象负责图元服务的控制。ComposerCommandEngine 则是处理一些来自上层的命令。
  • HWC2On1Adapter 则是为了适配 hw_device_t 和版本 2 之间的区别,同时注册一个监听到驱动中。
  • 对于回调来讲,分为两部分,当 registerCallback 的时候会消耗从底层回调上来。另一部分则是从底层直接拿到对应的 HwcHalImpl 的 hook 函数向上回调。

原理图 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1850235.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库系统概论——数据库恢复技术

文章目录 数据库恢复技术事务的基本概念什么是事务如何定义事务&#xff1a;事务的特性 数据库恢复概述故障的种类恢复的实现技术恢复策略事务故障的恢复系统故障的恢复介质故障的恢复 数据库恢复技术 事务的基本概念 什么是事务 事务使用户定义的一个数据库操作序列&#x…

步步精:连接器领域的卓越品牌

自1987年成立以来&#xff0c;步步精坐落于美丽的旅游城市——温州市乐清虹桥镇&#xff0c;被誉为“国家电子主体生产基地”、“国家精密模具制造基地”。公司拥有7大厂区、9大事业部&#xff0c;800名专职员工&#xff0c;致力于提供高品质的连接器解决方案。注册商标“BBJCO…

数据库精选题(三)(SQL语言精选题)(按语句类型分类)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;数据库 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 前言 创建语句 创建表 创建视图 创建索引…

【机器学习】基于Softmax松弛技术的离散数据采样

1.引言 1.1.离散数据采样的意义 离散数据采样在深度学习中起着至关重要的作用&#xff0c;它直接影响到模型的性能、泛化能力、训练效率、鲁棒性和解释性。 首先&#xff0c;采样方法能够有效地平衡数据集中不同类别的样本数量&#xff0c;使得模型在训练时能够更均衡地学习…

开启声音的奇幻之旅:AI声音变换器的魔法秘籍与创意应用

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/这个充满科技魔力的时代&#xff0c;AI Voice Changer 就像一把神奇的钥匙&#xff0c;能为我们打开声音的魔法之门。今天&#xff0c;就让我带你…

在线教育系统源码入门:教育培训小程序开发全流程

本篇文章&#xff0c;笔者将详细介绍在线教育系统源码的入门知识&#xff0c;并带领大家了解教育培训小程序的开发全流程。 一、在线教育系统的基本概念 一个完整的在线教育系统应具备以下几个模块&#xff1a; 用户管理 课程管理 教学互动 支付模块 数据统计 二、开发工…

【开发】内网穿透ztncui搭建私有节点

文章目录 写在前面一键部署ztnuci记录后续 写在前面 前面搭建moon节点转发的确会降低延迟&#xff0c;但是总有出现moon节点解析不成功的例子&#xff0c;于是疯狂寻找答案是为什么&#xff1f;终于在知乎上找到这样一个答案。 一键部署ztnuci 参考这篇很完善的教程和贴心的…

小i机器人:总负债5.31亿,员工数量在减少,银行借款在增加,净利润已下降-362.68%

小i机器人:总负债5.31亿,员工数量在减少,银行借款在增加,总收入在增长,净利润已下降-362.68% 来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 目录 一、小i机器人公司介绍 二、小i机器人过去20年的发展历程和取得的成就 三、小i机器人的产品和技术架构 四、小i机器人…

动手学深度学习(Pytorch版)代码实践 -卷积神经网络-24深度卷积神经网络AlexNet

24深度卷积神经网络AlexNet import torch from torch import nn import liliPytorch as lp import liliPytorch as lp import matplotlib.pyplot as pltdropout1 0.5 #Alexnet架构 net nn.Sequential(nn.Conv2d(1, 96, kernel_size11, stride4, padding1),nn.ReLU(),nn.MaxPo…

29-Linux--守护进程

一.基础概念 1.守护进程&#xff1a;精灵进程&#xff0c;在后台为用户提高服务&#xff0c;是一个生存周期长&#xff0c;通常独立于控制终端并且周期性的执行任务火处理事件发生 2.ps axj&#xff1a;查看守护进程 3.进程组&#xff1a;多个进程的集合&#xff0c;由于管理…

# 消息中间件 RocketMQ 高级功能和源码分析(十一)

消息中间件 RocketMQ 高级功能和源码分析&#xff08;十一&#xff09; 一、消息中间件 RocketMQ 源码分析&#xff1a; 拉取消息长轮询机制 1、消息拉取长轮询机制分析 RocketMQ 未真正实现消息推模式&#xff0c;而是消费者主动向消息服务器拉取消息&#xff0c;RocketMQ …

LeetCode80. 删除有序数组中的重复项 II题解

LeetCode80. 删除有序数组中的重复项 II题解 题目链接&#xff1a; https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii/ 题目描述&#xff1a; 给你一个有序数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使得出现次数超过两次的元素…

基于uniapp的h5接入企业微信客服在线聊天

首先说下企业微信接入场景,支持的接入场景有以下几种,基本上涵盖了微信生态大部分场景: 接入步骤 1.创建企业微信号 按照官方操作步骤注册,需要注意的是未认证仅支持接入100人,已认证支持接入2000人. 2.创建客服账号 每个客服账号支持配置人工或是机器人或是人工机器人回复…

艾尔登法环黄金树幽影/ELDEN RING Shadow of the Erdtree(全DLC)

百度网盘 https://pan.baidu.com/s/1ulKiNQdVtV5dto-Vm7k4IA 提取码&#xff1a;yqrs 游戏介绍 《艾尔登法环》是一款以正统黑暗奇幻世界为舞台的动作RPG游戏。走进辽阔的场景与地下迷宫探索未知&#xff0c;挑战困难重重的险境&#xff0c;享受克服困境时的成就感吧。…

【单元测试】Spring Boot 的测试库

Spring Boot 的测试库 1.了解回归测试框架 JUnit2.了解 assertThat3.了解 Mockito4.了解 JSONPath5.测试的回滚 单元测试&#xff08;unit test&#xff09;是为了检验程序的正确性。一个单元可能是单个 程序、类、对象、方法 等&#xff0c;它是应用程序的最小可测试部件。 单…

如何修复“AI的原罪”

如何修复“AI的原罪” 上个月&#xff0c;《纽约时报》声称&#xff0c;科技巨头OpenAI和谷歌不顾服务条款和版权法的禁止&#xff0c;将大量YouTube视频转录成文本&#xff0c;并将其用作人工智能模型的额外训练数据&#xff0c;从而进入了版权灰色地带。《纽约时报》还援引Me…

HTML(18)——浮动

标准流 标准流也叫文档流&#xff0c;指的是标签在页面中默认的排布规则&#xff0c;例如&#xff1a;块元素独占一行&#xff0c;行内元素可以一行显示多个 浮动 作用&#xff1a;让块级元素水平排列 属性名&#xff1a;float 属性值 left&#xff1a;左对齐right&#…

高通安卓12-ADB操作

1.ADB安装 从网上下载一个adb工具 如&#xff1a; https://dl.google.com/android/repository/platform-tools_r31.0.2-windows.zip 解压出来 增加一个环境变量 查看安装情况 2.设备连接 从网上下载ADB工具 1.进入命令行&#xff0c;查询设备状态 adb devices 2.进入shel…

【硬件开发】共模电感

为什么电源无论直流还是交流的输入端都需要一个共模电感 图中L1就是共模电感&#xff0c;长下面这个样子&#xff0c;两侧的匝数&#xff0c;线径和材料都是一模一样的 共模电感的作用是为了抑制共模信号 抑制共模信号工作原理 http://【共模电感是如何抑制共模信号的】https…

视频讲解|基于模型预测算法的含储能微网双层能量管理模型【mpc】

1 主要内容 该讲解视频对应的免费程序链接为【防骗贴】基于模型预测算法的含储能微网双层能量管理模型&#xff0c;主要做的是一个微网双层优化调度模型&#xff0c;微网聚合单元包括风电、光伏、储能以及超级电容器&#xff0c;在微网的运行成本层面考虑了电池的退化成本&…