Merge "Avoid copying Transaction objects unneccessarily." into main
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index ea5343a..a3499c1 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -277,15 +277,6 @@
         "-fvisibility=hidden",
         "-DBUILDING_LIBBINDER",
     ],
-
-    target: {
-        vendor: {
-            // Trimming the exported symbols reveals a bug in vendor code, so
-            // disable it for the vendor variant for now. http://b/349657329
-            // TODO: Fix the issue and remove this override.
-            cflags: ["-fvisibility=default"],
-        },
-    },
 }
 
 cc_defaults {
diff --git a/libs/binder/rust/rpcbinder/src/server/trusty.rs b/libs/binder/rust/rpcbinder/src/server/trusty.rs
index fe45dec..54d82d5 100644
--- a/libs/binder/rust/rpcbinder/src/server/trusty.rs
+++ b/libs/binder/rust/rpcbinder/src/server/trusty.rs
@@ -106,6 +106,10 @@
     ctx: *mut c_void,
 }
 
+// SAFETY: The opaque handle: `ctx` points into a dynamic allocation,
+// and not tied to anything specific to the current thread.
+unsafe impl Send for RpcServerConnection {}
+
 impl Drop for RpcServerConnection {
     fn drop(&mut self) {
         // We do not need to close handle_fd since we do not own it.
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 6a8a698..771c65b 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -1160,6 +1160,12 @@
             pub const fn enum_values() -> [Self; $size] {
                 [$(Self::$name),*]
             }
+
+            #[inline(always)]
+            #[allow(missing_docs)]
+            pub const fn get(&self) -> $backing {
+                self.0
+            }
         }
 
         impl std::fmt::Debug for $enum {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 359a528..69ba1d7 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2545,6 +2545,7 @@
     if (status.isOk()) {
         // convert gui::StaticDisplayInfo to ui::StaticDisplayInfo
         outInfo->connectionType = static_cast<ui::DisplayConnectionType>(ginfo.connectionType);
+        outInfo->port = ginfo.port;
         outInfo->density = ginfo.density;
         outInfo->secure = ginfo.secure;
         outInfo->installOrientation = static_cast<ui::Rotation>(ginfo.installOrientation);
diff --git a/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl b/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl
index 0ccda56..7ff332c 100644
--- a/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl
+++ b/libs/gui/aidl/android/gui/StaticDisplayInfo.aidl
@@ -23,6 +23,7 @@
 /** @hide */
 parcelable StaticDisplayInfo {
     DisplayConnectionType connectionType = DisplayConnectionType.Internal;
+    int port = -1;
     float density;
     boolean secure;
     @nullable DeviceProductInfo deviceProductInfo;
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index a893b84..ce1bc95 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -130,9 +130,6 @@
   description: "Remove BufferQueueProducer::dequeue's wait on this fence (or the fence entirely) to prevent deadlocks"
   bug: "339705065"
   is_fixed_read_only: true
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
 } # bq_gl_fence_cleanup
 
 flag {
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index cf05fd4..adf8fce 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -222,8 +222,8 @@
         ASSERT_EQ(InputEventType::MOTION, ev->getType());
         MotionEvent* mev = static_cast<MotionEvent*>(ev);
         EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
-        EXPECT_EQ(x, mev->getX(0));
-        EXPECT_EQ(y, mev->getY(0));
+        EXPECT_NEAR(x, mev->getX(0), EPSILON);
+        EXPECT_NEAR(y, mev->getY(0), EPSILON);
         EXPECT_EQ(flags, mev->getFlags() & flags);
 
         ev = consumeEvent();
@@ -241,8 +241,8 @@
         MotionEvent* mev = static_cast<MotionEvent*>(ev);
         EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
         const PointerCoords& coords = *mev->getRawPointerCoords(0 /*pointerIndex*/);
-        EXPECT_EQ(displayX, coords.getX());
-        EXPECT_EQ(displayY, coords.getY());
+        EXPECT_NEAR(displayX, coords.getX(), EPSILON);
+        EXPECT_NEAR(displayY, coords.getY(), EPSILON);
         EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
 
         ev = consumeEvent();
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 7f207f0..f9b84fa 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -25,6 +25,7 @@
     defaults: [
         "android.hardware.graphics.composer3-ndk_shared",
         "renderengine_defaults",
+        "libsurfaceflinger_common_deps",
     ],
     cflags: [
         "-DGL_GLEXT_PROTOTYPES",
@@ -117,7 +118,10 @@
 // possible if libskia_renderengine is just pulled into librenderengine via whole_static_libs.
 cc_defaults {
     name: "librenderengine_deps",
-    defaults: ["skia_renderengine_deps"],
+    defaults: [
+        "skia_renderengine_deps",
+        "libsurfaceflinger_common_deps",
+    ],
     static_libs: ["libskia_renderengine"],
 }
 
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
index f84db0b..2d18ddb 100644
--- a/libs/renderengine/benchmark/Android.bp
+++ b/libs/renderengine/benchmark/Android.bp
@@ -28,6 +28,7 @@
         "android.hardware.graphics.composer3-ndk_shared",
         "librenderengine_deps",
         "surfaceflinger_defaults",
+        "libsurfaceflinger_common_deps",
     ],
     srcs: [
         "main.cpp",
@@ -38,7 +39,6 @@
     static_libs: [
         "librenderengine",
         "libshaders",
-        "libsurfaceflinger_common",
         "libtonemap",
     ],
     cflags: [
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 25afc7b..9e1c226 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -1236,6 +1236,16 @@
     LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
     auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface));
     trace(drawFence);
+    FenceTimePtr fenceTime = FenceTime::makeValid(drawFence);
+    for (const auto& layer : layers) {
+        if (FlagManager::getInstance().monitor_buffer_fences()) {
+            if (layer.source.buffer.buffer) {
+                layer.source.buffer.buffer->getBuffer()
+                        ->getDependencyMonitor()
+                        .addAccessCompletion(fenceTime, "RE");
+            }
+        }
+    }
     resultPromise->set_value(std::move(drawFence));
 }
 
diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp
index f262158..6a577ff 100644
--- a/libs/renderengine/skia/filters/LutShader.cpp
+++ b/libs/renderengine/skia/filters/LutShader.cpp
@@ -24,7 +24,6 @@
 #include <ui/ColorSpace.h>
 
 #include "include/core/SkColorSpace.h"
-#include "src/core/SkColorFilterPriv.h"
 
 using aidl::android::hardware::graphics::composer3::LutProperties;
 
@@ -116,7 +115,7 @@
                 linear = mix(c0, c1, linear.b);
             }
         }
-        return float4(linear, rgba.a);
+        return float4(fromLinearSrgb(linear), rgba.a);
     })");
 
 // same as shader::toColorSpace function
@@ -289,9 +288,7 @@
                                       lutProperties[i].samplingKey, srcDataspace);
         }
 
