Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bdd5915f authored by Marin Shalamanov's avatar Marin Shalamanov
Browse files

SF: Refactor HWComposer::onHotplug()

Currently HWComposer::onHotplug() contains logic that is
executed when hotplug disconnect or connect event occurs.
However reading what exactly is done during e.g. hotplug
connect is hard, because the logic is spread between two
if statements and the function HWComposer::onHotplugConnect().
This change moves all hotplug connect logic to
HWComposer::onHotplugConnect() and all disconnect logic
to a new function HWComposer::onHotplugDisconnect().

Additionally this CLs refactors onHotplugConnect() which
is currently responsible for multiple concerns, which is
making the code hard to understand and reuse.

 - checking if the event should be ignored is moved to
   a new function shouldIgnoreHotplugConnect()
 - creating the necessary state for the new display is
   moved to a new function allocatePhysicalDisplay()

Some of the newly created functions will be directly reused
in following CLs.

Bug: 143451809
Test: m surfaceflinger
Test: atest libsurfaceflinger_unittest
Merged-In: Ib4dae2ce81e8cac176a56b6de787a4f76d01c050
Change-Id: Ib4dae2ce81e8cac176a56b6de787a4f76d01c050
(cherry picked from commit 05f9398f)
parent 2dbf7c97
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ public:

    MOCK_METHOD3(allocateVirtualDisplay,
                 std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*));
    MOCK_METHOD2(allocatePhysicalDisplay, void(hwc2_display_t, DisplayId));
    MOCK_METHOD1(createLayer, HWC2::Layer*(DisplayId));
    MOCK_METHOD2(destroyLayer, void(DisplayId, HWC2::Layer*));
    MOCK_METHOD3(getDeviceCompositionChanges,
+94 −79
Original line number Diff line number Diff line
@@ -205,56 +205,14 @@ bool HWComposer::hasDisplayCapability(const std::optional<DisplayId>& displayId,

std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hwc2_display_t hwcDisplayId,
                                                               HWC2::Connection connection) {
    std::optional<DisplayIdentificationInfo> info;

    if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) {
        info = DisplayIdentificationInfo{.id = *displayId,
                                         .name = std::string(),
                                         .deviceProductInfo = std::nullopt};
    } else {
        if (connection == HWC2::Connection::Disconnected) {
            ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId);
    switch (connection) {
        case HWC2::Connection::Connected:
            return onHotplugConnect(hwcDisplayId);
        case HWC2::Connection::Disconnected:
            return onHotplugDisconnect(hwcDisplayId);
        case HWC2::Connection::Invalid:
            return {};
    }

        info = onHotplugConnect(hwcDisplayId);
        if (!info) return {};
    }

    ALOGV("%s: %s %s display %s with HWC ID %" PRIu64, __FUNCTION__, to_string(connection).c_str(),
          hwcDisplayId == mInternalHwcDisplayId ? "internal" : "external",
          to_string(info->id).c_str(), hwcDisplayId);

    if (connection == HWC2::Connection::Connected) {
        auto& displayData = mDisplayData[info->id];
        // If we get a hotplug connected event for a display we already have,
        // destroy the display and recreate it. This will force us to requery
        // the display params and recreate all layers on that display.
        if (displayData.hwcDisplay != nullptr && displayData.hwcDisplay->isConnected()) {
            ALOGI("Hotplug connecting an already connected display."
                  " Clearing old display state.");
        }
        displayData.hwcDisplay.reset();
        auto newDisplay =
                std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId,
                                                      HWC2::DisplayType::Physical);
        newDisplay->setConnected(true);
        displayData.hwcDisplay = std::move(newDisplay);
        mPhysicalDisplayIdMap[hwcDisplayId] = info->id;
    } else if (connection == HWC2::Connection::Disconnected) {
        // The display will later be destroyed by a call to
        // destroyDisplay(). For now we just mark it disconnected.
        auto& displayData = mDisplayData[info->id];
        if (displayData.hwcDisplay) {
            displayData.hwcDisplay->setConnected(false);
        } else {
            ALOGW("Attempted to disconnect unknown display %" PRIu64, hwcDisplayId);
        }
        // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
        // via SurfaceFlinger's onHotplugReceived callback handling
    }

    return info;
}

