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

发布于:2024-06-23 ⋅ 阅读:(201) ⋅ 点赞:(0)

        上一篇文章分析了回调注册监听的调用流程,对于数据的回调正好是注册监听的逆向调用。首先前面提到过在 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 函数向上回调。

原理图 


网站公告

今日签到

点亮在社区的每一天
去签到