-        auto colorXformLutToDst =
-                SkColorFilterPriv::MakeColorSpaceXform(lutMathColorSpace, outColorSpace);
-        input = input->makeWithColorFilter(colorXformLutToDst);
+        input = input->makeWithWorkingColorSpace(outColorSpace);
     }
     return input;
 }
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 87e213e..10cb992 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -122,6 +122,7 @@
 
     srcs: [
         "DebugUtils.cpp",
+        "DependencyMonitor.cpp",
         "DeviceProductInfo.cpp",
         "DisplayIdentification.cpp",
         "DynamicDisplayInfo.cpp",
diff --git a/libs/ui/DependencyMonitor.cpp b/libs/ui/DependencyMonitor.cpp
new file mode 100644
index 0000000..b7e490e
--- /dev/null
+++ b/libs/ui/DependencyMonitor.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "DependencyMonitor"
+
+#include <ui/DependencyMonitor.h>
+#include <ui/Fence.h>
+#include <utils/Timers.h>
+
+#include <inttypes.h>
+
+namespace android {
+
+void DependencyMonitor::addIngress(FenceTimePtr fence, std::string annotation) {
+    std::lock_guard lock(mMutex);
+    resolveLocked();
+    if (mDependencies.isFull() && !mDependencies.front().updateSignalTimes(true)) {
+        ALOGD("%s: Clobbering unresolved dependencies -- make me bigger!", mToken.c_str());
+    }
+
+    auto& entry = mDependencies.next();
+    entry.reset(mToken.c_str());
+    ALOGV("%" PRId64 "/%s: addIngress at CPU time %" PRId64 " (%s)", mDependencies.back().id,
+          mToken.c_str(), systemTime(), annotation.c_str());
+
+    mDependencies.back().ingress = {std::move(fence), std::move(annotation)};
+}
+
+void DependencyMonitor::addAccessCompletion(FenceTimePtr fence, std::string annotation) {
+    std::lock_guard lock(mMutex);
+    if (mDependencies.size() == 0) {
+        return;
+    }
+    ALOGV("%" PRId64 "/%s: addAccessCompletion at CPU time %" PRId64 " (%s)",
+          mDependencies.back().id, mToken.c_str(), systemTime(), annotation.c_str());
+    mDependencies.back().accessCompletions.emplace_back(std::move(fence), std::move(annotation));
+}
+
+void DependencyMonitor::addEgress(FenceTimePtr fence, std::string annotation) {
+    std::lock_guard lock(mMutex);
+    if (mDependencies.size() == 0) {
+        return;
+    }
+    ALOGV("%" PRId64 "/%s: addEgress at CPU time %" PRId64 " (%s)", mDependencies.back().id,
+          mToken.c_str(), systemTime(), annotation.c_str());
+    mDependencies.back().egress = {std::move(fence), std::move(annotation)};
+}
+
+void DependencyMonitor::resolveLocked() {
+    if (mDependencies.size() == 0) {
+        return;
+    }
+
+    for (size_t i = mDependencies.size(); i > 0; i--) {
+        auto& dependencyBlock = mDependencies[i - 1];
+
+        if (dependencyBlock.validated) {
+            continue;
+        }
+
+        if (!dependencyBlock.updateSignalTimes(false)) {
+            break;
+        }
+
+        dependencyBlock.validated = true;
+        dependencyBlock.checkUnsafeAccess();
+    }
+}
+
+bool DependencyMonitor::DependencyBlock::updateSignalTimes(bool excludeIngress) {
+    if (egress.fence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+        return false;
+    }
+
+    if (!excludeIngress && ingress.fence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+        return false;
+    }
+
+    for (auto& accessCompletion : accessCompletions) {
+        if (accessCompletion.fence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void DependencyMonitor::DependencyBlock::checkUnsafeAccess() const {
+    const nsecs_t egressTime = egress.fence->getCachedSignalTime();
+    const nsecs_t ingressTime = ingress.fence->getCachedSignalTime();
+
+    ALOGV_IF(egressTime != Fence::SIGNAL_TIME_INVALID,
+             "%" PRId64 "/%s: Egress time: %" PRId64 " (%s)", token, id, egressTime,
+             egress.annotation.c_str());
+    ALOGV_IF(Fence::isValidTimestamp(egressTime) && Fence::isValidTimestamp(ingressTime) &&
+                     egressTime < ingressTime,
+             "%" PRId64 "/%s: Detected egress before ingress!: %" PRId64 " (%s) < %" PRId64 " (%s)",
+             id, token, egressTime, egress.annotation, ingressTime, ingress.annotation.c_str());
+
+    for (auto& accessCompletion : accessCompletions) {
+        const nsecs_t accessCompletionTime = accessCompletion.fence->getCachedSignalTime();
+        if (!Fence::isValidTimestamp(accessCompletionTime)) {
+            ALOGI("%" PRId64 "/%s: Detected invalid access completion! <%s>", id, token,
+                  accessCompletion.annotation.c_str());
+            continue;
+        } else {
+            ALOGV("%" PRId64 "/%s: Access completion time: %" PRId64 " <%s>", id, token,
+                  accessCompletionTime, accessCompletion.annotation.c_str());
+        }
+
+        ALOGI_IF(Fence::isValidTimestamp(egressTime) && accessCompletionTime > egressTime,
+                 "%" PRId64 "/%s: Detected access completion after egress!: %" PRId64
+                 " (%s) > %" PRId64 " (%s)",
+                 id, token, accessCompletionTime, accessCompletion.annotation.c_str(), egressTime,
+                 egress.annotation.c_str());
+
+        ALOGI_IF(Fence::isValidTimestamp(ingressTime) && accessCompletionTime < ingressTime,
+                 "%" PRId64 "/%s: Detected access completion prior to ingress!: %" PRId64
+                 " (%s) < %" PRId64 " (%s)",
+                 id, token, accessCompletionTime, accessCompletion.annotation.c_str(), ingressTime,
+                 ingress.annotation.c_str());
+    }
+
+    ALOGV_IF(ingressTime != Fence::SIGNAL_TIME_INVALID,
+             "%" PRId64 "/%s: Ingress time: %" PRId64 " (%s)", id, token, ingressTime,
+             ingress.annotation.c_str());
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
index 4246c40..81afe9e 100644
--- a/libs/ui/FenceTime.cpp
+++ b/libs/ui/FenceTime.cpp
@@ -59,6 +59,14 @@
     }
 }
 
+FenceTimePtr FenceTime::makeValid(const sp<Fence>& fence) {
+    if (fence && fence->isValid()) {
+        return std::make_shared<FenceTime>(fence);
+    } else {
+        return std::make_shared<FenceTime>(systemTime());
+    }
+}
+
 void FenceTime::applyTrustedSnapshot(const Snapshot& src) {
     if (CC_UNLIKELY(src.state != Snapshot::State::SIGNAL_TIME)) {
         // Applying Snapshot::State::FENCE, could change the valid state of the
@@ -289,9 +297,10 @@
 // ============================================================================
 void FenceTimeline::push(const std::shared_ptr<FenceTime>& fence) {
     std::lock_guard<std::mutex> lock(mMutex);
-    while (mQueue.size() >= MAX_ENTRIES) {
+    static constexpr size_t MAX_QUEUE_SIZE = 64;
+    while (mQueue.size() >= MAX_QUEUE_SIZE) {
         // This is a sanity check to make sure the queue doesn't grow unbounded.
-        // MAX_ENTRIES should be big enough not to trigger this path.
+        // MAX_QUEUE_SIZE should be big enough not to trigger this path.
         // In case this path is taken though, users of FenceTime must make sure
         // not to rely solely on FenceTimeline to get the final timestamp and
         // should eventually call Fence::getSignalTime on their own.
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 18c9a6b..f7c9400 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -27,6 +27,8 @@
 #include <ui/GraphicBufferMapper.h>
 #include <utils/Trace.h>
 
+#include <string>
+
 namespace android {
 
 // ===========================================================================
@@ -104,6 +106,7 @@
     usage  = 0;
     layerCount = 0;
     handle = nullptr;
+    mDependencyMonitor.setToken(std::to_string(mId));
 }
 
 // deprecated
@@ -155,6 +158,8 @@
         layerCount = request.layerCount;
         usage = request.usage;
         usage_deprecated = int(usage);
+        std::string name = request.requestorName;
+        mDependencyMonitor.setToken(name.append(":").append(std::to_string(mId)));
     }
 }
 
@@ -252,6 +257,7 @@
         usage = inUsage;
         usage_deprecated = int(usage);
         stride = static_cast<int>(outStride);
+        mDependencyMonitor.setToken(requestorName.append(":").append(std::to_string(mId)));
     }
     return err;
 }
@@ -609,6 +615,14 @@
         mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);
     }
 
+    std::string name;
+    status_t err = mBufferMapper.getName(handle, &name);
+    if (err != NO_ERROR) {
+        name = "<Unknown>";
+    }
+
+    mDependencyMonitor.setToken(name.append(":").append(std::to_string(mId)));
+
     buffer = static_cast<void const*>(static_cast<uint8_t const*>(buffer) + sizeNeeded);
     size -= sizeNeeded;
     fds += numFds;
diff --git a/libs/ui/include/ui/DependencyMonitor.h b/libs/ui/include/ui/DependencyMonitor.h
new file mode 100644
index 0000000..5ad1fd9
--- /dev/null
+++ b/libs/ui/include/ui/DependencyMonitor.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ui/FatVector.h>
+#include <ui/FenceTime.h>
+#include <ui/RingBuffer.h>
+
+namespace android {
+
+// Debugging class for that tries to add userspace logging for fence depencencies.
+// The model that a DependencyMonitor tries to follow is, for each access of some resource:
+// 1. There is a single ingress fence, that guards whether a resource is now safe to read from
+// another system.
+// 2. There are multiple access fences, that are fired when a resource is read.
+// 3. There is a single egress fence, that is fired when a resource is released and sent to another
+// system.
+//
+// Note that there can be repeated ingress and egress of a resource, but the assumption is that
+// there is exactly one egress for every ingress, unless the resource is destroyed rather than
+// released.
+//
+// The DependencyMonitor will log if there is an anomaly in the fences tracked for some resource.
+// This includes:
+// * If (2) happens before (1)
+// * If (2) happens after (3)
+//
+// Note that this class has no knowledge of the "other system". I.e., if the other system ignores
+// the fence reported in (3), but still takes a long time to write to the resource and produce (1),
+// then nothing will be logged. That other system must have its own DependencyMonitor. Conversely,
+// this class has imperfect knowledge of the system it is monitoring. For example, this class does
+// not know the precise start times of reading from a resource, the exact time that a read might
+// occur from a hardware unit is not known to userspace.
+//
+// In other words, this class logs specific classes of fence violations, but is not sensitive to
+// *all* violations. One property of this is that unless the system tracked by a DependencyMonitor
+// is feeding in literally incorrect fences, then there is no chance of a false positive.
+//
+// This class is thread safe.
+class DependencyMonitor {
+public:
+    // Sets a debug token identifying the resource this monitor is tracking.
+    void setToken(std::string token) { mToken = std::move(token); }
+
+    // Adds a fence that is fired when the resource ready to be ingested by the system using the
+    // DependencyMonitor.
+    void addIngress(FenceTimePtr fence, std::string annotation);
+    // Adds a fence that is fired when the resource is accessed.
+    void addAccessCompletion(FenceTimePtr fence, std::string annotation);
+    // Adds a fence that is fired when the resource is released to another system.
+    void addEgress(FenceTimePtr fence, std::string annotation);
+
+private:
+    struct AnnotatedFenceTime {
+        FenceTimePtr fence;
+        std::string annotation;
+    };
+
+    struct DependencyBlock {
+        int64_t id = -1;
+        AnnotatedFenceTime ingress = {FenceTime::NO_FENCE, ""};
+        FatVector<AnnotatedFenceTime> accessCompletions;
+        AnnotatedFenceTime egress = {FenceTime::NO_FENCE, ""};
+        bool validated = false;
+        const char* token = nullptr;
+
+        void reset(const char* newToken) {
+            static std::atomic<int64_t> counter = 0;
+            id = counter++;
+            ingress = {FenceTime::NO_FENCE, ""};
+            accessCompletions.clear();
+            egress = {FenceTime::NO_FENCE, ""};
+            validated = false;
+            token = newToken;
+        }
+
+        // Returns true if all fences in this block have valid signal times.
+        bool updateSignalTimes(bool excludeIngress);
+
+        void checkUnsafeAccess() const;
+    };
+
+    void resolveLocked() REQUIRES(mMutex);
+
+    std::string mToken;
+    std::mutex mMutex;
+    ui::RingBuffer<DependencyBlock, 10> mDependencies GUARDED_BY(mMutex);
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index 937e3f1..1e1c77b 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -20,6 +20,7 @@
 #include <ostream>
 #include <string>
 
+#include <ftl/match.h>
 #include <ftl/optional.h>
 
 namespace android {
@@ -36,7 +37,6 @@
     DisplayId& operator=(const DisplayId&) = default;
 
     static constexpr DisplayId fromValue(uint64_t value) { return DisplayId(value); }
-    constexpr bool isVirtual() const { return value & FLAG_VIRTUAL; }
 
     uint64_t value;
 
@@ -66,13 +66,6 @@
     // TODO: b/162612135 - Remove default constructor.
     PhysicalDisplayId() = default;
 
-    static constexpr ftl::Optional<PhysicalDisplayId> tryCast(DisplayId id) {
-        if (id.isVirtual()) {
-            return std::nullopt;
-        }
-        return PhysicalDisplayId(id);
-    }
-
     // Returns a stable ID based on EDID and port information.
     static constexpr PhysicalDisplayId fromEdid(uint8_t port, uint16_t manufacturerId,
                                                 uint32_t modelHash) {
@@ -90,8 +83,6 @@
         return PhysicalDisplayId(value);
     }
 
-    constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); }
-
 private:
     // Flag indicating that the ID is stable across reboots.
     static constexpr uint64_t FLAG_STABLE = 1ULL << 62;
@@ -112,13 +103,6 @@
     // Flag indicating that this virtual display is backed by the GPU.
     static constexpr uint64_t FLAG_GPU = 1ULL << 61;
 
-    static constexpr std::optional<VirtualDisplayId> tryCast(DisplayId id) {
-        if (id.isVirtual()) {
-            return VirtualDisplayId(id);
-        }
-        return std::nullopt;
-    }
-
     static constexpr VirtualDisplayId fromValue(uint64_t value) {
         return VirtualDisplayId(SkipVirtualFlag{}, value);
     }
@@ -134,13 +118,6 @@
 struct HalVirtualDisplayId : VirtualDisplayId {
     explicit constexpr HalVirtualDisplayId(BaseId baseId) : VirtualDisplayId(baseId) {}
 
-    static constexpr std::optional<HalVirtualDisplayId> tryCast(DisplayId id) {
-        if (id.isVirtual() && !(id.value & FLAG_GPU)) {
-            return HalVirtualDisplayId(id);
-        }
-        return std::nullopt;
-    }
-
     static constexpr HalVirtualDisplayId fromValue(uint64_t value) {
         return HalVirtualDisplayId(SkipVirtualFlag{}, value);
     }
@@ -152,13 +129,6 @@
 struct GpuVirtualDisplayId : VirtualDisplayId {
     explicit constexpr GpuVirtualDisplayId(BaseId baseId) : VirtualDisplayId(FLAG_GPU | baseId) {}
 
-    static constexpr std::optional<GpuVirtualDisplayId> tryCast(DisplayId id) {
-        if (id.isVirtual() && (id.value & FLAG_GPU)) {
-            return GpuVirtualDisplayId(id);
-        }
-        return std::nullopt;
-    }
-
     static constexpr GpuVirtualDisplayId fromValue(uint64_t value) {
         return GpuVirtualDisplayId(SkipVirtualFlag{}, value);
     }
@@ -172,14 +142,6 @@
 struct HalDisplayId : DisplayId {
     constexpr HalDisplayId(HalVirtualDisplayId other) : DisplayId(other) {}
     constexpr HalDisplayId(PhysicalDisplayId other) : DisplayId(other) {}
-
-    static constexpr std::optional<HalDisplayId> tryCast(DisplayId id) {
-        if (GpuVirtualDisplayId::tryCast(id)) {
-            return std::nullopt;
-        }
-        return HalDisplayId(id);
-    }
-
     static constexpr HalDisplayId fromValue(uint64_t value) { return HalDisplayId(value); }
 
 private:
@@ -187,6 +149,47 @@
     explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {}
 };
 
+using DisplayIdVariant = std::variant<PhysicalDisplayId, GpuVirtualDisplayId, HalVirtualDisplayId>;
+using VirtualDisplayIdVariant = std::variant<GpuVirtualDisplayId, HalVirtualDisplayId>;
+
+template <typename DisplayIdType>
+inline auto asDisplayIdOfType(DisplayIdVariant variant) -> ftl::Optional<DisplayIdType> {
+    return ftl::match(
+            variant,
+            [](DisplayIdType id) -> ftl::Optional<DisplayIdType> { return ftl::Optional(id); },
+            [](auto) -> ftl::Optional<DisplayIdType> { return std::nullopt; });
+}
+
+template <typename Variant>
+inline auto asHalDisplayId(Variant variant) -> ftl::Optional<HalDisplayId> {
+    return ftl::match(
+            variant,
+            [](GpuVirtualDisplayId) -> ftl::Optional<HalDisplayId> { return std::nullopt; },
+            [](auto id) -> ftl::Optional<HalDisplayId> {
+                return ftl::Optional(static_cast<HalDisplayId>(id));
+            });
+}
+
+inline auto asPhysicalDisplayId(DisplayIdVariant variant) -> ftl::Optional<PhysicalDisplayId> {
+    return asDisplayIdOfType<PhysicalDisplayId>(variant);
+}
+
+inline auto asVirtualDisplayId(DisplayIdVariant variant) -> ftl::Optional<VirtualDisplayId> {
+    return ftl::match(
+            variant,
+            [](GpuVirtualDisplayId id) -> ftl::Optional<VirtualDisplayId> {
+                return ftl::Optional(static_cast<VirtualDisplayId>(id));
+            },
+            [](HalVirtualDisplayId id) -> ftl::Optional<VirtualDisplayId> {
+                return ftl::Optional(static_cast<VirtualDisplayId>(id));
+            },
+            [](auto) -> ftl::Optional<VirtualDisplayId> { return std::nullopt; });
+}
+
+inline auto asDisplayId(DisplayIdVariant variant) -> DisplayId {
+    return ftl::match(variant, [](auto id) -> DisplayId { return static_cast<DisplayId>(id); });
+}
+
 static_assert(sizeof(DisplayId) == sizeof(uint64_t));
 static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
 static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t));
diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h
index 334106f..3560d57 100644
--- a/libs/ui/include/ui/FenceTime.h
+++ b/libs/ui/include/ui/FenceTime.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_FENCE_TIME_H
 #define ANDROID_FENCE_TIME_H
 
+#include <stddef.h>
 #include <ui/Fence.h>
 #include <utils/Flattenable.h>
 #include <utils/Mutex.h>
@@ -30,6 +31,8 @@
 namespace android {
 
 class FenceToFenceTimeMap;
+class FenceTime;
+using FenceTimePtr = std::shared_ptr<FenceTime>;
 
 // A wrapper around fence that only implements isValid and getSignalTime.
 // It automatically closes the fence in a thread-safe manner once the signal
@@ -95,6 +98,10 @@
     FenceTime& operator=(const FenceTime&) = delete;
     FenceTime& operator=(FenceTime&&) = delete;
 
+    // Constructs a FenceTime, falling back to a timestamp if the fence is
+    // invalid.
+    static FenceTimePtr makeValid(const sp<Fence>& fence);
+
     // This method should only be called when replacing the fence with
     // a signalTime. Since this is an indirect way of setting the signal time
     // of a fence, the snapshot should come from a trusted source.
@@ -142,8 +149,6 @@
     std::atomic<nsecs_t> mSignalTime{Fence::SIGNAL_TIME_INVALID};
 };
 
-using FenceTimePtr = std::shared_ptr<FenceTime>;
-
 // A queue of FenceTimes that are expected to signal in FIFO order.
 // Only maintains a queue of weak pointers so it doesn't keep references
 // to Fences on its own.
@@ -162,8 +167,6 @@
 // different threads.
 class FenceTimeline {
 public:
-    static constexpr size_t MAX_ENTRIES = 64;
-
     void push(const std::shared_ptr<FenceTime>& fence);
     void updateSignalTimes();
 
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index 936bf8f..9305180 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -23,6 +23,7 @@
 #include <string>
 #include <utility>
 #include <vector>
+#include "ui/DependencyMonitor.h"
 
 #include <android/hardware_buffer.h>
 #include <ui/ANativeObjectBase.h>
@@ -229,6 +230,8 @@
 
     void addDeathCallback(GraphicBufferDeathCallback deathCallback, void* context);
 
+    DependencyMonitor& getDependencyMonitor() { return mDependencyMonitor; }
+
 private:
     ~GraphicBuffer();
 
@@ -295,6 +298,8 @@
     // and informs SurfaceFlinger that it should drop its strong pointer reference to the buffer.
     std::vector<std::pair<GraphicBufferDeathCallback, void* /*mDeathCallbackContext*/>>
             mDeathCallbacks;
+
+    DependencyMonitor mDependencyMonitor;
 };
 
 } // namespace android
diff --git a/libs/ui/include/ui/StaticDisplayInfo.h b/libs/ui/include/ui/StaticDisplayInfo.h
index 83da821..5316448 100644
--- a/libs/ui/include/ui/StaticDisplayInfo.h
+++ b/libs/ui/include/ui/StaticDisplayInfo.h
@@ -28,6 +28,7 @@
 // Immutable information about physical display.
 struct StaticDisplayInfo {
     DisplayConnectionType connectionType = DisplayConnectionType::Internal;
+    uint8_t port;
     float density = 0.f;
     bool secure = false;
     std::optional<DeviceProductInfo> deviceProductInfo;
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 2b11786..d950f2a 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -45,16 +45,6 @@
 }
 
 cc_test {
-    name: "DisplayId_test",
-    shared_libs: ["libui"],
-    srcs: ["DisplayId_test.cpp"],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-}
-
-cc_test {
     name: "DisplayIdentification_test",
     shared_libs: ["libui"],
     static_libs: ["libgmock"],
diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp
deleted file mode 100644
index 209acba..0000000
--- a/libs/ui/tests/DisplayId_test.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <ui/DisplayId.h>
-
-#include <gtest/gtest.h>
-
-namespace android::ui {
-
-TEST(DisplayIdTest, createPhysicalIdFromEdid) {
-    constexpr uint8_t port = 1;
-    constexpr uint16_t manufacturerId = 13;
-    constexpr uint32_t modelHash = 42;
-    const PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash);
-    EXPECT_EQ(port, id.getPort());
-    EXPECT_FALSE(VirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
-    EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
-    EXPECT_TRUE(HalDisplayId::tryCast(id));
-
-    EXPECT_EQ(id, DisplayId::fromValue(id.value));
-    EXPECT_EQ(id, PhysicalDisplayId::fromValue(id.value));
-}
-
-TEST(DisplayIdTest, createPhysicalIdFromPort) {
-    constexpr uint8_t port = 3;
-    const PhysicalDisplayId id = PhysicalDisplayId::fromPort(port);
-    EXPECT_EQ(port, id.getPort());
-    EXPECT_FALSE(VirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
-    EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
-    EXPECT_TRUE(HalDisplayId::tryCast(id));
-
-    EXPECT_EQ(id, DisplayId::fromValue(id.value));
-    EXPECT_EQ(id, PhysicalDisplayId::fromValue(id.value));
-}
-
-TEST(DisplayIdTest, createGpuVirtualId) {
-    const GpuVirtualDisplayId id(42);
-    EXPECT_TRUE(VirtualDisplayId::tryCast(id));
-    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
-    EXPECT_FALSE(HalDisplayId::tryCast(id));
-
-    EXPECT_EQ(id, DisplayId::fromValue(id.value));
-    EXPECT_EQ(id, GpuVirtualDisplayId::fromValue(id.value));
-}
-
-TEST(DisplayIdTest, createVirtualIdFromGpuVirtualId) {
-    const VirtualDisplayId id(GpuVirtualDisplayId(42));
-    EXPECT_TRUE(VirtualDisplayId::tryCast(id));
-    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
-    EXPECT_FALSE(HalDisplayId::tryCast(id));
-
-    const bool isGpuVirtualId = (id.value & VirtualDisplayId::FLAG_GPU);
-    EXPECT_EQ((id.isVirtual() && isGpuVirtualId), GpuVirtualDisplayId::tryCast(id).has_value());
-}
-
-TEST(DisplayIdTest, createHalVirtualId) {
-    const HalVirtualDisplayId id(42);
-    EXPECT_TRUE(VirtualDisplayId::tryCast(id));
-    EXPECT_TRUE(HalVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
-    EXPECT_TRUE(HalDisplayId::tryCast(id));
-
-    EXPECT_EQ(id, DisplayId::fromValue(id.value));
-    EXPECT_EQ(id, HalVirtualDisplayId::fromValue(id.value));
-}
-
-TEST(DisplayIdTest, createVirtualIdFromHalVirtualId) {
-    const VirtualDisplayId id(HalVirtualDisplayId(42));
-    EXPECT_TRUE(VirtualDisplayId::tryCast(id));
-    EXPECT_TRUE(HalVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
-    EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
-    EXPECT_TRUE(HalDisplayId::tryCast(id));
-
-    const bool isGpuVirtualId = (id.value & VirtualDisplayId::FLAG_GPU);
-    EXPECT_EQ((id.isVirtual() && !isGpuVirtualId), HalVirtualDisplayId::tryCast(id).has_value());
-}
-
-} // namespace android::ui
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 888fcfb..95e1c06 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -934,6 +934,7 @@
         mPendingEvent(nullptr),
         mLastDropReason(DropReason::NOT_DROPPED),
         mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
+        mWindowInfosVsyncId(-1),
         mMinTimeBetweenUserActivityPokes(DEFAULT_USER_ACTIVITY_POKE_INTERVAL),
         mConnectionManager(mLooper),
         mTouchStates(mWindowInfos, mConnectionManager),
@@ -7049,11 +7050,6 @@
             setInputWindowsLocked(handles, displayId);
         }
 
-        if (update.vsyncId < mWindowInfosVsyncId) {
-            ALOGE("Received out of order window infos update. Last update vsync id: %" PRId64
-                  ", current update vsync id: %" PRId64,
-                  mWindowInfosVsyncId, update.vsyncId);
-        }
         mWindowInfosVsyncId = update.vsyncId;
     }
     // Wake up poll loop since it may need to make new input dispatching choices.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 252adaa..2c0a66f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -34,7 +34,7 @@
  * A parameter object for creating Display instances
  */
 struct DisplayCreationArgs {
-    DisplayId id;
+    DisplayIdVariant idVariant;
 
     // Size of the display in pixels
     ui::Size pixels = ui::kInvalidSize;
@@ -68,8 +68,8 @@
 public:
     DisplayCreationArgs build() { return std::move(mArgs); }
 
-    DisplayCreationArgsBuilder& setId(DisplayId id) {
-        mArgs.id = id;
+    DisplayCreationArgsBuilder& setId(DisplayIdVariant idVariant) {
+        mArgs.idVariant = idVariant;
         return *this;
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 780758e..e2ea0f1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -167,6 +167,8 @@
     // Checks if the buffer's release fence has been set
     virtual LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() = 0;
 
+    virtual void setReleasedBuffer(sp<GraphicBuffer> buffer) = 0;
+
     // Indicates that the picture profile request was applied to this layer.
     virtual void onPictureProfileCommitted() = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index bda7856..4266da4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -170,6 +170,7 @@
 
     // Returns the DisplayId the output represents, if it has one
     virtual ftl::Optional<DisplayId> getDisplayId() const = 0;
+    virtual ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const = 0;
 
     // Enables (or disables) composition on this output
     virtual void setCompositionEnabled(bool) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 5519aaf..ec87acc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -46,6 +46,7 @@
 
     // compositionengine::Output overrides
     ftl::Optional<DisplayId> getDisplayId() const override;
+    ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const override;
     bool isValid() const override;
     void dump(std::string&) const override;
     using compositionengine::impl::Output::setReleasedLayers;
@@ -104,8 +105,11 @@
             override;
     bool hasPictureProcessing() const override;
     int32_t getMaxLayerPictureProfiles() const override;
+    bool isGpuVirtualDisplay() const {
+        return std::holds_alternative<GpuVirtualDisplayId>(mIdVariant);
+    }
 
-    DisplayId mId;
+    DisplayIdVariant mIdVariant;
     bool mIsDisconnected = false;
     adpf::PowerAdvisor* mPowerAdvisor = nullptr;
     bool mHasPictureProcessing = false;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 0ccdd22..873764b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -45,6 +45,7 @@
     // compositionengine::Output overrides
     bool isValid() const override;
     ftl::Optional<DisplayId> getDisplayId() const override;
+    ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const override;
     void setCompositionEnabled(bool) override;
     void setLayerCachingEnabled(bool) override;
     void setLayerCachingTexturePoolEnabled(bool) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index d2a5a20..f65a908 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -52,6 +52,7 @@
 
     MOCK_METHOD0(createReleaseFenceFuture, ftl::Future<FenceResult>());
     MOCK_METHOD1(setReleaseFence, void(const FenceResult&));
+    MOCK_METHOD1(setReleasedBuffer, void(sp<GraphicBuffer>));
     MOCK_METHOD0(getReleaseFencePromiseStatus, LayerFE::ReleaseFencePromiseStatus());
     MOCK_CONST_METHOD0(getDebugName, const char*());
     MOCK_CONST_METHOD0(getSequence, int32_t());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index f2c265a..eaa3dd3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -35,6 +35,7 @@
 
     MOCK_CONST_METHOD0(isValid, bool());
     MOCK_CONST_METHOD0(getDisplayId, ftl::Optional<DisplayId>());
+    MOCK_CONST_METHOD0(getDisplayIdVariant, ftl::Optional<DisplayIdVariant>());
 
     MOCK_METHOD1(setCompositionEnabled, void(bool));
     MOCK_METHOD1(setLayerCachingEnabled, void(bool));
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 34ede33..ab2a03c 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -97,7 +97,7 @@
 
     ui::PhysicalDisplayVector<compositionengine::Output*> outputsToOffload;
     for (const auto& output : outputs) {
-        if (!ftl::Optional(output->getDisplayId()).and_then(HalDisplayId::tryCast)) {
+        if (!output->getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) {
             // Not HWC-enabled, so it is always client-composited. No need to offload.
             continue;
         }
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 8364f4e..5a54677 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -52,7 +52,7 @@
 Display::~Display() = default;
 
 void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
-    mId = args.id;
+    mIdVariant = args.idVariant;
     mPowerAdvisor = args.powerAdvisor;
     mHasPictureProcessing = args.hasPictureProcessing;
     mMaxLayerPictureProfiles = args.maxLayerPictureProfiles;
@@ -67,7 +67,7 @@
 }
 
 DisplayId Display::getId() const {
-    return mId;
+    return asDisplayId(mIdVariant);
 }
 
 bool Display::isSecure() const {
@@ -79,11 +79,15 @@
 }
 
 bool Display::isVirtual() const {
-    return mId.isVirtual();
+    return !std::holds_alternative<PhysicalDisplayId>(mIdVariant);
 }
 
 ftl::Optional<DisplayId> Display::getDisplayId() const {
-    return mId;
+    return getId();
+}
+
+ftl::Optional<DisplayIdVariant> Display::getDisplayIdVariant() const {
+    return mIdVariant;
 }
 
 void Display::disconnect() {
@@ -93,14 +97,14 @@
 
     mIsDisconnected = true;
 
-    if (const auto id = HalDisplayId::tryCast(mId)) {
+    if (const auto id = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) {
         getCompositionEngine().getHwComposer().disconnectDisplay(*id);
     }
 }
 
 void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
     Output::setColorTransform(args);
-    const auto halDisplayId = HalDisplayId::tryCast(mId);
+    const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
     if (mIsDisconnected || !halDisplayId || CC_LIKELY(!args.colorTransformMatrix)) {
         return;
     }
@@ -108,7 +112,7 @@
     auto& hwc = getCompositionEngine().getHwComposer();
     status_t result = hwc.setColorTransform(*halDisplayId, *args.colorTransformMatrix);
     ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d",
-             to_string(mId).c_str(), result);
+             to_string(*halDisplayId).c_str(), result);
 }
 
 void Display::setColorProfile(const ColorProfile& colorProfile) {
@@ -125,7 +129,7 @@
 
     Output::setColorProfile(colorProfile);
 
-    const auto physicalId = PhysicalDisplayId::tryCast(mId);
+    const auto physicalId = getDisplayIdVariant().and_then(asPhysicalDisplayId);
     LOG_FATAL_IF(!physicalId);
     getCompositionEngine().getHwComposer().setActiveColorMode(*physicalId, colorProfile.mode,
                                                               colorProfile.renderIntent);
@@ -133,7 +137,7 @@
 
 void Display::dump(std::string& out) const {
     const char* const type = isVirtual() ? "virtual" : "physical";
-    base::StringAppendF(&out, "Display %s (%s, \"%s\")", to_string(mId).c_str(), type,
+    base::StringAppendF(&out, "Display %s (%s, \"%s\")", to_string(getId()).c_str(), type,
                         getName().c_str());
 
     out.append("\n   Composition Display State:\n");
@@ -157,7 +161,7 @@
         const sp<compositionengine::LayerFE>& layerFE) const {
     auto outputLayer = impl::createOutputLayer(*this, layerFE);
 
-    if (const auto halDisplayId = HalDisplayId::tryCast(mId);
+    if (const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
         outputLayer && !mIsDisconnected && halDisplayId) {
         auto& hwc = getCompositionEngine().getHwComposer();
         auto hwcLayer = hwc.createLayer(*halDisplayId);
@@ -171,8 +175,7 @@
 void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& refreshArgs) {
     Output::setReleasedLayers(refreshArgs);
 
-    if (mIsDisconnected || GpuVirtualDisplayId::tryCast(mId) ||
-        refreshArgs.layersWithQueuedFrames.empty()) {
+    if (mIsDisconnected || isGpuVirtualDisplay() || refreshArgs.layersWithQueuedFrames.empty()) {
         return;
     }
 
@@ -208,7 +211,7 @@
     if (!getState().displayBrightness) {
         return;
     }
-    if (auto displayId = PhysicalDisplayId::tryCast(mId)) {
+    if (auto displayId = getDisplayIdVariant().and_then(asPhysicalDisplayId)) {
         auto& hwc = getCompositionEngine().getHwComposer();
         status_t result = hwc.setDisplayBrightness(*displayId, *getState().displayBrightness,
                                                    getState().displayBrightnessNits,
@@ -226,7 +229,7 @@
     Output::beginFrame();
 
     // If we don't have a HWC display, then we are done.
-    const auto halDisplayId = HalDisplayId::tryCast(mId);
+    const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
     if (!halDisplayId) {
         return;
     }
@@ -244,7 +247,7 @@
     }
 
     // If we don't have a HWC display, then we are done.
-    const auto halDisplayId = HalDisplayId::tryCast(mId);
+    const auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
     if (!halDisplayId) {
         return false;
     }
@@ -266,9 +269,9 @@
     }
 
     if (isPowerHintSessionEnabled()) {
-        mPowerAdvisor->setHwcValidateTiming(mId, hwcValidateStartTime, TimePoint::now());
-        if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
-            mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId));
+        mPowerAdvisor->setHwcValidateTiming(getId(), hwcValidateStartTime, TimePoint::now());
+        if (auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) {
+            mPowerAdvisor->setSkippedValidate(*halDisplayId, hwc.getValidateSkipped(*halDisplayId));
         }
     }
 
@@ -292,7 +295,7 @@
 
 bool Display::getSkipColorTransform() const {
     auto& hwc = getCompositionEngine().getHwComposer();
-    if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+    if (auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) {
         return hwc.hasDisplayCapability(*halDisplayId,
                                         DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
     }
@@ -383,7 +386,7 @@
 }
 
 void Display::executeCommands() {
-    const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
+    const auto halDisplayIdOpt = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
     if (mIsDisconnected || !halDisplayIdOpt) {
         return;
     }
@@ -394,7 +397,7 @@
 compositionengine::Output::FrameFences Display::presentFrame() {
     auto fences = impl::Output::presentFrame();
 
-    const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
+    const auto halDisplayIdOpt = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>);
     if (mIsDisconnected || !halDisplayIdOpt) {
         return fences;
     }
@@ -404,13 +407,13 @@
     const TimePoint startTime = TimePoint::now();
 
     if (isPowerHintSessionEnabled() && getState().earliestPresentTime) {
-        mPowerAdvisor->setHwcPresentDelayedTime(mId, *getState().earliestPresentTime);
+        mPowerAdvisor->setHwcPresentDelayedTime(*halDisplayIdOpt, *getState().earliestPresentTime);
     }
 
     hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime);
 
     if (isPowerHintSessionEnabled()) {
-        mPowerAdvisor->setHwcPresentTiming(mId, startTime, TimePoint::now());
+        mPowerAdvisor->setHwcPresentTiming(*halDisplayIdOpt, startTime, TimePoint::now());
     }
 
     fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
@@ -433,8 +436,8 @@
 void Display::setExpensiveRenderingExpected(bool enabled) {
     Output::setExpensiveRenderingExpected(enabled);
 
-    if (mPowerAdvisor && !GpuVirtualDisplayId::tryCast(mId)) {
-        mPowerAdvisor->setExpensiveRenderingExpected(mId, enabled);
+    if (mPowerAdvisor && !isGpuVirtualDisplay()) {
+        mPowerAdvisor->setExpensiveRenderingExpected(getId(), enabled);
     }
 }
 
@@ -449,15 +452,15 @@
 // For ADPF GPU v0 this is expected to set start time to when the GPU commands are submitted with
 // fence returned, i.e. when RenderEngine flushes the commands and returns the draw fence.
 void Display::setHintSessionGpuStart(TimePoint startTime) {
-    mPowerAdvisor->setGpuStartTime(mId, startTime);
+    mPowerAdvisor->setGpuStartTime(getId(), startTime);
 }
 
 void Display::setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) {
-    mPowerAdvisor->setGpuFenceTime(mId, std::move(gpuFence));
+    mPowerAdvisor->setGpuFenceTime(getId(), std::move(gpuFence));
 }
 
 void Display::setHintSessionRequiresRenderEngine(bool requiresRenderEngine) {
-    mPowerAdvisor->setRequiresRenderEngine(mId, requiresRenderEngine);
+    mPowerAdvisor->setRequiresRenderEngine(getId(), requiresRenderEngine);
 }
 
 const aidl::android::hardware::graphics::composer3::OverlayProperties*
@@ -478,7 +481,7 @@
     // 1) It is being handled by hardware composer, which may need this to
     //    keep its virtual display state machine in sync, or
     // 2) There is work to be done (the dirty region isn't empty)
-    if (GpuVirtualDisplayId::tryCast(mId) && !mustRecompose()) {
+    if (isGpuVirtualDisplay() && !mustRecompose()) {
         ALOGV("Skipping display composition");
         return;
     }
@@ -487,7 +490,7 @@
 }
 
 bool Display::supportsOffloadPresent() const {
-    if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+    if (auto halDisplayId = getDisplayIdVariant().and_then(asHalDisplayId<DisplayIdVariant>)) {
         auto& hwc = getCompositionEngine().getHwComposer();
         return hwc.hasDisplayCapability(*halDisplayId, DisplayCapability::MULTI_THREADED_PRESENT);
     }
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index b30cf20..00a61a5 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -118,6 +118,10 @@
     return {};
 }
 
+ftl::Optional<DisplayIdVariant> Output::getDisplayIdVariant() const {
+    return {};
+}
+
 const std::string& Output::getName() const {
     return mName;
 }
@@ -436,8 +440,8 @@
 ftl::Future<std::monostate> Output::present(
         const compositionengine::CompositionRefreshArgs& refreshArgs) {
     const auto stringifyExpectedPresentTime = [this, &refreshArgs]() -> std::string {
-        return getDisplayId()
-                .and_then(PhysicalDisplayId::tryCast)
+        return getDisplayIdVariant()
+                .and_then(asPhysicalDisplayId)
                 .and_then([&refreshArgs](PhysicalDisplayId id) {
                     return refreshArgs.frameTargets.get(id);
                 })
@@ -890,8 +894,8 @@
         return;
     }
 
-    if (auto frameTargetPtrOpt = getDisplayId()
-                                         .and_then(PhysicalDisplayId::tryCast)
+    if (auto frameTargetPtrOpt = getDisplayIdVariant()
+                                         .and_then(asPhysicalDisplayId)
                                          .and_then([&refreshArgs](PhysicalDisplayId id) {
                                              return refreshArgs.frameTargets.get(id);
                                          })) {
@@ -1672,6 +1676,7 @@
                     Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
         }
         layer->getLayerFE().setReleaseFence(releaseFence);
+        layer->getLayerFE().setReleasedBuffer(layer->getLayerFE().getCompositionState()->buffer);
     }
 
     // We've got a list of layers needing fences, that are disjoint with
@@ -1841,7 +1846,7 @@
     if (!getDisplayId()) {
         return;
     }
-    if (auto displayId = PhysicalDisplayId::tryCast(*getDisplayId())) {
+    if (auto displayId = getDisplayIdVariant().and_then(asPhysicalDisplayId)) {
         auto& hwc = getCompositionEngine().getHwComposer();
         const status_t error =
                 hwc.setDisplayPictureProfileHandle(*displayId, getState().pictureProfileHandle);
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index b278000..34c09db 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -62,10 +62,15 @@
     void SetUp() override {
         EXPECT_CALL(*mOutput1, getDisplayId)
                 .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId1)));
+        EXPECT_CALL(*mOutput1, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId1));
+
         EXPECT_CALL(*mOutput2, getDisplayId)
                 .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId2)));
+        EXPECT_CALL(*mOutput2, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId2));
+
         EXPECT_CALL(*mOutput3, getDisplayId)
                 .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId3)));
+        EXPECT_CALL(*mOutput3, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId3));
 
         // Most tests will depend on the outputs being enabled.
         for (auto& state : mOutputStates) {
@@ -314,12 +319,23 @@
     void SetUp() override {
         EXPECT_CALL(*mDisplay1, getDisplayId)
                 .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId1)));
+        EXPECT_CALL(*mDisplay1, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId1));
+
         EXPECT_CALL(*mDisplay2, getDisplayId)
                 .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId2)));