bool HWComposer::onVsync(hwc2_display_t hwcDisplayId, int64_t timestamp) {
@@ -337,6 +295,22 @@ std::optional<DisplayId> HWComposer::allocateVirtualDisplay(uint32_t width, uint
    return displayId;
}

void HWComposer::allocatePhysicalDisplay(hwc2_display_t hwcDisplayId, DisplayId displayId) {
    if (!mInternalHwcDisplayId) {
        mInternalHwcDisplayId = hwcDisplayId;
    } else if (mInternalHwcDisplayId != hwcDisplayId && !mExternalHwcDisplayId) {
        mExternalHwcDisplayId = hwcDisplayId;
    }

    auto& displayData = mDisplayData[displayId];
    auto newDisplay =
            std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId,
                                                  HWC2::DisplayType::Physical);
    newDisplay->setConnected(true);
    displayData.hwcDisplay = std::move(newDisplay);
    mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
}

HWC2::Layer* HWComposer::createLayer(DisplayId displayId) {
    RETURN_IF_INVALID_DISPLAY(displayId, nullptr);

@@ -911,52 +885,93 @@ std::optional<hwc2_display_t> HWComposer::fromPhysicalDisplayId(DisplayId displa
    return {};
}

std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect(hwc2_display_t hwcDisplayId) {
bool HWComposer::shouldIgnoreHotplugConnect(hwc2_display_t hwcDisplayId,
                                            bool hasDisplayIdentificationData) const {
    if (isUsingVrComposer() && mInternalHwcDisplayId) {
        ALOGE("Ignoring connection of external display %" PRIu64 " in VR mode", hwcDisplayId);
        return {};
        return true;
    }

    if (mHasMultiDisplaySupport && !hasDisplayIdentificationData) {
        ALOGE("Ignoring connection of display %" PRIu64 " without identification data",
              hwcDisplayId);
        return true;
    }

    if (!mHasMultiDisplaySupport && mInternalHwcDisplayId && mExternalHwcDisplayId) {
        ALOGE("Ignoring connection of tertiary display %" PRIu64, hwcDisplayId);
        return true;
    }

    return false;
}

std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect(hwc2_display_t hwcDisplayId) {
    std::optional<DisplayIdentificationInfo> info;
    if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) {
        info = DisplayIdentificationInfo{.id = *displayId,
                                         .name = std::string(),
                                         .deviceProductInfo = std::nullopt};
    } else {
        uint8_t port;
        DisplayIdentificationData data;
    const bool hasMultiDisplaySupport = getDisplayIdentificationData(hwcDisplayId, &port, &data);

        const bool hasDisplayIdentificationData =
                getDisplayIdentificationData(hwcDisplayId, &port, &data);
        if (mPhysicalDisplayIdMap.empty()) {
        mHasMultiDisplaySupport = hasMultiDisplaySupport;
            mHasMultiDisplaySupport = hasDisplayIdentificationData;
            ALOGI("Switching to %s multi-display mode",
              hasMultiDisplaySupport ? "generalized" : "legacy");
    } else if (mHasMultiDisplaySupport && !hasMultiDisplaySupport) {
        ALOGE("Ignoring connection of display %" PRIu64 " without identification data",
              hwcDisplayId);
        return {};
                  mHasMultiDisplaySupport ? "generalized" : "legacy");
        }

    std::optional<DisplayIdentificationInfo> info;
        if (shouldIgnoreHotplugConnect(hwcDisplayId, hasDisplayIdentificationData)) {
            return {};
        }

        info = [this, hwcDisplayId, &port, &data, hasDisplayIdentificationData] {
            const bool isPrimary = !mInternalHwcDisplayId;
            if (mHasMultiDisplaySupport) {
        info = parseDisplayIdentificationData(port, data);
        ALOGE_IF(!info, "Failed to parse identification data for display %" PRIu64, hwcDisplayId);
    } else if (mInternalHwcDisplayId && mExternalHwcDisplayId) {
        ALOGE("Ignoring connection of tertiary display %" PRIu64, hwcDisplayId);
        return {};
                if (const auto info = parseDisplayIdentificationData(port, data)) {
                    return *info;
                }
                ALOGE("Failed to parse identification data for display %" PRIu64, hwcDisplayId);
            } else {
        ALOGW_IF(hasMultiDisplaySupport, "Ignoring identification data for display %" PRIu64,
                 hwcDisplayId);
        port = mInternalHwcDisplayId ? HWC_DISPLAY_EXTERNAL : HWC_DISPLAY_PRIMARY;
                ALOGW_IF(hasDisplayIdentificationData,
                         "Ignoring identification data for display %" PRIu64, hwcDisplayId);
                port = isPrimary ? HWC_DISPLAY_PRIMARY : HWC_DISPLAY_EXTERNAL;
            }

    if (!mInternalHwcDisplayId) {
        mInternalHwcDisplayId = hwcDisplayId;
    } else if (!mExternalHwcDisplayId) {
        mExternalHwcDisplayId = hwcDisplayId;
            return DisplayIdentificationInfo{.id = getFallbackDisplayId(port),
                                             .name = isPrimary ? "Internal display"
                                                               : "External display",
                                             .deviceProductInfo = std::nullopt};
        }();
    }

    if (info) return info;
    if (!isConnected(info->id)) {
        allocatePhysicalDisplay(hwcDisplayId, info->id);
    }
    return info;
}

    return DisplayIdentificationInfo{.id = getFallbackDisplayId(port),
                                     .name = hwcDisplayId == mInternalHwcDisplayId
                                             ? "Internal display"
                                             : "External display",
std::optional<DisplayIdentificationInfo> HWComposer::onHotplugDisconnect(
        hwc2_display_t hwcDisplayId) {
    const auto displayId = toPhysicalDisplayId(hwcDisplayId);
    if (!displayId) {
        ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId);
        return {};
    }

    // The display will later be destroyed by a call to
    // destroyDisplay(). For now we just mark it disconnected.
    if (isConnected(*displayId)) {
        mDisplayData[*displayId].hwcDisplay->setConnected(false);
    } else {
        ALOGW("Attempted to disconnect unknown display %" PRIu64, hwcDisplayId);
    }
    // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
    // via SurfaceFlinger's onHotplugReceived callback handling
    return DisplayIdentificationInfo{.id = *displayId,
                                     .name = std::string(),
                                     .deviceProductInfo = std::nullopt};
}

+10 −0
Original line number Diff line number Diff line
@@ -77,6 +77,8 @@ public:
    virtual std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
                                                            ui::PixelFormat* format) = 0;

    virtual void allocatePhysicalDisplay(hwc2_display_t hwcDisplayId, DisplayId displayId) = 0;

    // Attempts to create a new layer on this display
    virtual HWC2::Layer* createLayer(DisplayId displayId) = 0;
    // Destroy a previously created layer