+        EXPECT_CALL(*mDisplay2, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId2));
+
         EXPECT_CALL(*mVirtualDisplay, getDisplayId)
                 .WillRepeatedly(Return(std::make_optional<DisplayId>(kGpuVirtualDisplayId)));
+        const DisplayIdVariant gpuVariant =
+                GpuVirtualDisplayId::fromValue(kGpuVirtualDisplayId.value);
+        EXPECT_CALL(*mVirtualDisplay, getDisplayIdVariant).WillRepeatedly(Return(gpuVariant));
+
         EXPECT_CALL(*mHalVirtualDisplay, getDisplayId)
                 .WillRepeatedly(Return(std::make_optional<DisplayId>(kHalVirtualDisplayId)));
+        const DisplayIdVariant halVariant =
+                HalVirtualDisplayId::fromValue(kHalVirtualDisplayId.value);
+        EXPECT_CALL(*mHalVirtualDisplay, getDisplayIdVariant).WillRepeatedly(Return(halVariant));
 
         // Most tests will depend on the outputs being enabled.
         for (auto& state : mOutputStates) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index c1e59d0..77fd446 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -278,7 +278,7 @@
             impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForGpuVirtualDisplay());
     EXPECT_FALSE(display->isSecure());
     EXPECT_TRUE(display->isVirtual());
-    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(display->getId()));
+    EXPECT_TRUE(display->getDisplayIdVariant().and_then(asDisplayIdOfType<GpuVirtualDisplayId>));
 }
 
 /*
@@ -318,6 +318,7 @@
     EXPECT_EQ(HAL_VIRTUAL_DISPLAY_ID, mDisplay->getId());
     EXPECT_FALSE(mDisplay->isSecure());
     EXPECT_TRUE(mDisplay->isVirtual());
+    EXPECT_TRUE(mDisplay->getDisplayIdVariant().and_then(asDisplayIdOfType<HalVirtualDisplayId>));
     EXPECT_FALSE(mDisplay->isValid());
 
     const auto& filter = mDisplay->getState().layerFilter;
@@ -337,6 +338,7 @@
     EXPECT_EQ(GPU_VIRTUAL_DISPLAY_ID, mDisplay->getId());
     EXPECT_FALSE(mDisplay->isSecure());
     EXPECT_TRUE(mDisplay->isVirtual());
+    EXPECT_TRUE(mDisplay->getDisplayIdVariant().and_then(asDisplayIdOfType<GpuVirtualDisplayId>));
     EXPECT_FALSE(mDisplay->isValid());
 
     const auto& filter = mDisplay->getState().layerFilter;
@@ -572,7 +574,7 @@
     auto args = getDisplayCreationArgsForGpuVirtualDisplay();
     std::shared_ptr<Display> gpuDisplay =
             createPartialMockDisplay<Display>(mCompositionEngine, args);
-    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(gpuDisplay->getId()));
+    EXPECT_TRUE(gpuDisplay->getDisplayIdVariant().and_then(asDisplayIdOfType<GpuVirtualDisplayId>));
 
     chooseCompositionStrategy(gpuDisplay.get());
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 09ad9fa..590626a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -148,20 +148,23 @@
         virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
 
         virtual ftl::Optional<DisplayId> getDisplayId() const override { return mId; }
+        virtual ftl::Optional<DisplayIdVariant> getDisplayIdVariant() const override {
+            return DisplayIdVariant(mId);
+        }
 
         virtual bool hasPictureProcessing() const override { return mHasPictureProcessing; }
         virtual int32_t getMaxLayerPictureProfiles() const override {
             return mMaxLayerPictureProfiles;
         }
 
-        void setDisplayIdForTest(DisplayId value) { mId = value; }
+        void setDisplayIdForTest(PhysicalDisplayId value) { mId = value; }
 
         void setHasPictureProcessingForTest(bool value) { mHasPictureProcessing = value; }
 
         void setMaxLayerPictureProfilesForTest(int32_t value) { mMaxLayerPictureProfiles = value; }
 
     private:
-        ftl::Optional<DisplayId> mId;
+        PhysicalDisplayId mId;
         bool mHasPictureProcessing;
         int32_t mMaxLayerPictureProfiles;
     };
@@ -3311,6 +3314,17 @@
     sp<Fence> layer2Fence = sp<Fence>::make();
     sp<Fence> layer3Fence = sp<Fence>::make();
 
+    // Set up layerfe buffers
+    LayerFECompositionState layer1State;
+    layer1State.buffer = sp<GraphicBuffer>::make();
+    LayerFECompositionState layer2State;
+    layer2State.buffer = sp<GraphicBuffer>::make();
+    LayerFECompositionState layer3State;
+    layer3State.buffer = nullptr;
+    EXPECT_CALL(*mLayer1.layerFE, getCompositionState()).WillOnce(Return(&layer1State));
+    EXPECT_CALL(*mLayer2.layerFE, getCompositionState()).WillOnce(Return(&layer2State));
+    EXPECT_CALL(*mLayer3.layerFE, getCompositionState()).WillOnce(Return(&layer3State));
+
     Output::FrameFences frameFences;
     frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
     frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
@@ -3327,14 +3341,23 @@
             .WillOnce([&layer1Fence](FenceResult releaseFence) {
                 EXPECT_EQ(FenceResult(layer1Fence), releaseFence);
             });
+    EXPECT_CALL(*mLayer1.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+        EXPECT_EQ(layer1State.buffer, buffer);
+    });
     EXPECT_CALL(*mLayer2.layerFE, setReleaseFence(_))
             .WillOnce([&layer2Fence](FenceResult releaseFence) {
                 EXPECT_EQ(FenceResult(layer2Fence), releaseFence);
             });
+    EXPECT_CALL(*mLayer2.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+        EXPECT_EQ(layer2State.buffer, buffer);
+    });
     EXPECT_CALL(*mLayer3.layerFE, setReleaseFence(_))
             .WillOnce([&layer3Fence](FenceResult releaseFence) {
                 EXPECT_EQ(FenceResult(layer3Fence), releaseFence);
             });
+    EXPECT_CALL(*mLayer3.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+        EXPECT_EQ(layer3State.buffer, buffer);
+    });
 
     constexpr bool kFlushEvenWhenDisabled = false;
     mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
@@ -3350,6 +3373,17 @@
     frameFences.layerFences.emplace(&mLayer2.hwc2Layer, sp<Fence>::make());
     frameFences.layerFences.emplace(&mLayer3.hwc2Layer, sp<Fence>::make());
 
+    // Set up layerfe buffers
+    LayerFECompositionState layer1State;
+    layer1State.buffer = sp<GraphicBuffer>::make();
+    LayerFECompositionState layer2State;
+    layer2State.buffer = sp<GraphicBuffer>::make();
+    LayerFECompositionState layer3State;
+    layer3State.buffer = nullptr;
+    EXPECT_CALL(*mLayer1.layerFE, getCompositionState()).WillOnce(Return(&layer1State));
+    EXPECT_CALL(*mLayer2.layerFE, getCompositionState()).WillOnce(Return(&layer2State));
+    EXPECT_CALL(*mLayer3.layerFE, getCompositionState()).WillOnce(Return(&layer3State));
+
     EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
     EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
 
@@ -3359,6 +3393,15 @@
     EXPECT_CALL(*mLayer1.layerFE, setReleaseFence).WillOnce(Return());
     EXPECT_CALL(*mLayer2.layerFE, setReleaseFence).WillOnce(Return());
     EXPECT_CALL(*mLayer3.layerFE, setReleaseFence).WillOnce(Return());
+    EXPECT_CALL(*mLayer1.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+        EXPECT_EQ(layer1State.buffer, buffer);
+    });
+    EXPECT_CALL(*mLayer2.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+        EXPECT_EQ(layer2State.buffer, buffer);
+    });
+    EXPECT_CALL(*mLayer3.layerFE, setReleasedBuffer(_)).WillOnce([&](sp<GraphicBuffer> buffer) {
+        EXPECT_EQ(layer3State.buffer, buffer);
+    });
     constexpr bool kFlushEvenWhenDisabled = false;
     mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 }
@@ -3401,7 +3444,6 @@
             .WillOnce([&presentFence](FenceResult fenceResult) {
                 EXPECT_EQ(FenceResult(presentFence), fenceResult);
             });
-
     constexpr bool kFlushEvenWhenDisabled = false;
     mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index b396544..de7d455 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -26,7 +26,6 @@
 
 #include <common/trace.h>
 #include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Display.h>
 #include <compositionengine/DisplayColorProfile.h>
 #include <compositionengine/DisplayColorProfileCreationArgs.h>
 #include <compositionengine/DisplayCreationArgs.h>
@@ -51,17 +50,6 @@
 
 namespace hal = hardware::graphics::composer::hal;
 
-namespace gui {
-inline std::string_view to_string(ISurfaceComposer::OptimizationPolicy optimizationPolicy) {
-    switch (optimizationPolicy) {
-        case ISurfaceComposer::OptimizationPolicy::optimizeForPower:
-            return "optimizeForPower";
-        case ISurfaceComposer::OptimizationPolicy::optimizeForPerformance:
-            return "optimizeForPerformance";
-    }
-}
-} // namespace gui
-
 DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(
         const sp<SurfaceFlinger>& flinger, HWComposer& hwComposer, const wp<IBinder>& displayToken,
         std::shared_ptr<compositionengine::Display> compositionDisplay)
@@ -308,6 +296,10 @@
     return mCompositionDisplay->getId();
 }
 
+bool DisplayDevice::isVirtual() const {
+    return mCompositionDisplay->isVirtual();
+}
+
 bool DisplayDevice::isSecure() const {
     return mCompositionDisplay->isSecure();
 }
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 02522a3..1b14145 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -23,6 +23,8 @@
 #include <android-base/thread_annotations.h>
 #include <android/native_window.h>
 #include <binder/IBinder.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplaySurface.h>
 #include <gui/LayerState.h>
 #include <math/mat4.h>
 #include <renderengine/RenderEngine.h>
@@ -61,15 +63,21 @@
 struct CompositionInfo;
 struct DisplayDeviceCreationArgs;
 
-namespace compositionengine {
-class Display;
-class DisplaySurface;
-} // namespace compositionengine
-
 namespace display {
 class DisplaySnapshot;
 } // namespace display
 
+namespace gui {
+inline const char* to_string(ISurfaceComposer::OptimizationPolicy optimizationPolicy) {
+    switch (optimizationPolicy) {
+        case ISurfaceComposer::OptimizationPolicy::optimizeForPower:
+            return "optimizeForPower";
+        case ISurfaceComposer::OptimizationPolicy::optimizeForPerformance:
+            return "optimizeForPerformance";
+    }
+}
+} // namespace gui
+
 class DisplayDevice : public RefBase {
 public:
     constexpr static float sDefaultMinLumiance = 0.0;
@@ -85,7 +93,7 @@
         return mCompositionDisplay;
     }
 
-    bool isVirtual() const { return getId().isVirtual(); }
+    bool isVirtual() const;
     bool isPrimary() const { return mIsPrimary; }
 
     // isSecure indicates whether this display can be trusted to display
@@ -123,17 +131,30 @@
 
     DisplayId getId() const;
 
+    DisplayIdVariant getDisplayIdVariant() const {
+        const auto idVariant = mCompositionDisplay->getDisplayIdVariant();
+        LOG_FATAL_IF(!idVariant);
+        return *idVariant;
+    }
+
+    std::optional<VirtualDisplayIdVariant> getVirtualDisplayIdVariant() const {
+        return ftl::match(
+                getDisplayIdVariant(),
+                [](PhysicalDisplayId) { return std::optional<VirtualDisplayIdVariant>(); },
+                [](auto id) { return std::optional<VirtualDisplayIdVariant>(id); });
+    }
+
     // Shorthand to upcast the ID of a display whose type is known as a precondition.
     PhysicalDisplayId getPhysicalId() const {
-        const auto id = PhysicalDisplayId::tryCast(getId());
-        LOG_FATAL_IF(!id);
-        return *id;
+        const auto physicalDisplayId = asPhysicalDisplayId(getDisplayIdVariant());
+        LOG_FATAL_IF(!physicalDisplayId);
+        return *physicalDisplayId;
     }
 
     VirtualDisplayId getVirtualId() const {
-        const auto id = VirtualDisplayId::tryCast(getId());
-        LOG_FATAL_IF(!id);
-        return *id;
+        const auto virtualDisplayId = asVirtualDisplayId(getDisplayIdVariant());
+        LOG_FATAL_IF(!virtualDisplayId);
+        return *virtualDisplayId;
     }
 
     const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 384f7b2..56ed11f 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -47,7 +47,8 @@
 
 namespace android {
 
-VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId displayId,
+VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc,
+                                             VirtualDisplayIdVariant virtualIdVariant,
                                              const sp<IGraphicBufferProducer>& sink,
                                              const sp<IGraphicBufferProducer>& bqProducer,
                                              const sp<IGraphicBufferConsumer>& bqConsumer,
@@ -58,7 +59,7 @@
       : ConsumerBase(bqConsumer),
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
         mHwc(hwc),
-        mDisplayId(displayId),
+        mVirtualIdVariant(virtualIdVariant),
         mDisplayName(name),
         mSource{},
         mDefaultOutputFormat(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED),
@@ -119,7 +120,7 @@
 }
 
 status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) {
-    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+    if (isBackedByGpu()) {
         return NO_ERROR;
     }
 
@@ -133,7 +134,7 @@
 }
 
 status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) {
-    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+    if (isBackedByGpu()) {
         return NO_ERROR;
     }
 
@@ -181,7 +182,10 @@
 }
 
 status_t VirtualDisplaySurface::advanceFrame(float hdrSdrRatio) {
-    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+    const auto halVirtualDisplayId = ftl::match(
+            mVirtualIdVariant, [](HalVirtualDisplayId id) { return ftl::Optional(id); },
+            [](auto) { return ftl::Optional<HalVirtualDisplayId>(); });
+    if (!halVirtualDisplayId) {
         return NO_ERROR;
     }
 
@@ -212,11 +216,8 @@
     VDS_LOGV("%s: fb=%d(%p) out=%d(%p)", __func__, mFbProducerSlot, fbBuffer.get(),
              mOutputProducerSlot, outBuffer.get());
 
-    const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
-    LOG_FATAL_IF(!halDisplayId);
-    // At this point we know the output buffer acquire fence,
-    // so update HWC state with it.
-    mHwc.setOutputBuffer(*halDisplayId, mOutputFence, outBuffer);
+    // At this point we know the output buffer acquire fence, so update HWC state with it.
+    mHwc.setOutputBuffer(*halVirtualDisplayId, mOutputFence, outBuffer);
 
     status_t result = NO_ERROR;
     if (fbBuffer != nullptr) {
@@ -227,7 +228,7 @@
             hwcBuffer = fbBuffer; // HWC hasn't previously seen this buffer in this slot
         }
         // TODO: Correctly propagate the dataspace from GL composition
-        result = mHwc.setClientTarget(*halDisplayId, mFbProducerSlot, mFbFence, hwcBuffer,
+        result = mHwc.setClientTarget(*halVirtualDisplayId, mFbProducerSlot, mFbFence, hwcBuffer,
                                       ui::Dataspace::UNKNOWN, hdrSdrRatio);
     }
 
@@ -235,8 +236,8 @@
 }
 
 void VirtualDisplaySurface::onFrameCommitted() {
-    const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
-    if (!halDisplayId) {
+    const auto halDisplayId = asHalDisplayId(mVirtualIdVariant);
+    if (!halDisplayId.has_value()) {
         return;
     }
 
@@ -250,8 +251,7 @@
         Mutex::Autolock lock(mMutex);
         int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot);
         VDS_LOGV("%s: release scratch sslot=%d", __func__, sslot);
-        addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot],
-                retireFence);
+        addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot], retireFence);
         releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot]);
     }
 
@@ -299,7 +299,7 @@
 
 status_t VirtualDisplaySurface::requestBuffer(int pslot,
         sp<GraphicBuffer>* outBuf) {
-    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+    if (isBackedByGpu()) {
         return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
     }
 
@@ -321,7 +321,7 @@
 
 status_t VirtualDisplaySurface::dequeueBuffer(Source source,
         PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) {
-    LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value());
+    LOG_ALWAYS_FATAL_IF(isBackedByGpu());
 
     status_t result =
             mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight,
@@ -373,7 +373,7 @@
                                               PixelFormat format, uint64_t usage,
                                               uint64_t* outBufferAge,
                                               FrameEventHistoryDelta* outTimestamps) {
-    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+    if (isBackedByGpu()) {
         return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage, outBufferAge,
                                                    outTimestamps);
     }
@@ -459,7 +459,7 @@
 
 status_t VirtualDisplaySurface::queueBuffer(int pslot,
         const QueueBufferInput& input, QueueBufferOutput* output) {
-    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+    if (isBackedByGpu()) {
         return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
     }
 
@@ -517,7 +517,7 @@
 
 status_t VirtualDisplaySurface::cancelBuffer(int pslot,
         const sp<Fence>& fence) {
-    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
+    if (isBackedByGpu()) {
         return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
     }
 
@@ -621,7 +621,10 @@
 }
 
 status_t VirtualDisplaySurface::refreshOutputBuffer() {
-    LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value());
+    const auto halVirtualDisplayId = ftl::match(
+            mVirtualIdVariant, [](HalVirtualDisplayId id) { return ftl::Optional(id); },
+            [](auto) { return ftl::Optional<HalVirtualDisplayId>(); });
+    LOG_ALWAYS_FATAL_IF(!halVirtualDisplayId);
 
     if (mOutputProducerSlot >= 0) {
         mSource[SOURCE_SINK]->cancelBuffer(
@@ -640,14 +643,16 @@
     // until after GPU calls queueBuffer(). So here we just set the buffer
     // (for use in HWC prepare) but not the fence; we'll call this again with
     // the proper fence once we have it.
-    const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
-    LOG_FATAL_IF(!halDisplayId);
-    result = mHwc.setOutputBuffer(*halDisplayId, Fence::NO_FENCE,
+    result = mHwc.setOutputBuffer(*halVirtualDisplayId, Fence::NO_FENCE,
                                   mProducerBuffers[mOutputProducerSlot]);
 
     return result;
 }
 
+bool VirtualDisplaySurface::isBackedByGpu() const {
+    return std::holds_alternative<GpuVirtualDisplayId>(mVirtualIdVariant);
+}
+
 // This slot mapping function is its own inverse, so two copies are unnecessary.
 // Both are kept to make the intent clear where the function is called, and for
 // the (unlikely) chance that we switch to a different mapping function.
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 90426f7..cb65c79 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -75,7 +75,8 @@
                               public BnGraphicBufferProducer,
                               private ConsumerBase {
 public:
-    VirtualDisplaySurface(HWComposer&, VirtualDisplayId, const sp<IGraphicBufferProducer>& sink,
+    VirtualDisplaySurface(HWComposer&, VirtualDisplayIdVariant,
+                          const sp<IGraphicBufferProducer>& sink,
                           const sp<IGraphicBufferProducer>& bqProducer,
                           const sp<IGraphicBufferConsumer>& bqConsumer, const std::string& name);
 
@@ -145,6 +146,7 @@
     void updateQueueBufferOutput(QueueBufferOutput&&);
     void resetPerFrameState();
     status_t refreshOutputBuffer();
+    bool isBackedByGpu() const;
 
     // Both the sink and scratch buffer pools have their own set of slots
     // ("source slots", or "sslot"). We have to merge these into the single
@@ -159,7 +161,7 @@
     // Immutable after construction
     //
     HWComposer& mHwc;
-    const VirtualDisplayId mDisplayId;
+    const VirtualDisplayIdVariant mVirtualIdVariant;
     const std::string mDisplayName;
     sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
     uint32_t mDefaultOutputFormat;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 95a7170..2e31282 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -722,6 +722,10 @@
     uint32_t currentMaxAcquiredBufferCount =
             mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
 
+    if (FlagManager::getInstance().monitor_buffer_fences()) {
+        buffer->getDependencyMonitor().addEgress(FenceTime::makeValid(fence), "Layer release");
+    }
+
     if (listener) {
         listener->onReleaseBuffer(callbackId, fence, currentMaxAcquiredBufferCount);
     }
@@ -940,6 +944,7 @@
             std::max(mDrawingState.frameNumber, mDrawingState.barrierFrameNumber);
 
     mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
+    mDrawingState.previousBuffer = std::move(mDrawingState.buffer);
     mDrawingState.buffer = std::move(buffer);
     mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
             ? bufferData.acquireFence
@@ -1122,6 +1127,7 @@
             handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
             handle->frameNumber = mDrawingState.frameNumber;
             handle->previousFrameNumber = mDrawingState.previousFrameNumber;
+            handle->previousBuffer = mDrawingState.previousBuffer;
             if (mPreviousReleaseBufferEndpoint == handle->listener) {
                 // Add fence from previous screenshot now so that it can be dispatched to the
                 // client.
@@ -1433,8 +1439,8 @@
                                                presentFence,
                                                FrameTracer::FrameEvent::PRESENT_FENCE);
             mDeprecatedFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
-        } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
-                   displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
+        } else if (const auto displayId = asPhysicalDisplayId(display->getDisplayIdVariant());
+                   displayId.has_value() && mFlinger->getHwComposer().isConnected(*displayId)) {
             // The HWC doesn't support present fences, so use the present timestamp instead.
             const nsecs_t presentTimestamp =
                     mFlinger->getHwComposer().getPresentTimestamp(*displayId);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 081bb22..88754f9 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -117,6 +117,7 @@
         uint32_t bufferTransform;
         bool transformToDisplayInverse;
         Region transparentRegionHint;
+        std::shared_ptr<renderengine::ExternalTexture> previousBuffer;
         std::shared_ptr<renderengine::ExternalTexture> buffer;
         sp<Fence> acquireFence;
         std::shared_ptr<FenceTime> acquireFenceTime;
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index b619268..5e076bd 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -410,6 +410,15 @@
     if (mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::FULFILLED) {
         return;
     }
+
+    if (releaseFence.has_value()) {
+        if (FlagManager::getInstance().monitor_buffer_fences()) {
+            if (auto strongBuffer = mReleasedBuffer.promote()) {
+                strongBuffer->getDependencyMonitor()
+                        .addAccessCompletion(FenceTime::makeValid(releaseFence.value()), "HWC");
+            }
+        }
+    }
     mReleaseFence.set_value(releaseFence);
     mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::FULFILLED;
 }
@@ -428,6 +437,10 @@
     return mReleaseFencePromiseStatus;
 }
 
+void LayerFE::setReleasedBuffer(sp<GraphicBuffer> buffer) {
+    mReleasedBuffer = std::move(buffer);
+}
+
 void LayerFE::setLastHwcState(const LayerFE::HwcLayerDebugState &state) {
     mLastHwcState = state;
 }
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index a537456..b89b6b4 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -18,6 +18,7 @@
 
 #include <android/gui/CachingHint.h>
 #include <gui/LayerMetadata.h>
+#include <ui/GraphicBuffer.h>
 #include <ui/LayerStack.h>
 #include <ui/PictureProfileHandle.h>
 
@@ -58,6 +59,7 @@
     ftl::Future<FenceResult> createReleaseFenceFuture() override;
     void setReleaseFence(const FenceResult& releaseFence) override;
     LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() override;
+    void setReleasedBuffer(sp<GraphicBuffer> buffer) override;
     void onPictureProfileCommitted() override;
 
     // Used for debugging purposes, e.g. perfetto tracing, dumpsys.
@@ -95,6 +97,7 @@
     std::promise<FenceResult> mReleaseFence;
     ReleaseFencePromiseStatus mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::UNINITIALIZED;
     HwcLayerDebugState mLastHwcState;
+    wp<GraphicBuffer> mReleasedBuffer;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h
index 38dc11d..81155fd 100644
--- a/services/surfaceflinger/LayerVector.h
+++ b/services/surfaceflinger/LayerVector.h
@@ -49,7 +49,8 @@
     using Visitor = std::function<void(Layer*)>;
 
 private:
-    const StateSet mStateSet;
+    // FIXME: This is set but not used anywhere.
+    [[maybe_unused]] const StateSet mStateSet;
 };
 }
 
diff --git a/services/surfaceflinger/PowerAdvisor/SessionManager.h b/services/surfaceflinger/PowerAdvisor/SessionManager.h
index 93a80b5..afa52eb 100644
--- a/services/surfaceflinger/PowerAdvisor/SessionManager.h
+++ b/services/surfaceflinger/PowerAdvisor/SessionManager.h
@@ -68,7 +68,8 @@
     bool isLayerRelevant(int32_t layerId);
 
     // The UID of whoever created our ISessionManager connection
-    const uid_t mUid;
+    // FIXME: This is set but is not used anywhere.
+    [[maybe_unused]] const uid_t mUid;
 
     // State owned by the main thread
 
@@ -99,4 +100,4 @@
 };
 
 } // namespace adpf
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 514adac..615492a 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -348,7 +348,7 @@
 
     SurfaceFlinger::ScreenshotArgs screenshotArgs;
     screenshotArgs.captureTypeVariant = displayWeak;
-    screenshotArgs.displayId = std::nullopt;
+    screenshotArgs.displayIdVariant = std::nullopt;
     screenshotArgs.sourceCrop = sampledBounds.isEmpty() ? layerStackSpaceRect : sampledBounds;
     screenshotArgs.reqSize = sampledBounds.getSize();
     screenshotArgs.dataspace = ui::Dataspace::V0_SRGB;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
index 767462d..70ae940 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
@@ -36,7 +36,7 @@
 
 using CompositionCoverageFlags = ftl::Flags<CompositionCoverage>;
 
-using CompositionCoveragePerDisplay = ui::DisplayMap<DisplayId, CompositionCoverageFlags>;
+using CompositionCoveragePerDisplay = ui::DisplayMap<DisplayIdVariant, CompositionCoverageFlags>;
 
 inline CompositionCoverageFlags multiDisplayUnion(const CompositionCoveragePerDisplay& displays) {
     CompositionCoverageFlags coverage;
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index af6d4d3..2906bbd 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -30,11 +30,12 @@
 std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) {
     std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated<
             ScreenCaptureOutput, compositionengine::CompositionEngine,
-            /* sourceCrop */ const Rect, std::optional<DisplayId>,
+            /* sourceCrop */ const Rect, ftl::Optional<DisplayIdVariant>,
             const compositionengine::Output::ColorProfile&,
             /* layerAlpha */ float,