@@ -164,6 +166,7 @@ public:

    // Returns stable display ID (and display name on connection of new or previously disconnected
    // display), or std::nullopt if hotplug event was ignored.
    // This function is called from SurfaceFlinger.
    virtual std::optional<DisplayIdentificationInfo> onHotplug(hwc2_display_t hwcDisplayId,
                                                               HWC2::Connection connection) = 0;

@@ -238,6 +241,9 @@ public:
    std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
                                                    ui::PixelFormat* format) override;

    // Called from SurfaceFlinger, when the state for a new physical display needs to be recreated.
    void allocatePhysicalDisplay(hwc2_display_t hwcDisplayId, DisplayId displayId) override;

    // Attempts to create a new layer on this display
    HWC2::Layer* createLayer(DisplayId displayId) override;
    // Destroy a previously created layer
@@ -361,6 +367,10 @@ private:
    friend TestableSurfaceFlinger;

    std::optional<DisplayIdentificationInfo> onHotplugConnect(hwc2_display_t hwcDisplayId);
    std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hwc2_display_t hwcDisplayId);
    bool shouldIgnoreHotplugConnect(hwc2_display_t hwcDisplayId,
                                    bool hasDisplayIdentificationData) const;

    void loadCapabilities();
    void loadLayerMetadataSupport();
    uint32_t getMaxVirtualDisplayCount() const;