-            /* regionSampling */ bool>(args.compositionEngine, args.sourceCrop, args.displayId,
-                                       args.colorProfile, args.layerAlpha, args.regionSampling,
+            /* regionSampling */ bool>(args.compositionEngine, args.sourceCrop,
+                                       args.displayIdVariant, args.colorProfile, args.layerAlpha,
+                                       args.regionSampling,
                                        args.dimInGammaSpaceForEnhancedScreenshots,
                                        args.enableLocalTonemapping);
     output->editState().isSecure = args.isSecure;
@@ -59,8 +60,8 @@
 
     {
         std::string name = args.regionSampling ? "RegionSampling" : "ScreenCaptureOutput";
-        if (args.displayId) {
-            base::StringAppendF(&name, " for %" PRIu64, args.displayId.value().value);
+        if (const auto id = args.displayIdVariant.and_then(asDisplayIdOfType<DisplayId>)) {
+            base::StringAppendF(&name, " for %" PRIu64, id->value);
         }
         output->setName(name);
     }
@@ -68,12 +69,12 @@
 }
 
 ScreenCaptureOutput::ScreenCaptureOutput(
-        const Rect sourceCrop, std::optional<DisplayId> displayId,
+        const Rect sourceCrop, ftl::Optional<DisplayIdVariant> displayIdVariant,
         const compositionengine::Output::ColorProfile& colorProfile, float layerAlpha,
         bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots,
         bool enableLocalTonemapping)
       : mSourceCrop(sourceCrop),
-        mDisplayId(displayId),
+        mDisplayIdVariant(displayIdVariant),
         mColorProfile(colorProfile),
         mLayerAlpha(layerAlpha),
         mRegionSampling(regionSampling),
@@ -137,12 +138,9 @@
         }
 
         std::vector<aidl::android::hardware::graphics::composer3::Luts> luts;
-        if (mDisplayId) {
-            const auto id = PhysicalDisplayId::tryCast(mDisplayId.value());
-            if (id) {
-                auto& hwc = getCompositionEngine().getHwComposer();
-                hwc.getLuts(*id, buffers, &luts);
-            }
+        if (const auto physicalDisplayId = mDisplayIdVariant.and_then(asPhysicalDisplayId)) {
+            auto& hwc = getCompositionEngine().getHwComposer();
+            hwc.getLuts(*physicalDisplayId, buffers, &luts);
         }
 
         if (buffers.size() == luts.size()) {
diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h
index b3e98b1..d4e20fc 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.h
+++ b/services/surfaceflinger/ScreenCaptureOutput.h
@@ -30,7 +30,7 @@
     ui::LayerStack layerStack;
     Rect sourceCrop;
     std::shared_ptr<renderengine::ExternalTexture> buffer;
-    std::optional<DisplayId> displayId;
+    ftl::Optional<DisplayIdVariant> displayIdVariant;
     ui::Size reqBufferSize;
     float sdrWhitePointNits;
     float displayBrightnessNits;
@@ -51,7 +51,7 @@
 // SurfaceFlinger::captureLayers and SurfaceFlinger::captureDisplay.
 class ScreenCaptureOutput : public compositionengine::impl::Output {
 public:
-    ScreenCaptureOutput(const Rect sourceCrop, std::optional<DisplayId> displayId,
+    ScreenCaptureOutput(const Rect sourceCrop, ftl::Optional<DisplayIdVariant> displayIdVariant,
                         const compositionengine::Output::ColorProfile& colorProfile,
                         float layerAlpha, bool regionSampling,
                         bool dimInGammaSpaceForEnhancedScreenshots, bool enableLocalTonemapping);
@@ -70,7 +70,7 @@
 private:
     std::unordered_map<int32_t, aidl::android::hardware::graphics::composer3::Luts> generateLuts();
     const Rect mSourceCrop;
-    const std::optional<DisplayId> mDisplayId;
+    const ftl::Optional<DisplayIdVariant> mDisplayIdVariant;
     const compositionengine::Output::ColorProfile& mColorProfile;
     const float mLayerAlpha;
     const bool mRegionSampling;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0bbef95..aa933ee 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -659,12 +659,14 @@
     }
 }
 
-VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution, ui::PixelFormat format,
-                                                       const std::string& uniqueId) {
+std::optional<VirtualDisplayIdVariant> SurfaceFlinger::acquireVirtualDisplay(
+        ui::Size resolution, ui::PixelFormat format, const std::string& uniqueId,
+        compositionengine::DisplayCreationArgsBuilder& builder) {
     if (auto& generator = mVirtualDisplayIdGenerators.hal) {
         if (const auto id = generator->generateId()) {
             if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format)) {
                 acquireVirtualDisplaySnapshot(*id, uniqueId);
+                builder.setId(*id);
                 return *id;
             }
 
@@ -679,22 +681,23 @@
     const auto id = mVirtualDisplayIdGenerators.gpu.generateId();
     LOG_ALWAYS_FATAL_IF(!id, "Failed to generate ID for GPU virtual display");
     acquireVirtualDisplaySnapshot(*id, uniqueId);
+    builder.setId(*id);
     return *id;
 }
 
-void SurfaceFlinger::releaseVirtualDisplay(VirtualDisplayId displayId) {
-    if (const auto id = HalVirtualDisplayId::tryCast(displayId)) {
-        if (auto& generator = mVirtualDisplayIdGenerators.hal) {
-            generator->releaseId(*id);
-            releaseVirtualDisplaySnapshot(*id);
-        }
-        return;
-    }
-
-    const auto id = GpuVirtualDisplayId::tryCast(displayId);
-    LOG_ALWAYS_FATAL_IF(!id);
-    mVirtualDisplayIdGenerators.gpu.releaseId(*id);
-    releaseVirtualDisplaySnapshot(*id);
+void SurfaceFlinger::releaseVirtualDisplay(VirtualDisplayIdVariant displayId) {
+    ftl::match(
+            displayId,
+            [this](HalVirtualDisplayId halVirtualDisplayId) {
+                if (auto& generator = mVirtualDisplayIdGenerators.hal) {
+                    generator->releaseId(halVirtualDisplayId);
+                    releaseVirtualDisplaySnapshot(halVirtualDisplayId);
+                }
+            },
+            [this](GpuVirtualDisplayId gpuVirtualDisplayId) {
+                mVirtualDisplayIdGenerators.gpu.releaseId(gpuVirtualDisplayId);
+                releaseVirtualDisplaySnapshot(gpuVirtualDisplayId);
+            });
 }
 
 void SurfaceFlinger::releaseVirtualDisplaySnapshot(VirtualDisplayId displayId) {
@@ -1182,6 +1185,7 @@
     const auto& snapshot = snapshotRef.get();
 
     info->connectionType = snapshot.connectionType();
+    info->port = snapshot.port();
     info->deviceProductInfo = snapshot.deviceProductInfo();
 
     if (mEmulatedDisplayDensity) {
@@ -1265,7 +1269,17 @@
     ui::FrameRateCategoryRate frameRateCategoryRate(normal.getValue(), high.getValue());
     info->frameRateCategoryRate = frameRateCategoryRate;
 
-    info->supportedRefreshRates = display->refreshRateSelector().getSupportedFrameRates();
+    if (info->hasArrSupport) {
+        info->supportedRefreshRates = display->refreshRateSelector().getSupportedFrameRates();
+    } else {
+        // On non-ARR devices, list the refresh rates same as the supported display modes.
+        std::vector<float> supportedFrameRates;
+        supportedFrameRates.reserve(info->supportedDisplayModes.size());
+        std::transform(info->supportedDisplayModes.begin(), info->supportedDisplayModes.end(),
+                       std::back_inserter(supportedFrameRates),
+                       [](ui::DisplayMode mode) { return mode.peakRefreshRate; });
+        info->supportedRefreshRates = supportedFrameRates;
+    }
     info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
     info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
 
@@ -2863,9 +2877,9 @@
     // output. Layer stacks are not tracked in Display when we iterate through
     // frameTargeters. Cross-referencing layer stacks allows us to filter out displays
     // by ID with duplicate layer stacks before adding them to CompositionEngine output.
-    ui::DisplayMap<DisplayId, ui::LayerStack> physicalDisplayLayerStacks;
+    ui::DisplayMap<PhysicalDisplayId, ui::LayerStack> physicalDisplayLayerStacks;
     for (auto& [_, display] : displays) {
-        const auto id = PhysicalDisplayId::tryCast(display->getId());
+        const auto id = asPhysicalDisplayId(display->getDisplayIdVariant());
         if (id && frameTargeters.contains(*id)) {
             physicalDisplayLayerStacks.try_emplace(*id, display->getLayerStack());
         }
@@ -3131,7 +3145,7 @@
     for (const auto& [_, display] : displays) {
         const auto& state = display->getCompositionDisplay()->getState();
         CompositionCoverageFlags& flags =
-                mCompositionCoverage.try_emplace(display->getId()).first->second;
+                mCompositionCoverage.try_emplace(display->getDisplayIdVariant()).first->second;
 
         if (state.usesDeviceComposition) {
             flags |= CompositionCoverage::Hwc;
@@ -3185,8 +3199,8 @@
     CompositeResultsPerDisplay resultsPerDisplay;
 
     // Filter out virtual displays.
-    for (const auto& [id, coverage] : mCompositionCoverage) {
-        if (const auto idOpt = PhysicalDisplayId::tryCast(id)) {
+    for (const auto& [idVar, coverage] : mCompositionCoverage) {
+        if (const auto idOpt = asPhysicalDisplayId(idVar)) {
             resultsPerDisplay.try_emplace(*idOpt, CompositeResult{coverage});
         }
     }
@@ -3224,16 +3238,12 @@
     return false;
 }
 
-ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(DisplayId displayId,
+ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(PhysicalDisplayId displayId,
                                                            bool isPrimary) const {
-    const auto id = PhysicalDisplayId::tryCast(displayId);
-    if (!id) {
-        return ui::ROTATION_0;
-    }
     if (!mIgnoreHwcPhysicalDisplayOrientation &&
         getHwComposer().getComposer()->isSupported(
                 Hwc2::Composer::OptionalFeature::PhysicalDisplayOrientation)) {
-        switch (getHwComposer().getPhysicalDisplayOrientation(*id)) {
+        switch (getHwComposer().getPhysicalDisplayOrientation(displayId)) {
             case Hwc2::AidlTransform::ROT_90:
                 return ui::ROTATION_90;
             case Hwc2::AidlTransform::ROT_180:
@@ -3881,13 +3891,17 @@
     creationArgs.hasWideColorGamut = false;
     creationArgs.supportedPerFrameMetadata = 0;
 
-    if (const auto physicalIdOpt = PhysicalDisplayId::tryCast(compositionDisplay->getId())) {
+    if (const auto physicalIdOpt =
+                compositionDisplay->getDisplayIdVariant().and_then(asPhysicalDisplayId)) {
         const auto physicalId = *physicalIdOpt;
 
         creationArgs.isPrimary = physicalId == getPrimaryDisplayIdLocked();
         creationArgs.refreshRateSelector =
                 FTL_FAKE_GUARD(kMainThreadContext,
                                mDisplayModeController.selectorPtrFor(physicalId));
+        creationArgs.physicalOrientation =
+                getPhysicalDisplayOrientation(physicalId, creationArgs.isPrimary);
+        ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation));
 
         mPhysicalDisplays.get(physicalId)
                 .transform(&PhysicalDisplay::snapshotRef)
@@ -3900,7 +3914,8 @@
                 }));
     }
 
-    if (const auto id = HalDisplayId::tryCast(compositionDisplay->getId())) {
+    if (const auto id = compositionDisplay->getDisplayIdVariant().and_then(
+                asHalDisplayId<DisplayIdVariant>)) {
         getHwComposer().getHdrCapabilities(*id, &creationArgs.hdrCapabilities);
         creationArgs.supportedPerFrameMetadata = getHwComposer().getSupportedPerFrameMetadata(*id);
     }
@@ -3916,10 +3931,6 @@
         nativeWindow->setSwapInterval(nativeWindow.get(), 0);
     }
 
-    creationArgs.physicalOrientation =
-            getPhysicalDisplayOrientation(compositionDisplay->getId(), creationArgs.isPrimary);
-    ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation));
-
     if (FlagManager::getInstance().correct_virtual_display_power_state()) {
         creationArgs.initialPowerMode = state.initialPowerMode;
     } else {
@@ -3949,7 +3960,8 @@
                                              mode.getPeakFps());
     }
 
-    display->setLayerFilter(makeLayerFilterForDisplay(display->getId(), state.layerStack));
+    display->setLayerFilter(
+            makeLayerFilterForDisplay(display->getDisplayIdVariant(), state.layerStack));
     display->setProjection(state.orientation, state.layerStackSpaceRect,
                            state.orientedDisplaySpaceRect);
     display->setDisplayName(state.displayName);
@@ -4005,10 +4017,12 @@
     }
 
     compositionengine::DisplayCreationArgsBuilder builder;
+    std::optional<VirtualDisplayIdVariant> virtualDisplayIdVariantOpt;
     if (const auto& physical = state.physical) {
         builder.setId(physical->id);
     } else {
-        builder.setId(acquireVirtualDisplay(resolution, pixelFormat, state.uniqueId));
+        virtualDisplayIdVariantOpt =
+                acquireVirtualDisplay(resolution, pixelFormat, state.uniqueId, builder);
     }
 
     builder.setPixels(resolution);
@@ -4028,10 +4042,10 @@
     getFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false);
 
     if (state.isVirtual()) {
-        const auto displayId = VirtualDisplayId::tryCast(compositionDisplay->getId());
-        LOG_FATAL_IF(!displayId);
-        auto surface = sp<VirtualDisplaySurface>::make(getHwComposer(), *displayId, state.surface,
-                                                       bqProducer, bqConsumer, state.displayName);
+        LOG_FATAL_IF(!virtualDisplayIdVariantOpt);
+        auto surface = sp<VirtualDisplaySurface>::make(getHwComposer(), *virtualDisplayIdVariantOpt,
+                                                       state.surface, bqProducer, bqConsumer,
+                                                       state.displayName);
         displaySurface = surface;
         producer = std::move(surface);
     } else {
@@ -4039,18 +4053,17 @@
                  "adding a supported display, but rendering "
                  "surface is provided (%p), ignoring it",
                  state.surface.get());
-        const auto displayId = PhysicalDisplayId::tryCast(compositionDisplay->getId());
-        LOG_FATAL_IF(!displayId);
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
         const auto frameBufferSurface =
-                sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqProducer, bqConsumer,
+                sp<FramebufferSurface>::make(getHwComposer(), state.physical->id, bqProducer,
+                                             bqConsumer,
                                              state.physical->activeMode->getResolution(),
                                              ui::Size(maxGraphicsWidth, maxGraphicsHeight));
         displaySurface = frameBufferSurface;
         producer = frameBufferSurface->getSurface()->getIGraphicBufferProducer();
 #else
         displaySurface =
-                sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqConsumer,
+                sp<FramebufferSurface>::make(getHwComposer(), state.physical->id, bqConsumer,
                                              state.physical->activeMode->getResolution(),
                                              ui::Size(maxGraphicsWidth, maxGraphicsHeight));
         producer = bqProducer;
@@ -4107,8 +4120,8 @@
     if (display) {
         display->disconnect();
 
-        if (display->isVirtual()) {
-            releaseVirtualDisplay(display->getVirtualId());
+        if (const auto virtualDisplayIdVariant = display->getVirtualDisplayIdVariant()) {
+            releaseVirtualDisplay(*virtualDisplayIdVariant);
         } else {
             mScheduler->unregisterDisplay(display->getPhysicalId(), mActiveDisplayId);
         }
@@ -4151,8 +4164,8 @@
     if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) {
         if (const auto display = getDisplayDeviceLocked(displayToken)) {
             display->disconnect();
-            if (display->isVirtual()) {
-                releaseVirtualDisplay(display->getVirtualId());
+            if (const auto virtualDisplayIdVariant = display->getVirtualDisplayIdVariant()) {
+                releaseVirtualDisplay(*virtualDisplayIdVariant);
             }
 
             if (display->isRefreshable()) {
@@ -4184,8 +4197,8 @@
 
     if (const auto display = getDisplayDeviceLocked(displayToken)) {
         if (currentState.layerStack != drawingState.layerStack) {
-            display->setLayerFilter(
-                    makeLayerFilterForDisplay(display->getId(), currentState.layerStack));
+            display->setLayerFilter(makeLayerFilterForDisplay(display->getDisplayIdVariant(),
+                                                              currentState.layerStack));
         }
         if (currentState.flags != drawingState.flags) {
             display->setFlags(currentState.flags);
@@ -4427,7 +4440,7 @@
 void SurfaceFlinger::updateCursorAsync() {
     compositionengine::CompositionRefreshArgs refreshArgs;
     for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
-        if (HalDisplayId::tryCast(display->getId())) {
+        if (asHalDisplayId(display->getDisplayIdVariant())) {
             refreshArgs.outputs.push_back(display->getCompositionDisplay());
         }
     }
@@ -5085,6 +5098,12 @@
                                                      layerName.c_str(), transactionState.getId());
             if (resolvedState.externalTexture) {
                 resolvedState.state.bufferData->buffer = resolvedState.externalTexture->getBuffer();
+                if (FlagManager::getInstance().monitor_buffer_fences()) {
+                    resolvedState.state.bufferData->buffer->getDependencyMonitor()
+                            .addIngress(FenceTime::makeValid(
+                                                resolvedState.state.bufferData->acquireFence),
+                                        "Incoming txn");
+                }
             }
             mBufferCountTracker.increment(resolvedState.layerId);
         }
@@ -5700,7 +5719,13 @@
         incRefreshableDisplays();
     }
 
+    if (displayId == mActiveDisplayId &&
+        FlagManager::getInstance().correct_virtual_display_power_state()) {
+        applyOptimizationPolicy(__func__);
+    }
+
     const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr;
+    using OptimizationPolicy = gui::ISurfaceComposer::OptimizationPolicy;
     if (currentMode == hal::PowerMode::OFF) {
         // Turn on the display
 
@@ -5715,12 +5740,10 @@
             onActiveDisplayChangedLocked(activeDisplay.get(), *display);
         }
 
-        if (displayId == mActiveDisplayId) {
-            if (FlagManager::getInstance().correct_virtual_display_power_state()) {
-                applyOptimizationPolicy("setPhysicalDisplayPowerMode(ON)");
-            } else {
-                disablePowerOptimizations("setPhysicalDisplayPowerMode(ON)");
-            }
+        if (displayId == mActiveDisplayId &&
+            !FlagManager::getInstance().correct_virtual_display_power_state()) {
+            optimizeThreadScheduling("setPhysicalDisplayPowerMode(ON/DOZE)",
+                                     OptimizationPolicy::optimizeForPerformance);
         }
 
         getHwComposer().setPowerMode(displayId, mode);
@@ -5729,7 +5752,8 @@
                     mScheduler->getVsyncSchedule(displayId)->getPendingHardwareVsyncState();
             requestHardwareVsync(displayId, enable);
 
-            if (displayId == mActiveDisplayId) {
+            if (displayId == mActiveDisplayId &&
+                !FlagManager::getInstance().correct_virtual_display_power_state()) {
                 mScheduler->enableSyntheticVsync(false);
             }
 
@@ -5746,13 +5770,13 @@
             if (const auto display = getActivatableDisplay()) {
                 onActiveDisplayChangedLocked(activeDisplay.get(), *display);
             } else {
-                if (FlagManager::getInstance().correct_virtual_display_power_state()) {
-                    applyOptimizationPolicy("setPhysicalDisplayPowerMode(OFF)");
-                } else {
-                    enablePowerOptimizations("setPhysicalDisplayPowerMode(OFF)");
+                if (!FlagManager::getInstance().correct_virtual_display_power_state()) {
+                    optimizeThreadScheduling("setPhysicalDisplayPowerMode(OFF)",
+                                             OptimizationPolicy::optimizeForPower);
                 }
 
-                if (currentModeNotDozeSuspend) {
+                if (currentModeNotDozeSuspend &&
+                    !FlagManager::getInstance().correct_virtual_display_power_state()) {
                     mScheduler->enableSyntheticVsync();
                 }
             }
@@ -5780,7 +5804,9 @@
                 ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
                 mVisibleRegionsDirty = true;
                 scheduleRepaint();
-                mScheduler->enableSyntheticVsync(false);
+                if (!FlagManager::getInstance().correct_virtual_display_power_state()) {
+                    mScheduler->enableSyntheticVsync(false);
+                }
             }
             constexpr bool kAllowToEnable = true;
             mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, activeMode.get());
@@ -5790,7 +5816,8 @@
         constexpr bool kDisallow = true;
         mScheduler->disableHardwareVsync(displayId, kDisallow);
 
-        if (displayId == mActiveDisplayId) {
+        if (displayId == mActiveDisplayId &&
+            !FlagManager::getInstance().correct_virtual_display_power_state()) {
             mScheduler->enableSyntheticVsync();
         }
         getHwComposer().setPowerMode(displayId, mode);
@@ -5829,43 +5856,44 @@
           to_string(displayId).c_str());
 }
 
-bool SurfaceFlinger::shouldOptimizeForPerformance() {
-    for (const auto& [_, display] : mDisplays) {
-        // Displays that are optimized for power are always powered on and should not influence
-        // whether there is an active display for the purpose of power optimization, etc. If these
-        // displays are being shown somewhere, a different (physical or virtual) display that is
-        // optimized for performance will be powered on in addition. Displays optimized for
-        // performance will change power mode, so if they are off then they are not active.
-        if (display->isPoweredOn() &&
-            display->getOptimizationPolicy() ==
-                    gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance) {
-            return true;
-        }
-    }
-    return false;
-}
+void SurfaceFlinger::optimizeThreadScheduling(
+        const char* whence, gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy) {
+    ALOGD("%s: Optimizing thread scheduling: %s", whence, to_string(optimizationPolicy));
 
-void SurfaceFlinger::enablePowerOptimizations(const char* whence) {
-    ALOGD("%s: Enabling power optimizations", whence);
-
-    setSchedAttr(false, whence);
-    setSchedFifo(false, whence);
-}
-
-void SurfaceFlinger::disablePowerOptimizations(const char* whence) {
-    ALOGD("%s: Disabling power optimizations", whence);
-
+    const bool optimizeForPerformance =
+            optimizationPolicy == gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance;
     // TODO: b/281692563 - Merge the syscalls. For now, keep uclamp in a separate syscall
     // and set it before SCHED_FIFO due to b/190237315.
-    setSchedAttr(true, whence);
-    setSchedFifo(true, whence);
+    setSchedAttr(optimizeForPerformance, whence);
+    setSchedFifo(optimizeForPerformance, whence);
 }
 
 void SurfaceFlinger::applyOptimizationPolicy(const char* whence) {
-    if (shouldOptimizeForPerformance()) {
-        disablePowerOptimizations(whence);
-    } else {
-        enablePowerOptimizations(whence);
+    using OptimizationPolicy = gui::ISurfaceComposer::OptimizationPolicy;
+
+    const bool optimizeForPerformance =
+            std::any_of(mDisplays.begin(), mDisplays.end(), [](const auto& pair) {
+                const auto& display = pair.second;
+                return display->isPoweredOn() &&
+                        display->getOptimizationPolicy() ==
+                        OptimizationPolicy::optimizeForPerformance;
+            });
+
+    optimizeThreadScheduling(whence,
+                             optimizeForPerformance ? OptimizationPolicy::optimizeForPerformance
+                                                    : OptimizationPolicy::optimizeForPower);
+
+    if (mScheduler) {
+        const bool disableSyntheticVsync =
+                std::any_of(mDisplays.begin(), mDisplays.end(), [](const auto& pair) {
+                    const auto& display = pair.second;
+                    const hal::PowerMode powerMode = display->getPowerMode();
+                    return powerMode != hal::PowerMode::OFF &&
+                            powerMode != hal::PowerMode::DOZE_SUSPEND &&
+                            display->getOptimizationPolicy() ==
+                            OptimizationPolicy::optimizeForPerformance;
+                });
+        mScheduler->enableSyntheticVsync(!disableSyntheticVsync);
     }
 }
 
@@ -6089,17 +6117,15 @@
 
     for (const auto& [token, display] : mDisplays) {
         if (display->isVirtual()) {
-            const auto displayId = display->getId();
+            const VirtualDisplayId virtualId = display->getVirtualId();
             utils::Dumper::Section section(dumper,
-                                           ftl::Concat("Virtual Display ", displayId.value).str());
+                                           ftl::Concat("Virtual Display ", virtualId.value).str());
             display->dump(dumper);
 
-            if (const auto virtualIdOpt = VirtualDisplayId::tryCast(displayId)) {
-                std::lock_guard lock(mVirtualDisplaysMutex);
-                const auto virtualSnapshotIt = mVirtualDisplays.find(virtualIdOpt.value());
-                if (virtualSnapshotIt != mVirtualDisplays.end()) {
-                    virtualSnapshotIt->second.dump(dumper);
-                }
+            std::lock_guard lock(mVirtualDisplaysMutex);
+            const auto virtualSnapshotIt = mVirtualDisplays.find(virtualId);
+            if (virtualSnapshotIt != mVirtualDisplays.end()) {
+                virtualSnapshotIt->second.dump(dumper);
             }
         }
     }
@@ -6107,7 +6133,7 @@
 
 void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
     for (const auto& [token, display] : mDisplays) {
-        const auto displayId = PhysicalDisplayId::tryCast(display->getId());
+        const auto displayId = asPhysicalDisplayId(display->getDisplayIdVariant());
         if (!displayId) {
             continue;
         }
@@ -6316,7 +6342,7 @@
 
 void SurfaceFlinger::dumpHwcLayersMinidump(std::string& result) const {
     for (const auto& [token, display] : mDisplays) {
-        const auto displayId = HalDisplayId::tryCast(display->getId());
+        const auto displayId = asHalDisplayId(display->getDisplayIdVariant());
         if (!displayId) {
             continue;
         }
@@ -7383,7 +7409,7 @@
     }
 
     wp<const DisplayDevice> displayWeak;
-    DisplayId displayId;
+    ftl::Optional<DisplayIdVariant> displayIdVariantOpt;
     ui::LayerStack layerStack;
     ui::Size reqSize(args.width, args.height);
     std::unordered_set<uint32_t> excludeLayerIds;
@@ -7399,7 +7425,7 @@
             return;
         }
         displayWeak = display;
-        displayId = display->getId();
+        displayIdVariantOpt = display->getDisplayIdVariant();
         layerStack = display->getLayerStack();
         displayIsSecure = display->isSecure();
 
@@ -7427,7 +7453,7 @@
 
     ScreenshotArgs screenshotArgs;
     screenshotArgs.captureTypeVariant = displayWeak;
-    screenshotArgs.displayId = displayId;
+    screenshotArgs.displayIdVariant = displayIdVariantOpt;
     screenshotArgs.sourceCrop = gui::aidl_utils::fromARect(captureArgs.sourceCrop);
     if (screenshotArgs.sourceCrop.isEmpty()) {
         screenshotArgs.sourceCrop = layerStackSpaceRect;
@@ -7446,6 +7472,7 @@
                                     const sp<IScreenCaptureListener>& captureListener) {
     ui::LayerStack layerStack;
     wp<const DisplayDevice> displayWeak;
+    ftl::Optional<DisplayIdVariant> displayIdVariantOpt;
     ui::Size size;
     Rect layerStackSpaceRect;
     bool displayIsSecure;
@@ -7461,6 +7488,7 @@
         }
 
         displayWeak = display;
+        displayIdVariantOpt = display->getDisplayIdVariant();
         layerStack = display->getLayerStack();
         layerStackSpaceRect = display->getLayerStackSpaceRect();
         size = display->getLayerStackSpaceRect().getSize();
@@ -7495,7 +7523,7 @@
 
     ScreenshotArgs screenshotArgs;
     screenshotArgs.captureTypeVariant = displayWeak;
-    screenshotArgs.displayId = displayId;
+    screenshotArgs.displayIdVariant = displayIdVariantOpt;
     screenshotArgs.sourceCrop = layerStackSpaceRect;
     screenshotArgs.reqSize = size;
     screenshotArgs.dataspace = static_cast<ui::Dataspace>(args.dataspace);
@@ -7987,7 +8015,7 @@
                                         .layerStack = layerStack,
                                         .sourceCrop = args.sourceCrop,
                                         .buffer = std::move(buffer),
-                                        .displayId = args.displayId,
+                                        .displayIdVariant = args.displayIdVariant,
                                         .reqBufferSize = args.reqSize,
                                         .sdrWhitePointNits = args.sdrWhitePointNits,
                                         .displayBrightnessNits = args.displayBrightnessNits,
@@ -8387,8 +8415,8 @@
     // TODO(b/255635821): Choose the pacesetter display, considering both internal and external
     // displays. For now, pick the other internal display, assuming a dual-display foldable.
     return findDisplay([this](const DisplayDevice& display) REQUIRES(mStateLock) {
-        const auto idOpt = PhysicalDisplayId::tryCast(display.getId());
-        return idOpt && *idOpt != mActiveDisplayId && display.isPoweredOn() &&
+        const auto idOpt = asPhysicalDisplayId(display.getDisplayIdVariant());
+        return idOpt.has_value() && *idOpt != mActiveDisplayId && display.isPoweredOn() &&
                 mPhysicalDisplays.get(*idOpt)
                         .transform(&PhysicalDisplay::isInternal)
                         .value_or(false);
@@ -8925,6 +8953,7 @@
     if (status == NO_ERROR) {
         // convert ui::StaticDisplayInfo to gui::StaticDisplayInfo
         outInfo->connectionType = static_cast<gui::DisplayConnectionType>(info.connectionType);
+        outInfo->port = info.port;
         outInfo->density = info.density;
         outInfo->secure = info.secure;
         outInfo->installOrientation = static_cast<gui::Rotation>(info.installOrientation);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 7568479..f61214c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -160,6 +160,7 @@
 class OutputLayer;
 
 struct CompositionRefreshArgs;
+class DisplayCreationArgsBuilder;
 } // namespace compositionengine
 
 namespace renderengine {
@@ -732,19 +733,14 @@
     void setVirtualDisplayPowerMode(const sp<DisplayDevice>& display, hal::PowerMode mode)
             REQUIRES(mStateLock, kMainThreadContext);
 
-    // Returns whether to optimize globally for performance instead of power.
-    bool shouldOptimizeForPerformance() REQUIRES(mStateLock);
-
-    // Turns on power optimizations, for example when there are no displays to be optimized for
-    // performance.
-    static void enablePowerOptimizations(const char* whence);
-
-    // Turns off power optimizations.
-    static void disablePowerOptimizations(const char* whence);
+    // Adjusts thread scheduling according to the optimization policy
+    static void optimizeThreadScheduling(
+            const char* whence, gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy);
 
     // Enables or disables power optimizations depending on whether there are displays that should
     // be optimized for performance.
-    void applyOptimizationPolicy(const char* whence) REQUIRES(mStateLock);
+    void applyOptimizationPolicy(const char* whence) REQUIRES(kMainThreadContext)
+            REQUIRES(mStateLock);
 
     // Returns the preferred mode for PhysicalDisplayId if the Scheduler has selected one for that
     // display. Falls back to the display's defaultModeId otherwise.
@@ -885,7 +881,7 @@
         std::variant<int32_t, wp<const DisplayDevice>> captureTypeVariant;
 
         // Display ID of the display the result will be on
-        std::optional<DisplayId> displayId{std::nullopt};
+        ftl::Optional<DisplayIdVariant> displayIdVariant{std::nullopt};
 
         // If true, transform is inverted from the parent layer snapshot
         bool childrenOnly{false};
@@ -1039,10 +1035,10 @@
     // region of all screens presenting this layer stack.
     void invalidateLayerStack(const ui::LayerFilter& layerFilter, const Region& dirty);
 
-    ui::LayerFilter makeLayerFilterForDisplay(DisplayId displayId, ui::LayerStack layerStack)
+    ui::LayerFilter makeLayerFilterForDisplay(DisplayIdVariant displayId, ui::LayerStack layerStack)
             REQUIRES(mStateLock) {
         return {layerStack,
-                PhysicalDisplayId::tryCast(displayId)
+                asPhysicalDisplayId(displayId)
                         .and_then(display::getPhysicalDisplay(mPhysicalDisplays))
                         .transform(&display::PhysicalDisplay::isInternal)
                         .value_or(false)};
@@ -1135,8 +1131,10 @@
     void enableHalVirtualDisplays(bool);
 
     // Virtual display lifecycle for ID generation and HAL allocation.
-    VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat, const std::string& uniqueId)
-            REQUIRES(mStateLock);
+    std::optional<VirtualDisplayIdVariant> acquireVirtualDisplay(
+            ui::Size, ui::PixelFormat, const std::string& uniqueId,
+            compositionengine::DisplayCreationArgsBuilder&) REQUIRES(mStateLock);
+
     template <typename ID>
     void acquireVirtualDisplaySnapshot(ID displayId, const std::string& uniqueId) {
         std::lock_guard lock(mVirtualDisplaysMutex);
@@ -1147,7 +1145,7 @@
         }
     }
 
-    void releaseVirtualDisplay(VirtualDisplayId);
+    void releaseVirtualDisplay(VirtualDisplayIdVariant displayId);
     void releaseVirtualDisplaySnapshot(VirtualDisplayId displayId);
 
     // Returns a display other than `mActiveDisplayId` that can be activated, if any.
@@ -1232,7 +1230,7 @@
 
     bool isHdrLayer(const frontend::LayerSnapshot& snapshot) const;
 
-    ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const
+    ui::Rotation getPhysicalDisplayOrientation(PhysicalDisplayId, bool isPrimary) const
             REQUIRES(mStateLock);
     void traverseLegacyLayers(const LayerVector::Visitor& visitor) const
             REQUIRES(kMainThreadContext);
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index b22ec66..2e8c8c1 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -18,7 +18,7 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
-//#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "TransactionCallbackInvoker"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -28,6 +28,7 @@
 #include "Utils/FenceUtils.h"
 
 #include <binder/IInterface.h>
+#include <common/FlagManager.h>
 #include <common/trace.h>
 #include <utils/RefBase.h>
 
@@ -142,8 +143,17 @@
                                                     handle->transformHint,
                                                     handle->currentMaxAcquiredBufferCount,
                                                     eventStats, handle->previousReleaseCallbackId);
+
         if (handle->bufferReleaseChannel &&
             handle->previousReleaseCallbackId != ReleaseCallbackId::INVALID_ID) {
+            if (FlagManager::getInstance().monitor_buffer_fences()) {
+                if (auto previousBuffer = handle->previousBuffer.lock()) {
+                    previousBuffer->getBuffer()
+                            ->getDependencyMonitor()
+                            .addEgress(FenceTime::makeValid(handle->previousReleaseFence),
+                                       "Txn release");
+                }
+            }
             mBufferReleases.emplace_back(handle->name, handle->bufferReleaseChannel,
                                          handle->previousReleaseCallbackId,
                                          handle->previousReleaseFence,
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 178ddbb..34f6ffc 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -25,6 +25,7 @@
 #include <ftl/future.h>
 #include <gui/BufferReleaseChannel.h>
 #include <gui/ITransactionCompletedListener.h>
+#include <renderengine/ExternalTexture.h>
 #include <ui/Fence.h>
 #include <ui/FenceResult.h>
 
@@ -55,6 +56,7 @@
     uint64_t previousFrameNumber = 0;
     ReleaseCallbackId previousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
     std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel;
+    std::weak_ptr<renderengine::ExternalTexture> previousBuffer;
 };
 
 class TransactionCallbackInvoker {
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 5ff3d82..bf10351 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -129,6 +129,7 @@
     DUMP_ACONFIG_FLAG(correct_virtual_display_power_state);
     DUMP_ACONFIG_FLAG(graphite_renderengine_preview_rollout);
     DUMP_ACONFIG_FLAG(increase_missed_frame_jank_threshold);
+    DUMP_ACONFIG_FLAG(monitor_buffer_fences);
     DUMP_ACONFIG_FLAG(refresh_rate_overlay_on_external_display);
     DUMP_ACONFIG_FLAG(vsync_predictor_recovery);
 
@@ -303,6 +304,7 @@
 FLAG_MANAGER_ACONFIG_FLAG(adpf_native_session_manager, "");
 FLAG_MANAGER_ACONFIG_FLAG(graphite_renderengine_preview_rollout, "");
 FLAG_MANAGER_ACONFIG_FLAG(increase_missed_frame_jank_threshold, "");
+FLAG_MANAGER_ACONFIG_FLAG(monitor_buffer_fences, "");
 FLAG_MANAGER_ACONFIG_FLAG(vsync_predictor_recovery, "");
 
 /// Trunk stable server (R/W) flags from outside SurfaceFlinger ///
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 419e92b..8f361ac 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -63,6 +63,7 @@
     bool correct_virtual_display_power_state() const;
     bool graphite_renderengine_preview_rollout() const;
     bool increase_missed_frame_jank_threshold() const;
+    bool monitor_buffer_fences() const;
     bool refresh_rate_overlay_on_external_display() const;
     bool vsync_predictor_recovery() const;
 
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index d8f51fe..d412a19 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -216,6 +216,13 @@
 } # local_tonemap_screenshots
 
 flag {
+  name: "monitor_buffer_fences"
+  namespace: "core_graphics"
+  description: "Monitors fences for each buffer"
+  bug: "360932099"
+} # monitor_buffer_fences
+
+flag {
   name: "no_vsyncs_on_screen_off"
   namespace: "core_graphics"
   description: "Stop vsync / Choreographer callbacks to apps when the screen is off"
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 4322af7..75182e5 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -183,7 +183,7 @@
 
 template <typename PhysicalDisplay>
 struct DisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
-    static PhysicalDisplayId get() {
+    static DisplayIdVariant get() {
         if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
             return PhysicalDisplayId::fromPort(static_cast<bool>(PhysicalDisplay::PRIMARY)
                                                        ? LEGACY_DISPLAY_TYPE_PRIMARY
@@ -199,12 +199,12 @@
 
 template <VirtualDisplayId::BaseId displayId>
 struct DisplayIdGetter<HalVirtualDisplayIdType<displayId>> {
-    static HalVirtualDisplayId get() { return HalVirtualDisplayId(displayId); }
+    static DisplayIdVariant get() { return HalVirtualDisplayId(displayId); }
 };
 
 template <>
 struct DisplayIdGetter<GpuVirtualDisplayIdType> {
-    static GpuVirtualDisplayId get() { return GpuVirtualDisplayId(0); }
+    static DisplayIdVariant get() { return GpuVirtualDisplayId(0); }
 };
 
 template <typename>
@@ -374,9 +374,8 @@
     // Called by tests to inject a HWC display setup
     template <hal::PowerMode kPowerMode = hal::PowerMode::ON>
     static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
-        const auto displayId = DisplayVariant::DISPLAY_ID::get();
-        ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
-        TestableSurfaceFlinger::FakeHwcDisplayInjector(displayId, HWC_DISPLAY_TYPE,
+        TestableSurfaceFlinger::FakeHwcDisplayInjector(DisplayVariant::DISPLAY_ID::get(),
+                                                       HWC_DISPLAY_TYPE,
                                                        static_cast<bool>(DisplayVariant::PRIMARY))
                 .setHwcDisplayId(HWC_DISPLAY_ID)
                 .setResolution(DisplayVariant::RESOLUTION)
@@ -663,9 +662,8 @@
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
 
-        const auto displayId = Base::DISPLAY_ID::get();
         auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
-                                     .setId(displayId)
+                                     .setId(Base::DISPLAY_ID::get())
                                      .setPixels(Base::RESOLUTION)
                                      .setIsSecure(static_cast<bool>(Base::SECURE))
                                      .setPowerAdvisor(&test->mPowerAdvisor)
@@ -678,7 +676,12 @@
                                                        ceDisplayArgs);
 
         // Insert display data so that the HWC thinks it created the virtual display.
-        test->mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+        const auto ceDisplayIdVar = compositionDisplay->getDisplayIdVariant();
+        LOG_ALWAYS_FATAL_IF(!ceDisplayIdVar);
+        EXPECT_EQ(*ceDisplayIdVar, Base::DISPLAY_ID::get());
+        const auto displayId = asHalDisplayId(*ceDisplayIdVar);
+        LOG_ALWAYS_FATAL_IF(!displayId);
+        test->mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
 
         return compositionDisplay;
     }
@@ -816,8 +819,9 @@
 
 inline DisplayModePtr createDisplayMode(DisplayModeId modeId, Fps refreshRate, int32_t group = 0,
                                         ui::Size resolution = ui::Size(1920, 1080)) {
-    return mock::createDisplayMode(modeId, refreshRate, group, resolution,
-                                   PrimaryDisplayVariant::DISPLAY_ID::get());
+    const auto physicalDisplayId = asPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get());
+    LOG_ALWAYS_FATAL_IF(!physicalDisplayId);
+    return mock::createDisplayMode(modeId, refreshRate, group, resolution, *physicalDisplayId);
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h b/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h
index 90e716f..edcb639 100644
--- a/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h
+++ b/services/surfaceflinger/tests/unittests/DualDisplayTransactionTest.h
@@ -48,8 +48,10 @@
         }
     }
 
-    static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get();
-    static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get();
+    static inline PhysicalDisplayId kInnerDisplayId =
+            asPhysicalDisplayId(InnerDisplayVariant::DISPLAY_ID::get()).value();
+    static inline PhysicalDisplayId kOuterDisplayId =
+            asPhysicalDisplayId(OuterDisplayVariant::DISPLAY_ID::get()).value();
 
     sp<DisplayDevice> mInnerDisplay, mOuterDisplay;
 };
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index aa5b786..aa48c1b 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -50,9 +50,9 @@
         EXPECT_EQ(display.requestedRefreshRate, Fps::fromValue(requestedRefreshRate));
         EXPECT_EQ(name.c_str(), display.displayName);
 
-        const VirtualDisplayId vid = GpuVirtualDisplayId(baseId);
         sp<DisplayDevice> device =
-                mFlinger.createVirtualDisplayDevice(displayToken, vid, requestedRefreshRate);
+                mFlinger.createVirtualDisplayDevice(displayToken, GpuVirtualDisplayId(baseId),
+                                                    requestedRefreshRate);
 
         EXPECT_TRUE(device->isVirtual());
         device->adjustRefreshRate(Fps::fromValue(pacesetterDisplayRefreshRate));
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
index 1335640..eac5a8e 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -68,13 +68,9 @@
 
 template <typename Case, bool connected>
 void DisplayTransactionCommitTest::expectHotplugReceived(mock::EventThread* eventThread) {
-    const auto convert = [](auto physicalDisplayId) {
-        return std::make_optional(DisplayId{physicalDisplayId});
-    };
-
-    EXPECT_CALL(*eventThread,
-                onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected))
-            .Times(1);
+    const auto physicalDisplayId = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
+    ASSERT_TRUE(physicalDisplayId);
+    EXPECT_CALL(*eventThread, onHotplugReceived(*physicalDisplayId, connected)).Times(1);
 }
 
 template <typename Case>
@@ -111,7 +107,7 @@
 
     std::optional<DisplayDeviceState::Physical> expectedPhysical;
     if (Case::Display::CONNECTION_TYPE::value) {
-        const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
+        const auto displayId = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
@@ -137,10 +133,10 @@
     EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
     // SF should have a display token.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+    const auto displayIdOpt = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
+    ASSERT_TRUE(displayIdOpt);
 
-    const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId);
+    const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(*displayIdOpt);
     ASSERT_TRUE(displayOpt);
 
     const auto& display = displayOpt->get();
@@ -246,9 +242,9 @@
     EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
     // SF should not have a PhysicalDisplay.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-    ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId));
+    const auto physicalDisplayIdOpt = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
+    ASSERT_TRUE(physicalDisplayIdOpt);
+    ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(*physicalDisplayIdOpt));
 
     // The existing token should have been removed.
     verifyDisplayIsNotConnected(existing.token());
@@ -356,9 +352,10 @@
                 EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
                 // SF should not have a PhysicalDisplay.
-                const auto displayId = Case::Display::DISPLAY_ID::get();
-                ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-                ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId));
+                const auto physicalDisplayIdOpt =
+                        asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
+                ASSERT_TRUE(physicalDisplayIdOpt);
+                ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(*physicalDisplayIdOpt));
             }(),
             testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
 }
@@ -400,10 +397,12 @@
 
                 // The existing token should have been removed.
                 verifyDisplayIsNotConnected(existing.token());
-                const auto displayId = Case::Display::DISPLAY_ID::get();
-                ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+                const auto physicalDisplayIdOpt =
+                        asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
+                ASSERT_TRUE(physicalDisplayIdOpt);
 
-                const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId);
+                const auto displayOpt =
+                        mFlinger.mutablePhysicalDisplays().get(*physicalDisplayIdOpt);
                 ASSERT_TRUE(displayOpt);
                 EXPECT_NE(existing.token(), displayOpt->get().token());
 
@@ -540,9 +539,9 @@
     // Preconditions
 
     // A virtual display is set up but is removed from the current state.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
-    mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+    const auto displayId = asHalDisplayId(Case::Display::DISPLAY_ID::get());
+    ASSERT_TRUE(displayId);
+    mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
     Case::Display::injectHwcDisplay(this);
     auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
     existing.inject();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index 49972b0..b8b1b95 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -90,7 +90,9 @@
     mFlinger.configure();
 
     EXPECT_TRUE(hasPhysicalHwcDisplay(PrimaryDisplay::HWC_DISPLAY_ID));
-    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(PrimaryDisplay::DISPLAY_ID::get()));
+    const auto primaryDisplayId = asPhysicalDisplayId(PrimaryDisplay::DISPLAY_ID::get());
+    ASSERT_TRUE(primaryDisplayId);
+    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(*primaryDisplayId));
     const auto primaryDisplayIdOpt =
             mFlinger.getHwComposer().toPhysicalDisplayId(PrimaryDisplay::HWC_DISPLAY_ID);
     ASSERT_TRUE(primaryDisplayIdOpt.has_value());
@@ -98,13 +100,15 @@
             mFlinger.physicalDisplays().get(primaryDisplayIdOpt.value());
     ASSERT_TRUE(primaryPhysicalDisplayOpt.has_value());
     const auto primaryDisplaySnapshotRef = primaryPhysicalDisplayOpt->get().snapshotRef();
-    EXPECT_EQ(PrimaryDisplay::DISPLAY_ID::get(), primaryDisplaySnapshotRef.get().displayId());
+    EXPECT_EQ(*primaryDisplayId, primaryDisplaySnapshotRef.get().displayId());
     EXPECT_EQ(PrimaryDisplay::PORT::value, primaryDisplaySnapshotRef.get().port());
     EXPECT_EQ(PrimaryDisplay::CONNECTION_TYPE::value,
               primaryDisplaySnapshotRef.get().connectionType());
 
     EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
-    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(ExternalDisplay::DISPLAY_ID::get()));
+    const auto externalDisplayId = asPhysicalDisplayId(ExternalDisplay::DISPLAY_ID::get());
+    ASSERT_TRUE(externalDisplayId);
+    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(*externalDisplayId));
     const auto externalDisplayIdOpt =
             mFlinger.getHwComposer().toPhysicalDisplayId(ExternalDisplay::HWC_DISPLAY_ID);
     ASSERT_TRUE(externalDisplayIdOpt.has_value());
@@ -112,7 +116,7 @@
             mFlinger.physicalDisplays().get(externalDisplayIdOpt.value());
     ASSERT_TRUE(externalPhysicalDisplayOpt.has_value());
     const auto externalDisplaySnapshotRef = externalPhysicalDisplayOpt->get().snapshotRef();
-    EXPECT_EQ(ExternalDisplay::DISPLAY_ID::get(), externalDisplaySnapshotRef.get().displayId());
+    EXPECT_EQ(*externalDisplayId, externalDisplaySnapshotRef.get().displayId());
     EXPECT_EQ(ExternalDisplay::PORT::value, externalDisplaySnapshotRef.get().port());
     EXPECT_EQ(ExternalDisplay::CONNECTION_TYPE::value,
               externalDisplaySnapshotRef.get().connectionType());
@@ -154,8 +158,8 @@
     constexpr PhysicalDisplayId primaryInternalDisplayId =
             PhysicalDisplayId::fromPort(primaryInternalDisplayPort);
     EXPECT_TRUE(hasPhysicalHwcDisplay(PrimaryDisplay::HWC_DISPLAY_ID));
-    ASSERT_EQ(primaryInternalDisplayId, PrimaryDisplay::DISPLAY_ID::get());
-    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(PrimaryDisplay::DISPLAY_ID::get()));
+    ASSERT_EQ(primaryInternalDisplayId, asPhysicalDisplayId(PrimaryDisplay::DISPLAY_ID::get()));
+    EXPECT_TRUE(mFlinger.getHwComposer().isConnected(primaryInternalDisplayId));
     const auto primaryDisplayIdOpt =
             mFlinger.getHwComposer().toPhysicalDisplayId(PrimaryDisplay::HWC_DISPLAY_ID);
     ASSERT_TRUE(primaryDisplayIdOpt.has_value());
@@ -220,7 +224,9 @@
     // The display should be scheduled for removal during the next commit. At this point, it should
     // still exist but be marked as disconnected.
     EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
-    EXPECT_FALSE(mFlinger.getHwComposer().isConnected(ExternalDisplay::DISPLAY_ID::get()));
+    const auto externalDisplayId = asPhysicalDisplayId(ExternalDisplay::DISPLAY_ID::get());
+    ASSERT_TRUE(externalDisplayId);
+    EXPECT_FALSE(mFlinger.getHwComposer().isConnected(*externalDisplayId));
 }
 
 TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp
index a1e37ff..2332bf6 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp
@@ -17,6 +17,7 @@
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
+#include <android_companion_virtualdevice_flags.h>
 #include <com_android_graphics_surfaceflinger_flags.h>
 #include <common/test/FlagUtils.h>
 #include "DisplayTransactionTestHelpers.h"
@@ -78,11 +79,19 @@
 struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
     static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, true)).Times(1);
+        setupDisableSyntheticVsyncCallExpectations(test);
+    }
+
+    static void setupDisableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0);
     }
 
     static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, false)).Times(1);
+        setupEnableSyntheticVsyncCallExpectations(test);
+    }
+
+    static void setupEnableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0);
     }
 };
@@ -91,12 +100,20 @@
     static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) {
         // Expect to enable hardware VSYNC and disable synthetic VSYNC.
         EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, true)).Times(1);
+        setupDisableSyntheticVsyncCallExpectations(test);
+    }
+
+    static void setupDisableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(false)).Times(1);
     }
 
     static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) {
         // Expect to disable hardware VSYNC and enable synthetic VSYNC.
         EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, false)).Times(1);
+        setupEnableSyntheticVsyncCallExpectations(test);
+    }
+
+    static void setupEnableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(true)).Times(1);
     }
 };
@@ -151,7 +168,7 @@
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
-        Case::EventThread::setupVsyncNoCallExpectations(test);
+        Case::EventThread::setupEnableSyntheticVsyncCallExpectations(test);
         Case::setupRepaintEverythingCallExpectations(test);
     }
 
@@ -176,7 +193,7 @@
       : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupVsyncNoCallExpectations(test);
+        Case::EventThread::setupEnableSyntheticVsyncCallExpectations(test);
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
     }
 
@@ -188,7 +205,7 @@
 struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupVsyncNoCallExpectations(test);
+        Case::EventThread::setupDisableSyntheticVsyncCallExpectations(test);
         Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
     }
 };
@@ -206,7 +223,7 @@
 struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupVsyncNoCallExpectations(test);
+        Case::EventThread::setupDisableSyntheticVsyncCallExpectations(test);
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
     }
 };
@@ -234,7 +251,7 @@
       : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupVsyncNoCallExpectations(test);
+        Case::EventThread::setupDisableSyntheticVsyncCallExpectations(test);
         Case::setupNoComposerPowerModeCallExpectations(test);
     }
 };
@@ -335,11 +352,13 @@
     // --------------------------------------------------------------------
     // Preconditions
 
+    SET_FLAG_FOR_TEST(android::companion::virtualdevice::flags::correct_virtual_display_power_state,
+                      true);
+
     Case::Doze::setupComposerCallExpectations(this);
     auto display =
             Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
-    auto displayId = display->getId();
-    if (auto physicalDisplayId = PhysicalDisplayId::tryCast(displayId)) {
+    if (auto physicalDisplayId = asPhysicalDisplayId(display->getDisplayIdVariant())) {
         Case::setInitialHwVsyncEnabled(this, *physicalDisplayId,
                                        PowerModeInitialVSyncEnabled<
                                                Case::Transition::INITIAL_POWER_MODE>::value);
@@ -393,9 +412,9 @@
     // Preconditions
 
     // Insert display data so that the HWC thinks it created the virtual display.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
-    mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+    const auto displayId = asHalDisplayId(Case::Display::DISPLAY_ID::get());
+    ASSERT_TRUE(displayId);
+    ASSERT_TRUE(mFlinger.mutableHwcDisplayData().try_emplace(*displayId).second);
 
     // A virtual display device is set up
     Case::Display::injectHwcDisplay(this);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index cd554ea..23e73de 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -235,7 +235,7 @@
 
     constexpr auto kConnectionTypeOpt = Case::Display::CONNECTION_TYPE::value;
     if constexpr (kConnectionTypeOpt) {
-        const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
+        const auto displayId = asPhysicalDisplayId(Case::Display::DISPLAY_ID::get());
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
@@ -282,7 +282,7 @@
     // Postconditions
 
     ASSERT_NE(nullptr, device);
-    EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
+    EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getDisplayIdVariant());
     EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
     EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
     EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index f0e2361..13c32bd 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -481,7 +481,7 @@
 
         SurfaceFlinger::ScreenshotArgs screenshotArgs;
         screenshotArgs.captureTypeVariant = display;
-        screenshotArgs.displayId = std::nullopt;
+        screenshotArgs.displayIdVariant = std::nullopt;
         screenshotArgs.sourceCrop = sourceCrop;
         screenshotArgs.reqSize = sourceCrop.getSize();
         screenshotArgs.dataspace = dataspace;
@@ -573,7 +573,7 @@
     }
 
     sp<DisplayDevice> createVirtualDisplayDevice(const sp<IBinder> displayToken,
-                                                 VirtualDisplayId displayId,
+                                                 GpuVirtualDisplayId displayId,
                                                  float requestedRefreshRate) {
         constexpr ui::Size kResolution = {1080, 1920};
         auto compositionDisplay = compositionengine::impl::
@@ -817,9 +817,11 @@
         static constexpr int32_t DEFAULT_DPI = 320;
         static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0;
 
-        FakeHwcDisplayInjector(HalDisplayId displayId, hal::DisplayType hwcDisplayType,
+        FakeHwcDisplayInjector(DisplayIdVariant displayIdVariant, hal::DisplayType hwcDisplayType,
                                bool isPrimary)
-              : mDisplayId(displayId), mHwcDisplayType(hwcDisplayType), mIsPrimary(isPrimary) {}
+              : mDisplayIdVariant(displayIdVariant),
+                mHwcDisplayType(hwcDisplayType),
+                mIsPrimary(isPrimary) {}
 
         auto& setHwcDisplayId(hal::HWDisplayId displayId) {
             mHwcDisplayId = displayId;
@@ -884,7 +886,9 @@
 
             display->setPowerMode(mPowerMode);
 
-            flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
+            const auto halDisplayId = asHalDisplayId(mDisplayIdVariant);
+            ASSERT_TRUE(halDisplayId);
+            flinger->mutableHwcDisplayData()[*halDisplayId].hwcDisplay = std::move(display);
 
             EXPECT_CALL(*composer, getDisplayConfigs(mHwcDisplayId, _))
                     .WillRepeatedly(
@@ -922,9 +926,10 @@
                             DoAll(SetArgPointee<3>(mConfigGroup), Return(hal::Error::NONE)));
 
             if (mHwcDisplayType == hal::DisplayType::PHYSICAL) {
-                const auto physicalId = PhysicalDisplayId::tryCast(mDisplayId);
-                LOG_ALWAYS_FATAL_IF(!physicalId);
-                flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId);
+                const auto physicalDisplayId = asPhysicalDisplayId(mDisplayIdVariant);
+                ASSERT_TRUE(physicalDisplayId);
+                flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId,
+                                                                  *physicalDisplayId);
                 if (mIsPrimary) {
                     flinger->mutablePrimaryHwcDisplayId() = mHwcDisplayId;
                 } else {
@@ -937,7 +942,7 @@
         }
 
     private:
-        const HalDisplayId mDisplayId;
+        const DisplayIdVariant mDisplayIdVariant;
         const hal::DisplayType mHwcDisplayType;
         const bool mIsPrimary;
 
@@ -973,8 +978,8 @@
         sp<IBinder> token() const { return mDisplayToken; }
 
         auto physicalDisplay() const {
-            return ftl::Optional(mCreationArgs.compositionDisplay->getDisplayId())
-                    .and_then(&PhysicalDisplayId::tryCast)
+            return mCreationArgs.compositionDisplay->getDisplayIdVariant()
+                    .and_then(asPhysicalDisplayId)
                     .and_then(display::getPhysicalDisplay(mFlinger.physicalDisplays()));
         }
 
@@ -1072,7 +1077,9 @@
             DisplayDeviceState state;
             state.isSecure = mCreationArgs.isSecure;
 
-            if (const auto physicalId = PhysicalDisplayId::tryCast(*displayId)) {
+            if (const auto physicalId =
+                        mCreationArgs.compositionDisplay->getDisplayIdVariant().and_then(
+                                asPhysicalDisplayId)) {
                 LOG_ALWAYS_FATAL_IF(!mConnectionType);
                 LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);