Merge "Statically link missing libary to SurfaceFlinger_test"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 5db44c7..307e21c 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -53,6 +53,9 @@
         },
         {
           "include-filter": "*RelativeZTest.*"
+        },
+        {
+          "include-filter": "*RefreshRateOverlayTest.*"
         }
       ]
     },
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index c8277fe..91d5524 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1350,7 +1350,7 @@
 static void DumpHals(int out_fd = STDOUT_FILENO) {
     if (!ds.IsZipping()) {
         RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"},
-                   CommandOptions::WithTimeout(10).AsRootIfAvailable().Build(),
+                   CommandOptions::WithTimeout(60).AsRootIfAvailable().Build(),
                    false, out_fd);
         return;
     }
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index c8355e2..7aac7da 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -373,7 +373,6 @@
     outReturn->clear();
 
     for (const std::string& instance : allInstances) {
-        // TODO(b/169275998): allow checking policy only once for the interface
         if (mAccess->canFind(ctx, interface + "/" + instance)) {
             outReturn->push_back(instance);
         }
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 7372022..36097d6 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -17,6 +17,7 @@
 #ifndef _UI_INPUT_WINDOW_H
 #define _UI_INPUT_WINDOW_H
 
+#include <android/os/TouchOcclusionMode.h>
 #include <binder/Parcel.h>
 #include <binder/Parcelable.h>
 #include <input/Flags.h>
@@ -30,6 +31,8 @@
 
 #include "InputApplication.h"
 
+using android::os::TouchOcclusionMode;
+
 namespace android {
 
 /*
@@ -158,6 +161,10 @@
     // in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis.
     float globalScaleFactor = 1.0f;
 
+    // The opacity of this window, from 0.0 to 1.0 (inclusive).
+    // An alpha of 1.0 means fully opaque and 0.0 means fully transparent.
+    float alpha;
+
     // Transform applied to individual windows.
     ui::Transform transform;
 
@@ -176,8 +183,10 @@
      * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
      */
     bool trustedOverlay = false;
+    TouchOcclusionMode touchOcclusionMode = TouchOcclusionMode::BLOCK_UNTRUSTED;
     int32_t ownerPid = -1;
     int32_t ownerUid = -1;
+    std::string packageName;
     Flags<Feature> inputFeatures;
     int32_t displayId = ADISPLAY_ID_NONE;
     int32_t portalToDisplayId = ADISPLAY_ID_NONE;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 06f6249..ddd9f9b 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -520,14 +520,19 @@
 // Write RPC headers.  (previously just the interface token)
 status_t Parcel::writeInterfaceToken(const String16& interface)
 {
+    return writeInterfaceToken(interface.string(), interface.size());
+}
+
+status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) {
     const IPCThreadState* threadState = IPCThreadState::self();
     writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
     updateWorkSourceRequestHeaderPosition();
     writeInt32(threadState->shouldPropagateWorkSource() ?
             threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource);
     writeInt32(kHeader);
+
     // currently the interface identification token is just its name as a string
-    return writeString16(interface);
+    return writeString16(str, len);
 }
 
 bool Parcel::replaceCallingWorkSourceUid(uid_t uid)
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index b6cfb8e..fbfd6c5 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -86,6 +86,7 @@
 
     // Writes the RPC header.
     status_t            writeInterfaceToken(const String16& interface);
+    status_t            writeInterfaceToken(const char16_t* str, size_t len);
 
     // Parses the RPC header, returning true if the interface name
     // in the header matches the expected interface from the caller.
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index ca03397..4610ba9 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -74,12 +74,12 @@
 AIBinder::~AIBinder() {}
 
 std::optional<bool> AIBinder::associateClassInternal(const AIBinder_Class* clazz,
-                                                     const String8& newDescriptor, bool set) {
+                                                     const String16& newDescriptor, bool set) {
     std::lock_guard<std::mutex> lock(mClazzMutex);
     if (mClazz == clazz) return true;
 
     if (mClazz != nullptr) {
-        String8 currentDescriptor(mClazz->getInterfaceDescriptor());
+        const String16& currentDescriptor = mClazz->getInterfaceDescriptor();
         if (newDescriptor == currentDescriptor) {
             LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor
                        << "' match during associateClass, but they are different class objects. "
@@ -88,8 +88,7 @@
             LOG(ERROR) << __func__
                        << ": Class cannot be associated on object which already has a class. "
                           "Trying to associate to '"
-                       << newDescriptor.c_str() << "' but already set to '"
-                       << currentDescriptor.c_str() << "'.";
+                       << newDescriptor << "' but already set to '" << currentDescriptor << "'.";
         }
 
         // always a failure because we know mClazz != clazz
@@ -99,6 +98,7 @@
     if (set) {
         // if this is a local object, it's not one known to libbinder_ndk
         mClazz = clazz;
+        return true;
     }
 
     return {};
@@ -107,27 +107,27 @@
 bool AIBinder::associateClass(const AIBinder_Class* clazz) {
     if (clazz == nullptr) return false;
 
-    String8 newDescriptor(clazz->getInterfaceDescriptor());
+    const String16& newDescriptor = clazz->getInterfaceDescriptor();
 
     auto result = associateClassInternal(clazz, newDescriptor, false);
     if (result.has_value()) return *result;
 
     CHECK(asABpBinder() != nullptr);  // ABBinder always has a descriptor
 
-    String8 descriptor(getBinder()->getInterfaceDescriptor());
+    const String16& descriptor = getBinder()->getInterfaceDescriptor();
     if (descriptor != newDescriptor) {
         if (getBinder()->isBinderAlive()) {
-            LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor.c_str()
-                       << "' but descriptor is actually '" << descriptor.c_str() << "'.";
+            LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor
+                       << "' but descriptor is actually '" << descriptor << "'.";
         } else {
             // b/155793159
-            LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor.c_str()
+            LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor
                        << "' to dead binder.";
         }
         return false;
     }
 
-    return associateClassInternal(clazz, newDescriptor, true).value_or(true);
+    return associateClassInternal(clazz, newDescriptor, true).value();
 }
 
 ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData)
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index bf27046..0fa47c6 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -54,7 +54,7 @@
 
    private:
     std::optional<bool> associateClassInternal(const AIBinder_Class* clazz,
-                                               const ::android::String8& newDescriptor, bool set);
+                                               const ::android::String16& newDescriptor, bool set);
 
     // AIBinder instance is instance of this class for a local object. In order to transact on a
     // remote object, this also must be set for simplicity (although right now, only the
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 439b019..18877af 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -44,9 +44,14 @@
 class SpAIBinder {
    public:
     /**
+     * Default constructor.
+     */
+    SpAIBinder() : mBinder(nullptr) {}
+
+    /**
      * Takes ownership of one strong refcount of binder.
      */
-    explicit SpAIBinder(AIBinder* binder = nullptr) : mBinder(binder) {}
+    explicit SpAIBinder(AIBinder* binder) : mBinder(binder) {}
 
     /**
      * Convenience operator for implicitly constructing an SpAIBinder from nullptr. This is not
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index cce434a..87f7972 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -18,7 +18,7 @@
 #define LOG_TAG "BLASTBufferQueue"
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
-// #define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 
 #include <gui/BLASTBufferQueue.h>
 #include <gui/BufferItemConsumer.h>
@@ -252,8 +252,8 @@
     }
 
     if (rejectBuffer(bufferItem)) {
-        BQA_LOGE("rejecting buffer: configured width=%d, height=%d, buffer{w=%d, h=%d}", mWidth,
-                 mHeight, buffer->getWidth(), buffer->getHeight());
+        BQA_LOGE("rejecting buffer:configured size=%dx%d, buffer{size=%dx%d transform=%d}", mWidth,
+                 mHeight, buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform);
         mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
         return;
     }
@@ -330,7 +330,15 @@
         return false;
     }
 
+    uint32_t bufWidth = item.mGraphicBuffer->getWidth();
+    uint32_t bufHeight = item.mGraphicBuffer->getHeight();
+
+    // Take the buffer's orientation into account
+    if (item.mTransform & ui::Transform::ROT_90) {
+        std::swap(bufWidth, bufHeight);
+    }
+
     // reject buffers if the buffer size doesn't match.
-    return item.mGraphicBuffer->getWidth() != mWidth || item.mGraphicBuffer->getHeight() != mHeight;
+    return bufWidth != mWidth || bufHeight != mHeight;
 }
 } // namespace android
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index a814362..5e7a7ec 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -49,13 +49,13 @@
 
     status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
                            uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata,
-                           sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, int32_t* outId,
-                           uint32_t* outTransformHint) override {
+                           sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
+                           int32_t* outLayerId, uint32_t* outTransformHint) override {
         return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
                                                                             name, width, height,
                                                                             format, flags, parent,
                                                                             std::move(metadata),
-                                                                            handle, gbp, outId,
+                                                                            handle, gbp, outLayerId,
                                                                             outTransformHint);
     }
 
@@ -63,14 +63,14 @@
                                      PixelFormat format, uint32_t flags,
                                      const sp<IGraphicBufferProducer>& parent,
                                      LayerMetadata metadata, sp<IBinder>* handle,
-                                     sp<IGraphicBufferProducer>* gbp, int32_t* outId,
+                                     sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
                                      uint32_t* outTransformHint) override {
         return callRemote<decltype(
                 &ISurfaceComposerClient::createWithSurfaceParent)>(Tag::CREATE_WITH_SURFACE_PARENT,
                                                                    name, width, height, format,
                                                                    flags, parent,
                                                                    std::move(metadata), handle, gbp,
-                                                                   outId, outTransformHint);
+                                                                   outLayerId, outTransformHint);
     }
 
     status_t clearLayerFrameStats(const sp<IBinder>& handle) const override {
@@ -86,10 +86,10 @@
     }
 
     status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
-                           int32_t* outId) override {
+                           int32_t* outLayerId) override {
         return callRemote<decltype(&ISurfaceComposerClient::mirrorSurface)>(Tag::MIRROR_SURFACE,
                                                                             mirrorFromHandle,
-                                                                            outHandle, outId);
+                                                                            outHandle, outLayerId);
     }
 };
 
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index f3fcebe..4a92f53 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -58,7 +58,7 @@
     virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
                                    uint32_t flags, const sp<IBinder>& parent,
                                    LayerMetadata metadata, sp<IBinder>* handle,
-                                   sp<IGraphicBufferProducer>* gbp, int32_t* outId,
+                                   sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
                                    uint32_t* outTransformHint) = 0;
 
     /*
@@ -68,7 +68,7 @@
                                              PixelFormat format, uint32_t flags,
                                              const sp<IGraphicBufferProducer>& parent,
                                              LayerMetadata metadata, sp<IBinder>* handle,
-                                             sp<IGraphicBufferProducer>* gbp, int32_t* outId,
+                                             sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
                                              uint32_t* outTransformHint) = 0;
 
     /*
@@ -82,7 +82,7 @@
     virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0;
 
     virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
-                                   int32_t* outId) = 0;
+                                   int32_t* outLayerId) = 0;
 };
 
 class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> {
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 35fff0a..9299721 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -490,8 +490,8 @@
         IGraphicBufferProducer::QueueBufferOutput qbOutput;
         IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
                                                        Rect(bufWidth, bufHeight),
-                                                       NATIVE_WINDOW_SCALING_MODE_FREEZE, tr,
-                                                       Fence::NO_FENCE);
+                                                       NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
+                                                       tr, Fence::NO_FENCE);
         igbProducer->queueBuffer(slot, input, &qbOutput);
         ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
 
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 8f575a8..b442700 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -18,6 +18,8 @@
     name: "inputconstants_aidl",
     srcs: [
         "android/os/IInputConstants.aidl",
+        "android/os/TouchOcclusionMode.aidl",
+        "android/os/BlockUntrustedTouchesMode.aidl",
     ],
 }
 
@@ -71,10 +73,14 @@
                 "android/FocusRequest.aidl",
                 "android/InputApplicationInfo.aidl",
                 "android/os/IInputConstants.aidl",
+                "android/os/TouchOcclusionMode.aidl",
+                "android/os/BlockUntrustedTouchesMode.aidl",
                 "android/os/IInputFlinger.aidl",
                 "android/os/ISetInputWindowsListener.aidl",
             ],
 
+            export_shared_lib_headers: ["libbinder"],
+
             shared_libs: [
                 "libutils",
                 "libbinder",
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index dee240c..c0aa2e2 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -348,6 +348,9 @@
     DEFINE_AXIS(SCROLL), \
     DEFINE_AXIS(RELATIVE_X), \
     DEFINE_AXIS(RELATIVE_Y), \
+    {"RESERVED_29", 29}, \
+    {"RESERVED_30", 30}, \
+    {"RESERVED_31", 31}, \
     DEFINE_AXIS(GENERIC_1), \
     DEFINE_AXIS(GENERIC_2), \
     DEFINE_AXIS(GENERIC_3), \
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 885dc9b..8546bbb 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -59,10 +59,11 @@
             info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
             info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
             info.visible == visible && info.trustedOverlay == trustedOverlay &&
-            info.focusable == focusable && info.hasWallpaper == hasWallpaper &&
-            info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
-            info.inputFeatures == inputFeatures && info.displayId == displayId &&
-            info.portalToDisplayId == portalToDisplayId &&
+            info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode &&
+            info.hasWallpaper == hasWallpaper && info.paused == paused &&
+            info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
+            info.packageName == packageName && info.inputFeatures == inputFeatures &&
+            info.displayId == displayId && info.portalToDisplayId == portalToDisplayId &&
             info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
             info.applicationInfo == applicationInfo;
 }
@@ -91,6 +92,7 @@
         parcel->writeInt32(frameBottom) ?:
         parcel->writeInt32(surfaceInset) ?:
         parcel->writeFloat(globalScaleFactor) ?:
+        parcel->writeFloat(alpha) ?:
         parcel->writeFloat(transform.dsdx()) ?:
         parcel->writeFloat(transform.dtdx()) ?:
         parcel->writeFloat(transform.tx()) ?:
@@ -102,8 +104,10 @@
         parcel->writeBool(hasWallpaper) ?:
         parcel->writeBool(paused) ?:
         parcel->writeBool(trustedOverlay) ?:
+        parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?:
         parcel->writeInt32(ownerPid) ?:
         parcel->writeInt32(ownerUid) ?:
+        parcel->writeUtf8AsUtf16(packageName) ?:
         parcel->writeInt32(inputFeatures.get()) ?:
         parcel->writeInt32(displayId) ?:
         parcel->writeInt32(portalToDisplayId) ?:
@@ -134,6 +138,7 @@
     flags = Flags<Flag>(parcel->readInt32());
     type = static_cast<Type>(parcel->readInt32());
     float dsdx, dtdx, tx, dtdy, dsdy, ty;
+    int32_t touchOcclusionModeInt;
     // clang-format off
     status = parcel->readInt32(&frameLeft) ?:
         parcel->readInt32(&frameTop) ?:
@@ -141,6 +146,7 @@
         parcel->readInt32(&frameBottom) ?:
         parcel->readInt32(&surfaceInset) ?:
         parcel->readFloat(&globalScaleFactor) ?:
+        parcel->readFloat(&alpha) ?:
         parcel->readFloat(&dsdx) ?:
         parcel->readFloat(&dtdx) ?:
         parcel->readFloat(&tx) ?:
@@ -152,14 +158,18 @@
         parcel->readBool(&hasWallpaper) ?:
         parcel->readBool(&paused) ?:
         parcel->readBool(&trustedOverlay) ?:
+        parcel->readInt32(&touchOcclusionModeInt) ?:
         parcel->readInt32(&ownerPid) ?:
-        parcel->readInt32(&ownerUid);
+        parcel->readInt32(&ownerUid) ?:
+        parcel->readUtf8FromUtf16(&packageName);
     // clang-format on
 
     if (status != OK) {
         return status;
     }
 
+    touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
+
     inputFeatures = Flags<Feature>(parcel->readInt32());
     status = parcel->readInt32(&displayId) ?:
         parcel->readInt32(&portalToDisplayId) ?:
diff --git a/libs/input/android/os/BlockUntrustedTouchesMode.aidl b/libs/input/android/os/BlockUntrustedTouchesMode.aidl
new file mode 100644
index 0000000..9504e99
--- /dev/null
+++ b/libs/input/android/os/BlockUntrustedTouchesMode.aidl
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 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.
+ */
+
+package android.os;
+
+
+/**
+  * Block untrusted touches feature mode.
+  *
+  * @hide
+  */
+@Backing(type="int")
+enum BlockUntrustedTouchesMode {
+    /** Feature is off. */
+    DISABLED,
+
+    /** Untrusted touches are flagged but not blocked. */
+    PERMISSIVE,
+
+    /** Untrusted touches are blocked. */
+    BLOCK
+}
diff --git a/libs/input/android/os/TouchOcclusionMode.aidl b/libs/input/android/os/TouchOcclusionMode.aidl
new file mode 100644
index 0000000..106f159
--- /dev/null
+++ b/libs/input/android/os/TouchOcclusionMode.aidl
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 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.
+ */
+
+package android.os;
+
+
+/**
+  * Touch occlusion modes: These modes represent how windows are taken into
+  * consideration in order to decide whether to block obscured touches or
+  * not.
+  *
+  * @hide
+  */
+@Backing(type="int")
+enum TouchOcclusionMode {
+    /**
+      * Touches that pass through this window will be blocked if they are
+      * consumed by a different UID and this window is not trusted.
+      */
+    BLOCK_UNTRUSTED,
+
+    /**
+      * The window's opacity will be taken into consideration for touch
+      * occlusion rules if the touch passes through it and the window is not
+      * trusted.
+      */
+    USE_OPACITY,
+
+    /**
+      * The window won't count for touch occlusion rules if the touch passes
+      * through it.
+      */
+    ALLOW
+}
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 65a7761..c18a17f 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -53,13 +53,16 @@
     i.frameBottom = 19;
     i.surfaceInset = 17;
     i.globalScaleFactor = 0.3;
+    i.alpha = 0.7;
     i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
     i.visible = false;
     i.focusable = false;
     i.hasWallpaper = false;
     i.paused = false;
+    i.touchOcclusionMode = TouchOcclusionMode::ALLOW;
     i.ownerPid = 19;
     i.ownerUid = 24;
+    i.packageName = "com.example.package";
     i.inputFeatures = InputWindowInfo::Feature::DISABLE_USER_ACTIVITY;
     i.displayId = 34;
     i.portalToDisplayId = 2;
@@ -86,13 +89,16 @@
     ASSERT_EQ(i.frameBottom, i2.frameBottom);
     ASSERT_EQ(i.surfaceInset, i2.surfaceInset);
     ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
+    ASSERT_EQ(i.alpha, i2.alpha);
     ASSERT_EQ(i.transform, i2.transform);
     ASSERT_EQ(i.visible, i2.visible);
     ASSERT_EQ(i.focusable, i2.focusable);
     ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
     ASSERT_EQ(i.paused, i2.paused);
+    ASSERT_EQ(i.touchOcclusionMode, i2.touchOcclusionMode);
     ASSERT_EQ(i.ownerPid, i2.ownerPid);
     ASSERT_EQ(i.ownerUid, i2.ownerUid);
+    ASSERT_EQ(i.packageName, i2.packageName);
     ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
     ASSERT_EQ(i.displayId, i2.displayId);
     ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId);
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index ca16d2c..a637796 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -47,8 +47,8 @@
     // DataSpace::UNKNOWN otherwise.
     ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN;
 
-    // Additional color transform to apply in linear space after transforming
-    // to the output dataspace.
+    // Additional color transform to apply after transforming to the output
+    // dataspace, in non-linear space.
     mat4 colorTransform = mat4();
 
     // Region that will be cleared to (0, 0, 0, 1) prior to rendering.
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 1719cd8..69ad189 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -20,13 +20,13 @@
 #define LOG_TAG "RenderEngine"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include "SkiaGLRenderEngine.h"
-
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include <GLES2/gl2.h>
 #include <GrContextOptions.h>
 #include <SkCanvas.h>
+#include <SkColorFilter.h>
+#include <SkColorMatrix.h>
 #include <SkColorSpace.h>
 #include <SkImage.h>
 #include <SkImageFilters.h>
@@ -40,6 +40,7 @@
 #include <cmath>
 
 #include "../gl/GLExtensions.h"
+#include "SkiaGLRenderEngine.h"
 #include "filters/BlurFilter.h"
 
 extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
@@ -395,6 +396,13 @@
     }
 }
 
+static SkColorMatrix toSkColorMatrix(const mat4& matrix) {
+    return SkColorMatrix(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], 0, matrix[0][1],
+                         matrix[1][1], matrix[2][1], matrix[3][1], 0, matrix[0][2], matrix[1][2],
+                         matrix[2][2], matrix[3][2], 0, matrix[0][3], matrix[1][3], matrix[2][3],
+                         matrix[3][3], 0);
+}
+
 void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
     std::lock_guard<std::mutex> lock(mRenderingMutex);
     mImageCache.erase(bufferId);
@@ -566,6 +574,8 @@
             paint.setColor(SkColor4f{.fR = color.r, .fG = color.g, .fB = color.b, layer->alpha});
         }
 
+        paint.setColorFilter(SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)));
+
         // Layers have a local transform matrix that should be applied to them.
         canvas->save();
         canvas->concat(getSkM44(layer->geometry.positionTransform));
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index 9eb5483..f196ab9 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -17,13 +17,20 @@
 #pragma once
 
 #include <cstdint>
-#include <functional>
+#include <optional>
 #include <string>
 
 namespace android {
 
 // ID of a physical or a virtual display. This class acts as a type safe wrapper around uint64_t.
+// The encoding of the ID is type-specific for bits 0 to 61.
 struct DisplayId {
+    // Flag indicating that the display is virtual.
+    static constexpr uint64_t FLAG_VIRTUAL = 1ULL << 63;
+
+    // Flag indicating that the ID is stable across reboots.
+    static constexpr uint64_t FLAG_STABLE = 1ULL << 62;
+
     // TODO(b/162612135) Remove default constructor
     DisplayId() = default;
     constexpr DisplayId(const DisplayId&) = default;
@@ -51,8 +58,12 @@
 
 // DisplayId of a physical display, such as the internal display or externally connected display.
 struct PhysicalDisplayId : DisplayId {
-    // Flag indicating that the ID is stable across reboots.
-    static constexpr uint64_t FLAG_STABLE = 1ULL << 62;
+    static constexpr std::optional<PhysicalDisplayId> tryCast(DisplayId id) {
+        if (id.value & FLAG_VIRTUAL) {
+            return std::nullopt;
+        }
+        return {PhysicalDisplayId(id)};
+    }
 
     // Returns a stable ID based on EDID information.
     static constexpr PhysicalDisplayId fromEdid(uint8_t port, uint16_t manufacturerId,
@@ -69,8 +80,8 @@
 
     // TODO(b/162612135) Remove default constructor
     PhysicalDisplayId() = default;
+    // TODO(b/162612135) Remove constructor
     explicit constexpr PhysicalDisplayId(uint64_t id) : DisplayId(id) {}
-    explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other.value) {}
 
     constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); }
 
@@ -81,10 +92,82 @@
                                 uint32_t modelHash)
           : DisplayId(flags | (static_cast<uint64_t>(manufacturerId) << 40) |
                       (static_cast<uint64_t>(modelHash) << 8) | port) {}
+
+    explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other) {}
 };
 
 static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));
 
+struct VirtualDisplayId : DisplayId {
+    using BaseId = uint32_t;
+    // 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.value & FLAG_VIRTUAL) {
+            return {VirtualDisplayId(id)};
+        }
+        return std::nullopt;
+    }
+
+protected:
+    constexpr VirtualDisplayId(uint64_t flags, BaseId baseId)
+          : DisplayId(DisplayId::FLAG_VIRTUAL | flags | baseId) {}
+
+    explicit constexpr VirtualDisplayId(DisplayId other) : DisplayId(other) {}
+};
+
+struct HalVirtualDisplayId : VirtualDisplayId {
+    explicit constexpr HalVirtualDisplayId(BaseId baseId) : VirtualDisplayId(0, baseId) {}
+
+    static constexpr std::optional<HalVirtualDisplayId> tryCast(DisplayId id) {
+        if ((id.value & FLAG_VIRTUAL) && !(id.value & VirtualDisplayId::FLAG_GPU)) {
+            return {HalVirtualDisplayId(id)};
+        }
+        return std::nullopt;
+    }
+
+private:
+    explicit constexpr HalVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {}
+};
+
+struct GpuVirtualDisplayId : VirtualDisplayId {
+    explicit constexpr GpuVirtualDisplayId(BaseId baseId)
+          : VirtualDisplayId(VirtualDisplayId::FLAG_GPU, baseId) {}
+
+    static constexpr std::optional<GpuVirtualDisplayId> tryCast(DisplayId id) {
+        if ((id.value & FLAG_VIRTUAL) && (id.value & VirtualDisplayId::FLAG_GPU)) {
+            return {GpuVirtualDisplayId(id)};
+        }
+        return std::nullopt;
+    }
+
+private:
+    explicit constexpr GpuVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {}
+};
+
+// HalDisplayId is the ID of a display which is managed by HWC.
+// PhysicalDisplayId and HalVirtualDisplayId are implicitly convertible to HalDisplayId.
+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)};
+    }
+
+private:
+    explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {}
+};
+
+static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t));
+static_assert(sizeof(HalVirtualDisplayId) == sizeof(uint64_t));
+static_assert(sizeof(GpuVirtualDisplayId) == sizeof(uint64_t));
+static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
+
 } // namespace android
 
 namespace std {
@@ -99,4 +182,13 @@
 template <>
 struct hash<android::PhysicalDisplayId> : hash<android::DisplayId> {};
 
+template <>
+struct hash<android::HalVirtualDisplayId> : hash<android::DisplayId> {};
+
+template <>
+struct hash<android::GpuVirtualDisplayId> : hash<android::DisplayId> {};
+
+template <>
+struct hash<android::HalDisplayId> : hash<android::DisplayId> {};
+
 } // namespace std
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 032dd4c..d005ce8 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -29,6 +29,13 @@
 }
 
 cc_test {
+    name: "DisplayId_test",
+    shared_libs: ["libui"],
+    srcs: ["DisplayId_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
     name: "FlattenableHelpers_test",
     shared_libs: ["libui"],
     srcs: ["FlattenableHelpers_test.cpp"],
diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp
new file mode 100644
index 0000000..1d908b8
--- /dev/null
+++ b/libs/ui/tests/DisplayId_test.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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;
+    PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash);
+    EXPECT_EQ(port, id.getPort());
+    EXPECT_EQ(manufacturerId, id.getManufacturerId());
+    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));
+}
+
+TEST(DisplayIdTest, createPhysicalIdFromPort) {
+    constexpr uint8_t port = 3;
+    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));
+}
+
+TEST(DisplayIdTest, createGpuVirtualId) {
+    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));
+}
+
+TEST(DisplayIdTest, createHalVirtualId) {
+    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));
+}
+
+} // namespace android::ui
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 3ccb0c9..8b4ae03 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -402,6 +402,7 @@
         // To avoid leaking stack in case that call never comes, and for tests,
         // initialize it here anyways.
         mInTouchMode(true),
+        mMaximumObscuringOpacityForTouch(1.0f),
         mFocusedDisplayId(ADISPLAY_ID_DEFAULT) {
     mLooper = new Looper(false);
     mReporter = createInputReporter();
@@ -481,6 +482,33 @@
 }
 
 /**
+ * Raise ANR if there is no focused window.
+ * Before the ANR is raised, do a final state check:
+ * 1. The currently focused application must be the same one we are waiting for.
+ * 2. Ensure we still don't have a focused window.
+ */
+void InputDispatcher::processNoFocusedWindowAnrLocked() {
+    // Check if the application that we are waiting for is still focused.
+    std::shared_ptr<InputApplicationHandle> focusedApplication =
+            getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId);
+    if (focusedApplication == nullptr ||
+        focusedApplication->getApplicationToken() !=
+                mAwaitedFocusedApplication->getApplicationToken()) {
+        // Unexpected because we should have reset the ANR timer when focused application changed
+        ALOGE("Waited for a focused window, but focused application has already changed to %s",
+              focusedApplication->getName().c_str());
+        return; // The focused application has changed.
+    }
+
+    const sp<InputWindowHandle>& focusedWindowHandle =
+            getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId);
+    if (focusedWindowHandle != nullptr) {
+        return; // We now have a focused window. No need for ANR.
+    }
+    onAnrLocked(mAwaitedFocusedApplication);
+}
+
+/**
  * Check if any of the connections' wait queues have events that are too old.
  * If we waited for events to be ack'ed for more than the window timeout, raise an ANR.
  * Return the time at which we should wake up next.
@@ -491,8 +519,9 @@
     // Check if we are waiting for a focused window to appear. Raise ANR if waited too long
     if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
         if (currentTime >= *mNoFocusedWindowTimeoutTime) {
-            onAnrLocked(mAwaitedFocusedApplication);
+            processNoFocusedWindowAnrLocked();
             mAwaitedFocusedApplication.reset();
+            mNoFocusedWindowTimeoutTime = std::nullopt;
             return LONG_LONG_MIN;
         } else {
             // Keep waiting
@@ -1494,6 +1523,7 @@
                     DEFAULT_INPUT_DISPATCHING_TIMEOUT);
             mNoFocusedWindowTimeoutTime = currentTime + timeout.count();
             mAwaitedFocusedApplication = focusedApplicationHandle;
+            mAwaitedApplicationDisplayId = displayId;
             ALOGW("Waiting because no window has focus but %s may eventually add a "
                   "window when it finishes starting up. Will wait for %" PRId64 "ms",
                   mAwaitedFocusedApplication->getName().c_str(), millis(timeout));
@@ -1708,6 +1738,22 @@
             }
         }
 
+        // Drop events that can't be trusted due to occlusion
+        if (newTouchedWindowHandle != nullptr &&
+            mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) {
+            TouchOcclusionInfo occlusionInfo =
+                    computeTouchOcclusionInfoLocked(newTouchedWindowHandle, x, y);
+            // The order of the operands in the 'if' below is important because even if the feature
+            // is not BLOCK we want isTouchTrustedLocked() to execute in order to log details to
+            // logcat.
+            if (!isTouchTrustedLocked(occlusionInfo) &&
+                mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
+                ALOGW("Dropping untrusted touch event due to %s/%d",
+                      occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
+                newTouchedWindowHandle = nullptr;
+            }
+        }
+
         // Also don't send the new touch event to unresponsive gesture monitors
         newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors);
 
@@ -2109,9 +2155,9 @@
     auto otherInfo = otherHandle->getInfo();
     if (!otherInfo->visible) {
         return false;
-    } else if (info->ownerPid == otherInfo->ownerPid) {
-        // If ownerPid is the same we don't generate occlusion events as there
-        // is no in-process security boundary.
+    } else if (info->ownerUid == otherInfo->ownerUid) {
+        // If ownerUid is the same we don't generate occlusion events as there
+        // is no security boundary within an uid.
         return false;
     } else if (otherInfo->trustedOverlay) {
         return false;
@@ -2121,6 +2167,84 @@
     return true;
 }
 
+/**
+ * Returns touch occlusion information in the form of TouchOcclusionInfo. To check if the touch is
+ * untrusted, one should check:
+ *
+ * 1. If result.hasBlockingOcclusion is true.
+ *    If it's, it means the touch should be blocked due to a window with occlusion mode of
+ *    BLOCK_UNTRUSTED.
+ *
+ * 2. If result.obscuringOpacity > mMaximumObscuringOpacityForTouch.
+ *    If it is (and 1 is false), then the touch should be blocked because a stack of windows
+ *    (possibly only one) with occlusion mode of USE_OPACITY from one UID resulted in a composed
+ *    obscuring opacity above the threshold. Note that if there was no window of occlusion mode
+ *    USE_OPACITY, result.obscuringOpacity would've been 0 and since
+ *    mMaximumObscuringOpacityForTouch >= 0, the condition above would never be true.
+ *
+ * If neither of those is true, then it means the touch can be allowed.
+ */
+InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked(
+        const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const {
+    int32_t displayId = windowHandle->getInfo()->displayId;
+    const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+    TouchOcclusionInfo info;
+    info.hasBlockingOcclusion = false;
+    info.obscuringOpacity = 0;
+    info.obscuringUid = -1;
+    std::map<int32_t, float> opacityByUid;
+    for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
+        if (windowHandle == otherHandle) {
+            break; // All future windows are below us. Exit early.
+        }
+        const InputWindowInfo* otherInfo = otherHandle->getInfo();
+        if (canBeObscuredBy(windowHandle, otherHandle) &&
+            windowHandle->getInfo()->ownerUid != otherInfo->ownerUid &&
+            otherInfo->frameContainsPoint(x, y)) {
+            // canBeObscuredBy() has returned true above, which means this window is untrusted, so
+            // we perform the checks below to see if the touch can be propagated or not based on the
+            // window's touch occlusion mode
+            if (otherInfo->touchOcclusionMode == TouchOcclusionMode::BLOCK_UNTRUSTED) {
+                info.hasBlockingOcclusion = true;
+                info.obscuringUid = otherInfo->ownerUid;
+                info.obscuringPackage = otherInfo->packageName;
+                break;
+            }
+            if (otherInfo->touchOcclusionMode == TouchOcclusionMode::USE_OPACITY) {
+                uint32_t uid = otherInfo->ownerUid;
+                float opacity =
+                        (opacityByUid.find(uid) == opacityByUid.end()) ? 0 : opacityByUid[uid];
+                // Given windows A and B:
+                // opacity(A, B) = 1 - [1 - opacity(A)] * [1 - opacity(B)]
+                opacity = 1 - (1 - opacity) * (1 - otherInfo->alpha);
+                opacityByUid[uid] = opacity;
+                if (opacity > info.obscuringOpacity) {
+                    info.obscuringOpacity = opacity;
+                    info.obscuringUid = uid;
+                    info.obscuringPackage = otherInfo->packageName;
+                }
+            }
+        }
+    }
+    return info;
+}
+
+bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const {
+    if (occlusionInfo.hasBlockingOcclusion) {
+        ALOGW("Untrusted touch due to occlusion by %s/%d", occlusionInfo.obscuringPackage.c_str(),
+              occlusionInfo.obscuringUid);
+        return false;
+    }
+    if (occlusionInfo.obscuringOpacity > mMaximumObscuringOpacityForTouch) {
+        ALOGW("Untrusted touch due to occlusion by %s/%d (obscuring opacity = "
+              "%.2f, maximum allowed = %.2f)",
+              occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid,
+              occlusionInfo.obscuringOpacity, mMaximumObscuringOpacityForTouch);
+        return false;
+    }
+    return true;
+}
+
 bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
                                                     int32_t x, int32_t y) const {
     int32_t displayId = windowHandle->getInfo()->displayId;
@@ -4060,6 +4184,21 @@
     mInTouchMode = inTouchMode;
 }
 
+void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) {
+    if (opacity < 0 || opacity > 1) {
+        LOG_ALWAYS_FATAL("Maximum obscuring opacity for touch should be >= 0 and <= 1");
+        return;
+    }
+
+    std::scoped_lock lock(mLock);
+    mMaximumObscuringOpacityForTouch = opacity;
+}
+
+void InputDispatcher::setBlockUntrustedTouchesMode(BlockUntrustedTouchesMode mode) {
+    std::scoped_lock lock(mLock);
+    mBlockUntrustedTouchesMode = mode;
+}
+
 bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
     if (fromToken == toToken) {
         if (DEBUG_FOCUS) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 4fcdcc2..1772ad8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -112,6 +112,8 @@
     virtual void setInputDispatchMode(bool enabled, bool frozen) override;
     virtual void setInputFilterEnabled(bool enabled) override;
     virtual void setInTouchMode(bool inTouchMode) override;
+    virtual void setMaximumObscuringOpacityForTouch(float opacity) override;
+    virtual void setBlockUntrustedTouchesMode(BlockUntrustedTouchesMode mode) override;
 
     virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
                                     const sp<IBinder>& toToken) override;
@@ -296,6 +298,8 @@
     bool mDispatchFrozen GUARDED_BY(mLock);
     bool mInputFilterEnabled GUARDED_BY(mLock);
     bool mInTouchMode GUARDED_BY(mLock);
+    float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock);
+    BlockUntrustedTouchesMode mBlockUntrustedTouchesMode GUARDED_BY(mLock);
 
     std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
             GUARDED_BY(mLock);
@@ -390,6 +394,11 @@
      * Used to raise an ANR when we have no focused window.
      */
     std::shared_ptr<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock);
+    /**
+     * The displayId that the focused application is associated with.
+     */
+    int32_t mAwaitedApplicationDisplayId GUARDED_BY(mLock);
+    void processNoFocusedWindowAnrLocked() REQUIRES(mLock);
 
     /**
      * This map will store the pending focus requests that cannot be currently processed. This can
@@ -446,6 +455,17 @@
     void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
     bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
                                   const InjectionState* injectionState);
+
+    struct TouchOcclusionInfo {
+        bool hasBlockingOcclusion;
+        float obscuringOpacity;
+        std::string obscuringPackage;
+        int32_t obscuringUid;
+    };
+
+    TouchOcclusionInfo computeTouchOcclusionInfoLocked(const sp<InputWindowHandle>& windowHandle,
+                                                       int32_t x, int32_t y) const REQUIRES(mLock);
+    bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock);
     bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x,
                                        int32_t y) const REQUIRES(mLock);
     bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 67d9a06..65687c4 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -20,12 +20,15 @@
 #include <InputListener.h>
 #include <android-base/result.h>
 #include <android/FocusRequest.h>
+#include <android/os/BlockUntrustedTouchesMode.h>
 #include <android/os/ISetInputWindowsListener.h>
 #include <input/InputApplication.h>
 #include <input/InputTransport.h>
 #include <input/InputWindow.h>
 #include <unordered_map>
 
+using android::os::BlockUntrustedTouchesMode;
+
 namespace android {
 
 /*
@@ -145,6 +148,21 @@
      */
     virtual void setInTouchMode(bool inTouchMode) = 0;
 
+    /**
+     * Sets the maximum allowed obscuring opacity by UID to propagate touches.
+     * For certain window types (eg. SAWs), the decision of honoring
+     * FLAG_NOT_TOUCHABLE or not depends on the combined obscuring opacity of
+     * the windows above the touch-consuming window.
+     */
+    virtual void setMaximumObscuringOpacityForTouch(float opacity) = 0;
+
+    /**
+     * Sets the mode of the block untrusted touches feature.
+     *
+     * TODO(b/169067926): Clean-up feature modes.
+     */
+    virtual void setBlockUntrustedTouchesMode(BlockUntrustedTouchesMode mode) = 0;
+
     /* Transfers touch focus from one window to another window.
      *
      * Returns true on success.  False if the window did not actually have touch focus.
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 70f872a..766dc90 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -3483,6 +3483,68 @@
     mFocusedWindow->assertNoEvents();
 }
 
+/**
+ * If we have no focused window, and a key comes in, we start the ANR timer.
+ * The focused application should add a focused window before the timer runs out to prevent ANR.
+ *
+ * If the user touches another application during this time, the key should be dropped.
+ * Next, if a new focused window comes in, without toggling the focused application,
+ * then no ANR should occur.
+ *
+ * Normally, we would expect the new focused window to be accompanied by 'setFocusedApplication',
+ * but in some cases the policy may not update the focused application.
+ */
+TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) {
+    std::shared_ptr<FakeApplicationHandle> focusedApplication =
+            std::make_shared<FakeApplicationHandle>();
+    focusedApplication->setDispatchingTimeout(60ms);
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
+    // The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
+    mFocusedWindow->setFocusable(false);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+    mFocusedWindow->consumeFocusEvent(false);
+
+    // Send a key. The ANR timer should start because there is no focused window.
+    // 'focusedApplication' will get blamed if this timer completes.
+    // Key will not be sent anywhere because we have no focused window. It will remain pending.
+    int32_t result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
+                      INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result);
+
+    // Wait until dispatcher starts the "no focused window" timer. If we don't wait here,
+    // then the injected touches won't cause the focused event to get dropped.
+    // The dispatcher only checks for whether the queue should be pruned upon queueing.
+    // If we inject the touch right away and the ANR timer hasn't started, the touch event would
+    // simply be added to the queue without 'shouldPruneInboundQueueLocked' returning 'true'.
+    // For this test, it means that the key would get delivered to the window once it becomes
+    // focused.
+    std::this_thread::sleep_for(10ms);
+
+    // Touch unfocused window. This should force the pending key to get dropped.
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {UNFOCUSED_WINDOW_LOCATION});
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // We do not consume the motion right away, because that would require dispatcher to first
+    // process (== drop) the key event, and by that time, ANR will be raised.
+    // Set the focused window first.
+    mFocusedWindow->setFocusable(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+    setFocusedWindow(mFocusedWindow);
+    mFocusedWindow->consumeFocusEvent(true);
+    // We do not call "setFocusedApplication" here, even though the newly focused window belongs
+    // to another application. This could be a bug / behaviour in the policy.
+
+    mUnfocusedWindow->consumeMotionDown();
+
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    // Should not ANR because we actually have a focused window. It was just added too slowly.
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyAnrWasNotCalled());
+}
+
 // These tests ensure we cannot send touch events to a window that's positioned behind a window
 // that has feature NO_INPUT_CHANNEL.
 // Layout:
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 54a36a9..acbccea 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -3059,15 +3059,17 @@
     mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
     mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
 
-    device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
-                                            AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    KeyboardInputMapper& mapper2 =
+            device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
+                                                    AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
     device2->reset(ARBITRARY_TIME);
 
     ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL));
     ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML));
     ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_SCROLLL));
-    ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON,
+              mapper2.getMetaState());
 }
 
 // --- KeyboardInputMapperTest_ExternalDevice ---
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index cf60b71..c77298e 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -366,7 +366,7 @@
         mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
     } else if (!display) {
         // Do nothing.
-    } else if (const auto displayId = display->getId();
+    } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
                displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
         // The HWC doesn't support present fences, so use the refresh
         // timestamp instead.
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 07817b5..aac6c91 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -79,18 +79,18 @@
 status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
                                uint32_t flags, const sp<IBinder>& parentHandle,
                                LayerMetadata metadata, sp<IBinder>* handle,
-                               sp<IGraphicBufferProducer>* gbp, int32_t* outId,
+                               sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
                                uint32_t* outTransformHint) {
     // We rely on createLayer to check permissions.
     return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
-                                 parentHandle, nullptr, outId, outTransformHint);
+                                 parentHandle, outLayerId, nullptr, outTransformHint);
 }
 
 status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
                                          PixelFormat format, uint32_t flags,
                                          const sp<IGraphicBufferProducer>& parent,
                                          LayerMetadata metadata, sp<IBinder>* handle,
-                                         sp<IGraphicBufferProducer>* gbp, int32_t* outId,
+                                         sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
                                          uint32_t* outTransformHint) {
     if (mFlinger->authenticateSurfaceTexture(parent) == false) {
         ALOGE("failed to authenticate surface texture");
@@ -104,12 +104,12 @@
     }
 
     return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
-                                 nullptr, layer, outId, outTransformHint);
+                                 nullptr, outLayerId, layer, outTransformHint);
 }
 
 status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
-                               int32_t* outId) {
-    return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle, outId);
+                               int32_t* outLayerId) {
+    return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle, outLayerId);
 }
 
 status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 9462f1a..15cd763 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -50,18 +50,18 @@
     virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
                                    uint32_t flags, const sp<IBinder>& parent,
                                    LayerMetadata metadata, sp<IBinder>* handle,
-                                   sp<IGraphicBufferProducer>* gbp, int32_t* outId,
+                                   sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
                                    uint32_t* outTransformHint = nullptr);
 
     virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
                                              PixelFormat format, uint32_t flags,
                                              const sp<IGraphicBufferProducer>& parent,
                                              LayerMetadata metadata, sp<IBinder>* handle,
-                                             sp<IGraphicBufferProducer>* gbp, int32_t* outId,
+                                             sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
                                              uint32_t* outTransformHint = nullptr);
 
     status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle,
-                           int32_t* outId);
+                           int32_t* outLayerId);
 
     virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index a38d1f3..01dd534 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -34,8 +34,8 @@
  */
 class Display : public virtual Output {
 public:
-    // Gets the HWC DisplayId for the display if there is one
-    virtual const std::optional<DisplayId>& getId() const = 0;
+    // Gets the DisplayId for the display
+    virtual DisplayId getId() const = 0;
 
     // True if the display is secure
     virtual bool isSecure() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 6bc677d..95ba9f0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -20,12 +20,14 @@
 #include <optional>
 #include <string>
 
+#include <ui/DisplayId.h>
 #include <ui/DisplayInfo.h>
 #include <ui/PixelFormat.h>
 #include <ui/Size.h>
 
 #include "DisplayHardware/DisplayIdentification.h"
 #include "DisplayHardware/PowerAdvisor.h"
+#include "DisplayIdGenerator.h"
 
 namespace android::compositionengine {
 
@@ -65,6 +67,9 @@
 
     // Debugging. Human readable name for the display.
     std::string name;
+
+    // Generator for IDs of virtual displays, which are backed by the GPU.
+    DisplayIdGenerator<GpuVirtualDisplayId>* gpuVirtualDisplayIdGenerator;
 };
 
 /**
@@ -95,6 +100,12 @@
         return *this;
     }
 
+    DisplayCreationArgsBuilder& setGpuVirtualDisplayIdGenerator(
+            DisplayIdGenerator<GpuVirtualDisplayId>& generator) {
+        mArgs.gpuVirtualDisplayIdGenerator = &generator;
+        return *this;
+    }
+
     DisplayCreationArgsBuilder& setIsSecure(bool isSecure) {
         mArgs.isSecure = isSecure;
         return *this;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 7a4f738..54e91ae 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -57,7 +57,7 @@
     void finishFrame(const CompositionRefreshArgs&) override;
 
     // compositionengine::Display overrides
-    const std::optional<DisplayId>& getId() const override;
+    DisplayId getId() const override;
     bool isSecure() const override;
     bool isVirtual() const override;
     void disconnect() override;
@@ -85,12 +85,14 @@
     std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
 
     // Testing
-    void setDisplayIdForTesting(std::optional<DisplayId> displayId);
+    void setDisplayIdForTesting(DisplayId displayId);
 
 private:
     bool mIsVirtual = false;
-    std::optional<DisplayId> mId;
+    bool mIsDisconnected = false;
+    DisplayId mId;
     Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
+    DisplayIdGenerator<GpuVirtualDisplayId>* mGpuVirtualDisplayIdGenerator;
 };
 
 // This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 3a4c70f..08a8b84 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -32,7 +32,7 @@
     Display();
     virtual ~Display();
 
-    MOCK_CONST_METHOD0(getId, const std::optional<DisplayId>&());
+    MOCK_CONST_METHOD0(getId, DisplayId());
     MOCK_CONST_METHOD0(isSecure, bool());
     MOCK_CONST_METHOD0(isVirtual, bool());
 
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 5459861..0b0b8d5 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -51,19 +51,26 @@
 
 void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
     mIsVirtual = !args.physical;
-    mId = args.physical ? std::make_optional(args.physical->id) : std::nullopt;
     mPowerAdvisor = args.powerAdvisor;
-
     editState().isSecure = args.isSecure;
     editState().displaySpace.bounds = Rect(args.pixels);
-
     setLayerStackFilter(args.layerStackId,
-                        args.physical ? args.physical->type == DisplayConnectionType::Internal
-                                      : false);
+                        args.physical && args.physical->type == DisplayConnectionType::Internal);
     setName(args.name);
+    mGpuVirtualDisplayIdGenerator = args.gpuVirtualDisplayIdGenerator;
 
-    if (!args.physical && args.useHwcVirtualDisplays) {
-        mId = maybeAllocateDisplayIdForVirtualDisplay(args.pixels, args.pixelFormat);
+    if (args.physical) {
+        mId = args.physical->id;
+    } else {
+        std::optional<DisplayId> id;
+        if (args.useHwcVirtualDisplays) {
+            id = maybeAllocateDisplayIdForVirtualDisplay(args.pixels, args.pixelFormat);
+        }
+        if (!id) {
+            id = mGpuVirtualDisplayIdGenerator->nextId();
+        }
+        LOG_ALWAYS_FATAL_IF(!id, "Failed to generate display ID");
+        mId = *id;
     }
 }
 
@@ -78,7 +85,7 @@
     return Output::isValid() && mPowerAdvisor;
 }
 
-const std::optional<DisplayId>& Display::getId() const {
+DisplayId Display::getId() const {
     return mId;
 }
 
@@ -94,31 +101,36 @@
     return mId;
 }
 
-void Display::setDisplayIdForTesting(std::optional<DisplayId> displayId) {
+void Display::setDisplayIdForTesting(DisplayId displayId) {
     mId = displayId;
 }
 
 void Display::disconnect() {
-    if (!mId) {
+    if (mIsDisconnected) {
         return;
     }
 
-    auto& hwc = getCompositionEngine().getHwComposer();
-    hwc.disconnectDisplay(*mId);
-    mId.reset();
+    mIsDisconnected = true;
+    if (const auto id = GpuVirtualDisplayId::tryCast(mId)) {
+        mGpuVirtualDisplayIdGenerator->markUnused(*id);
+        return;
+    }
+    const auto halDisplayId = HalDisplayId::tryCast(mId);
+    LOG_FATAL_IF(!halDisplayId);
+    getCompositionEngine().getHwComposer().disconnectDisplay(*halDisplayId);
 }
 
 void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
     Output::setColorTransform(args);
-
-    if (!mId || CC_LIKELY(!args.colorTransformMatrix)) {
+    const auto halDisplayId = HalDisplayId::tryCast(mId);
+    if (mIsDisconnected || !halDisplayId || CC_LIKELY(!args.colorTransformMatrix)) {
         return;
     }
 
     auto& hwc = getCompositionEngine().getHwComposer();
-    status_t result = hwc.setColorTransform(*mId, *args.colorTransformMatrix);
+    status_t result = hwc.setColorTransform(*halDisplayId, *args.colorTransformMatrix);
     ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d",
-             mId ? to_string(*mId).c_str() : "", result);
+             to_string(mId).c_str(), result);
 }
 
 void Display::setColorProfile(const ColorProfile& colorProfile) {
@@ -140,8 +152,10 @@
 
     Output::setColorProfile(colorProfile);
 
-    auto& hwc = getCompositionEngine().getHwComposer();
-    hwc.setActiveColorMode(*mId, colorProfile.mode, colorProfile.renderIntent);
+    const auto physicalId = PhysicalDisplayId::tryCast(mId);
+    LOG_FATAL_IF(!physicalId);
+    getCompositionEngine().getHwComposer().setActiveColorMode(*physicalId, colorProfile.mode,
+                                                              colorProfile.renderIntent);
 }
 
 void Display::dump(std::string& out) const {
@@ -150,14 +164,8 @@
     StringAppendF(&out, "   Composition Display State: [\"%s\"]", getName().c_str());
 
     out.append("\n   ");
-
     dumpVal(out, "isVirtual", mIsVirtual);
-    if (mId) {
-        dumpVal(out, "hwcId", to_string(*mId));
-    } else {
-        StringAppendF(&out, "no hwcId, ");
-    }
-
+    dumpVal(out, "DisplayId", to_string(mId));
     out.append("\n");
 
     Output::dumpBase(out);
@@ -178,31 +186,33 @@
 
 std::unique_ptr<compositionengine::OutputLayer> Display::createOutputLayer(
         const sp<compositionengine::LayerFE>& layerFE) const {
-    auto result = impl::createOutputLayer(*this, layerFE);
+    auto outputLayer = impl::createOutputLayer(*this, layerFE);
 
-    if (result && mId) {
+    if (const auto halDisplayId = HalDisplayId::tryCast(mId);
+        outputLayer && !mIsDisconnected && halDisplayId) {
         auto& hwc = getCompositionEngine().getHwComposer();
-        auto displayId = *mId;
         // Note: For the moment we ensure it is safe to take a reference to the
         // HWComposer implementation by destroying all the OutputLayers (and
         // hence the HWC2::Layers they own) before setting a new HWComposer. See
         // for example SurfaceFlinger::updateVrFlinger().
         // TODO(b/121291683): Make this safer.
-        auto hwcLayer = std::shared_ptr<HWC2::Layer>(hwc.createLayer(displayId),
-                                                     [&hwc, displayId](HWC2::Layer* layer) {
-                                                         hwc.destroyLayer(displayId, layer);
-                                                     });
+        auto hwcLayer =
+                std::shared_ptr<HWC2::Layer>(hwc.createLayer(*halDisplayId),
+                                             [&hwc, id = *halDisplayId](HWC2::Layer* layer) {
+                                                 hwc.destroyLayer(id, layer);
+                                             });
         ALOGE_IF(!hwcLayer, "Failed to create a HWC layer for a HWC supported display %s",
                  getName().c_str());
-        result->setHwcLayer(std::move(hwcLayer));
+        outputLayer->setHwcLayer(std::move(hwcLayer));
     }
-    return result;
+    return outputLayer;
 }
 
 void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& refreshArgs) {
     Output::setReleasedLayers(refreshArgs);
 
-    if (!mId || refreshArgs.layersWithQueuedFrames.empty()) {
+    if (mIsDisconnected || GpuVirtualDisplayId::tryCast(mId) ||
+        refreshArgs.layersWithQueuedFrames.empty()) {
         return;
     }
 
@@ -238,19 +248,25 @@
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
 
+    if (mIsDisconnected) {
+        return;
+    }
+
     // Default to the base settings -- client composition only.
     Output::chooseCompositionStrategy();
 
-    // If we don't have a HWC display, then we are done
-    if (!mId) {
+    // If we don't have a HWC display, then we are done.
+    const auto halDisplayId = HalDisplayId::tryCast(mId);
+    if (!halDisplayId) {
         return;
     }
 
     // Get any composition changes requested by the HWC device, and apply them.
     std::optional<android::HWComposer::DeviceRequestedChanges> changes;
     auto& hwc = getCompositionEngine().getHwComposer();
-    if (status_t result = hwc.getDeviceCompositionChanges(*mId, anyLayersRequireClientComposition(),
-                                                          &changes);
+    if (status_t result =
+                hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
+                                                &changes);
         result != NO_ERROR) {
         ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
               strerror(-result));
@@ -271,8 +287,12 @@
 
 bool Display::getSkipColorTransform() const {
     const auto& hwc = getCompositionEngine().getHwComposer();
-    return mId ? hwc.hasDisplayCapability(*mId, hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM)
-               : hwc.hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM);
+    if (const auto halDisplayId = HalDisplayId::tryCast(mId)) {
+        return hwc.hasDisplayCapability(*halDisplayId,
+                                        hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
+    }
+
+    return hwc.hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM);
 }
 
 bool Display::anyLayersRequireClientComposition() const {
@@ -339,16 +359,17 @@
 }
 
 compositionengine::Output::FrameFences Display::presentAndGetFrameFences() {
-    auto result = impl::Output::presentAndGetFrameFences();
+    auto fences = impl::Output::presentAndGetFrameFences();
 
-    if (!mId) {
-        return result;
+    const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
+    if (mIsDisconnected || !halDisplayIdOpt) {
+        return fences;
     }
 
     auto& hwc = getCompositionEngine().getHwComposer();
-    hwc.presentAndGetReleaseFences(*mId);
+    hwc.presentAndGetReleaseFences(*halDisplayIdOpt);
 
-    result.presentFence = hwc.getPresentFence(*mId);
+    fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
 
     // TODO(b/121291683): Change HWComposer call to return entire map
     for (const auto* layer : getOutputLayersOrderedByZ()) {
@@ -357,19 +378,19 @@
             continue;
         }
 
-        result.layerFences.emplace(hwcLayer, hwc.getLayerReleaseFence(*mId, hwcLayer));
+        fences.layerFences.emplace(hwcLayer, hwc.getLayerReleaseFence(*halDisplayIdOpt, hwcLayer));
     }
 
-    hwc.clearReleaseFences(*mId);
+    hwc.clearReleaseFences(*halDisplayIdOpt);
 
-    return result;
+    return fences;
 }
 
 void Display::setExpensiveRenderingExpected(bool enabled) {
     Output::setExpensiveRenderingExpected(enabled);
 
-    if (mPowerAdvisor && mId) {
-        mPowerAdvisor->setExpensiveRenderingExpected(*mId, enabled);
+    if (mPowerAdvisor && !GpuVirtualDisplayId::tryCast(mId)) {
+        mPowerAdvisor->setExpensiveRenderingExpected(mId, enabled);
     }
 }
 
@@ -378,11 +399,10 @@
     // 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 (!mId) {
-        if (getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) {
-            ALOGV("Skipping display composition");
-            return;
-        }
+    if (GpuVirtualDisplayId::tryCast(mId) &&
+        getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) {
+        ALOGV("Skipping display composition");
+        return;
     }
 
     impl::Output::finishFrame(refreshArgs);
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 4519a9d..6d01bf1 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -176,6 +176,7 @@
     DisplayCreationArgs getDisplayCreationArgsForNonHWCVirtualDisplay() {
         return DisplayCreationArgsBuilder()
                 .setUseHwcVirtualDisplays(false)
+                .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator)
                 .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
                 .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
                 .setIsSecure(false)
@@ -189,6 +190,7 @@
     StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
     StrictMock<mock::CompositionEngine> mCompositionEngine;
     sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
+    RandomDisplayIdGenerator<GpuVirtualDisplayId> mGpuDisplayIdGenerator;
 };
 
 struct PartialMockDisplayTestCommon : public DisplayTestCommon {
@@ -245,7 +247,7 @@
                                        getDisplayCreationArgsForNonHWCVirtualDisplay());
     EXPECT_FALSE(display->isSecure());
     EXPECT_TRUE(display->isVirtual());
-    EXPECT_EQ(std::nullopt, display->getId());
+    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(display->getId()));
 }
 
 /*
@@ -332,6 +334,7 @@
     mDisplay->setConfiguration(
             DisplayCreationArgsBuilder()
                     .setUseHwcVirtualDisplays(true)
+                    .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator)
                     .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
                     .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
                     .setIsSecure(false)
@@ -340,7 +343,7 @@
                     .setName(getDisplayNameFromCurrentTest())
                     .build());
 
-    EXPECT_EQ(std::nullopt, mDisplay->getId());
+    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(mDisplay->getId()));
     EXPECT_FALSE(mDisplay->isSecure());
     EXPECT_TRUE(mDisplay->isVirtual());
     EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
@@ -352,6 +355,7 @@
     mDisplay->setConfiguration(
             DisplayCreationArgsBuilder()
                     .setUseHwcVirtualDisplays(false)
+                    .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator)
                     .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
                     .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
                     .setIsSecure(false)
@@ -360,7 +364,7 @@
                     .setName(getDisplayNameFromCurrentTest())
                     .build());
 
-    EXPECT_EQ(std::nullopt, mDisplay->getId());
+    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(mDisplay->getId()));
     EXPECT_FALSE(mDisplay->isSecure());
     EXPECT_TRUE(mDisplay->isVirtual());
     EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
@@ -375,16 +379,13 @@
 using DisplayDisconnectTest = PartialMockDisplayTestCommon;
 
 TEST_F(DisplayDisconnectTest, disconnectsDisplay) {
-    // The first call to disconnect will disconnect the display with the HWC and
-    // set mHwcId to -1.
-    EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(1);
+    // The first call to disconnect will disconnect the display with the HWC.
+    EXPECT_CALL(mHwComposer, disconnectDisplay(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
     mDisplay->disconnect();
-    EXPECT_FALSE(mDisplay->getId());
 
     // Subsequent calls will do nothing,
-    EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(0);
+    EXPECT_CALL(mHwComposer, disconnectDisplay(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(0);
     mDisplay->disconnect();
-    EXPECT_FALSE(mDisplay->getId());
 }
 
 /*
@@ -402,7 +403,8 @@
     // Identity matrix sets an identity state value
     const mat4 kIdentity;
 
-    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kIdentity)).Times(1);
+    EXPECT_CALL(mHwComposer, setColorTransform(HalDisplayId(DEFAULT_DISPLAY_ID), kIdentity))
+            .Times(1);
 
     refreshArgs.colorTransformMatrix = kIdentity;
     mDisplay->setColorTransform(refreshArgs);
@@ -410,7 +412,8 @@
     // Non-identity matrix sets a non-identity state value
     const mat4 kNonIdentity = mat4() * 2;
 
-    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kNonIdentity)).Times(1);
+    EXPECT_CALL(mHwComposer, setColorTransform(HalDisplayId(DEFAULT_DISPLAY_ID), kNonIdentity))
+            .Times(1);
 
     refreshArgs.colorTransformMatrix = kNonIdentity;
     mDisplay->setColorTransform(refreshArgs);
@@ -527,13 +530,14 @@
     sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
     StrictMock<HWC2::mock::Layer> hwcLayer;
 
-    EXPECT_CALL(mHwComposer, createLayer(DEFAULT_DISPLAY_ID)).WillOnce(Return(&hwcLayer));
+    EXPECT_CALL(mHwComposer, createLayer(HalDisplayId(DEFAULT_DISPLAY_ID)))
+            .WillOnce(Return(&hwcLayer));
 
     auto outputLayer = mDisplay->createOutputLayer(layerFE);
 
     EXPECT_EQ(&hwcLayer, outputLayer->getHwcLayer());
 
-    EXPECT_CALL(mHwComposer, destroyLayer(DEFAULT_DISPLAY_ID, &hwcLayer));
+    EXPECT_CALL(mHwComposer, destroyLayer(HalDisplayId(DEFAULT_DISPLAY_ID), &hwcLayer));
     outputLayer.reset();
 }
 
@@ -606,7 +610,7 @@
     auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
     std::shared_ptr<Display> nonHwcDisplay =
             createPartialMockDisplay<Display>(mCompositionEngine, args);
-    EXPECT_FALSE(nonHwcDisplay->getId());
+    EXPECT_TRUE(GpuVirtualDisplayId::tryCast(nonHwcDisplay->getId()));
 
     nonHwcDisplay->chooseCompositionStrategy();
 
@@ -617,7 +621,8 @@
 
 TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
     EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
-    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, false, _))
+    EXPECT_CALL(mHwComposer,
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _))
             .WillOnce(Return(INVALID_OPERATION));
 
     mDisplay->chooseCompositionStrategy();
@@ -639,7 +644,7 @@
             .InSequence(s)
             .WillOnce(Return(false));
 
-    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
+    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
 
@@ -669,7 +674,7 @@
             .InSequence(s)
             .WillOnce(Return(false));
 
-    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
+    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _))
             .WillOnce(DoAll(SetArgPointee<2>(changes), Return(NO_ERROR)));
     EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
     EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
@@ -699,7 +704,7 @@
 
 TEST_F(DisplayGetSkipColorTransformTest, checksDisplayCapability) {
     EXPECT_CALL(mHwComposer,
-                hasDisplayCapability(DEFAULT_DISPLAY_ID,
+                hasDisplayCapability(HalDisplayId(DEFAULT_DISPLAY_ID),
                                      hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM))
             .WillOnce(Return(true));
     EXPECT_TRUE(mDisplay->getSkipColorTransform());
@@ -857,13 +862,16 @@
     sp<Fence> layer1Fence = new Fence();
     sp<Fence> layer2Fence = new Fence();
 
-    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(DEFAULT_DISPLAY_ID)).Times(1);
-    EXPECT_CALL(mHwComposer, getPresentFence(DEFAULT_DISPLAY_ID)).WillOnce(Return(presentFence));
-    EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer1.hwc2Layer))
+    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
+    EXPECT_CALL(mHwComposer, getPresentFence(HalDisplayId(DEFAULT_DISPLAY_ID)))
+            .WillOnce(Return(presentFence));
+    EXPECT_CALL(mHwComposer,
+                getLayerReleaseFence(HalDisplayId(DEFAULT_DISPLAY_ID), &mLayer1.hwc2Layer))
             .WillOnce(Return(layer1Fence));
-    EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer2.hwc2Layer))
+    EXPECT_CALL(mHwComposer,
+                getLayerReleaseFence(HalDisplayId(DEFAULT_DISPLAY_ID), &mLayer2.hwc2Layer))
             .WillOnce(Return(layer2Fence));
-    EXPECT_CALL(mHwComposer, clearReleaseFences(DEFAULT_DISPLAY_ID)).Times(1);
+    EXPECT_CALL(mHwComposer, clearReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1);
 
     auto result = mDisplay->presentAndGetFrameFences();
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 1dd5df4..b5cda68 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -42,63 +42,68 @@
     MOCK_CONST_METHOD3(getDisplayIdentificationData,
                        bool(hal::HWDisplayId, uint8_t*, DisplayIdentificationData*));
     MOCK_CONST_METHOD1(hasCapability, bool(hal::Capability));
-    MOCK_CONST_METHOD2(hasDisplayCapability, bool(DisplayId, hal::DisplayCapability));
+    MOCK_CONST_METHOD2(hasDisplayCapability, bool(HalDisplayId, hal::DisplayCapability));
 
     MOCK_METHOD3(allocateVirtualDisplay,
                  std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*));
     MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
-    MOCK_METHOD1(createLayer, HWC2::Layer*(DisplayId));
-    MOCK_METHOD2(destroyLayer, void(DisplayId, HWC2::Layer*));
+    MOCK_METHOD1(createLayer, HWC2::Layer*(HalDisplayId));
+    MOCK_METHOD2(destroyLayer, void(HalDisplayId, HWC2::Layer*));
     MOCK_METHOD3(getDeviceCompositionChanges,
-                 status_t(DisplayId, bool,
+                 status_t(HalDisplayId, bool,
                           std::optional<android::HWComposer::DeviceRequestedChanges>*));
     MOCK_METHOD5(setClientTarget,
-                 status_t(DisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
+                 status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
                           ui::Dataspace));
-    MOCK_METHOD1(presentAndGetReleaseFences, status_t(DisplayId));
-    MOCK_METHOD2(setPowerMode, status_t(DisplayId, hal::PowerMode));
-    MOCK_METHOD2(setActiveConfig, status_t(DisplayId, size_t));
-    MOCK_METHOD2(setColorTransform, status_t(DisplayId, const mat4&));
-    MOCK_METHOD1(disconnectDisplay, void(DisplayId));
+    MOCK_METHOD1(presentAndGetReleaseFences, status_t(HalDisplayId));
+    MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
+    MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t));
+    MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&));
+    MOCK_METHOD1(disconnectDisplay, void(HalDisplayId));
     MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&));
-    MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(DisplayId));
-    MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(DisplayId, HWC2::Layer*));
-    MOCK_METHOD3(setOutputBuffer, status_t(DisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
-    MOCK_METHOD1(clearReleaseFences, void(DisplayId));
-    MOCK_METHOD2(getHdrCapabilities, status_t(DisplayId, HdrCapabilities*));
-    MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(DisplayId));
-    MOCK_CONST_METHOD2(getRenderIntents, std::vector<ui::RenderIntent>(DisplayId, ui::ColorMode));
-    MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(DisplayId, ui::Dataspace));
+    MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(HalDisplayId));
+    MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(HalDisplayId, HWC2::Layer*));
+    MOCK_METHOD3(setOutputBuffer,
+                 status_t(HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
+    MOCK_METHOD1(clearReleaseFences, void(HalDisplayId));
+    MOCK_METHOD2(getHdrCapabilities, status_t(HalDisplayId, HdrCapabilities*));
+    MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(HalDisplayId));
+    MOCK_CONST_METHOD2(getRenderIntents,
+                       std::vector<ui::RenderIntent>(HalDisplayId, ui::ColorMode));
+    MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(HalDisplayId, ui::Dataspace));
     MOCK_METHOD4(getDisplayedContentSamplingAttributes,
-                 status_t(DisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*));
-    MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(DisplayId, bool, uint8_t, uint64_t));
+                 status_t(HalDisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*));
+    MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(HalDisplayId, bool, uint8_t, uint64_t));
     MOCK_METHOD4(getDisplayedContentSample,
-                 status_t(DisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
-    MOCK_METHOD2(setDisplayBrightness, std::future<status_t>(DisplayId, float));
-    MOCK_METHOD2(getDisplayBrightnessSupport, status_t(DisplayId, bool*));
+                 status_t(HalDisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
+    MOCK_METHOD2(setDisplayBrightness, std::future<status_t>(PhysicalDisplayId, float));
+    MOCK_METHOD2(getDisplayBrightnessSupport, status_t(PhysicalDisplayId, bool*));
 
     MOCK_METHOD2(onHotplug,
                  std::optional<DisplayIdentificationInfo>(hal::HWDisplayId, hal::Connection));
     MOCK_METHOD2(onVsync, bool(hal::HWDisplayId, int64_t));
-    MOCK_METHOD2(setVsyncEnabled, void(DisplayId, hal::Vsync));
-    MOCK_CONST_METHOD1(getRefreshTimestamp, nsecs_t(DisplayId));
-    MOCK_CONST_METHOD1(isConnected, bool(DisplayId));
-    MOCK_CONST_METHOD1(getConfigs,
-                       std::vector<std::shared_ptr<const HWC2::Display::Config>>(DisplayId));
-    MOCK_CONST_METHOD1(getActiveConfig, std::shared_ptr<const HWC2::Display::Config>(DisplayId));
-    MOCK_CONST_METHOD1(getActiveConfigIndex, int(DisplayId));
-    MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(DisplayId));
-    MOCK_METHOD3(setActiveColorMode, status_t(DisplayId, ui::ColorMode, ui::RenderIntent));
+    MOCK_METHOD2(setVsyncEnabled, void(PhysicalDisplayId, hal::Vsync));
+    MOCK_CONST_METHOD1(getRefreshTimestamp, nsecs_t(PhysicalDisplayId));
+    MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId));
+    MOCK_CONST_METHOD1(
+            getConfigs,
+            std::vector<std::shared_ptr<const HWC2::Display::Config>>(PhysicalDisplayId));
+    MOCK_CONST_METHOD1(getActiveConfig,
+                       std::shared_ptr<const HWC2::Display::Config>(PhysicalDisplayId));
+    MOCK_CONST_METHOD1(getActiveConfigIndex, int(PhysicalDisplayId));
+    MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId));
+    MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent));
     MOCK_CONST_METHOD0(isUsingVrComposer, bool());
-    MOCK_CONST_METHOD1(getDisplayConnectionType, DisplayConnectionType(DisplayId));
-    MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(DisplayId));
-    MOCK_CONST_METHOD1(getDisplayVsyncPeriod, nsecs_t(DisplayId));
+    MOCK_CONST_METHOD1(getDisplayConnectionType, DisplayConnectionType(PhysicalDisplayId));
+    MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId));
+    MOCK_CONST_METHOD1(getDisplayVsyncPeriod, nsecs_t(PhysicalDisplayId));
     MOCK_METHOD4(setActiveConfigWithConstraints,
-                 status_t(DisplayId, size_t, const hal::VsyncPeriodChangeConstraints&,
+                 status_t(PhysicalDisplayId, size_t, const hal::VsyncPeriodChangeConstraints&,
                           hal::VsyncPeriodChangeTimeline*));
-    MOCK_METHOD2(setAutoLowLatencyMode, status_t(DisplayId, bool));
-    MOCK_METHOD2(getSupportedContentTypes, status_t(DisplayId, std::vector<hal::ContentType>*));
-    MOCK_METHOD2(setContentType, status_t(DisplayId, hal::ContentType));
+    MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool));
+    MOCK_METHOD2(getSupportedContentTypes,
+                 status_t(PhysicalDisplayId, std::vector<hal::ContentType>*));
+    MOCK_METHOD2(setContentType, status_t(PhysicalDisplayId, hal::ContentType));
     MOCK_CONST_METHOD0(getSupportedLayerGenericMetadata,
                        const std::unordered_map<std::string, bool>&());
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 8d1eb36..6ce8a6b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -33,7 +33,7 @@
 
 constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
 constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
-constexpr std::optional<DisplayId> DEFAULT_DISPLAY_ID = {PhysicalDisplayId(123u)};
+constexpr DisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId(123u);
 const std::string DEFAULT_DISPLAY_NAME = "Mock Display";
 
 using testing::_;
@@ -48,7 +48,7 @@
 class RenderSurfaceTest : public testing::Test {
 public:
     RenderSurfaceTest() {
-        EXPECT_CALL(mDisplay, getId()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_ID));
+        EXPECT_CALL(mDisplay, getId()).WillRepeatedly(Return(DEFAULT_DISPLAY_ID));
         EXPECT_CALL(mDisplay, getName()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_NAME));
         EXPECT_CALL(mCompositionEngine, getRenderEngine).WillRepeatedly(ReturnRef(mRenderEngine));
         EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL))
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index a1ccaad..5bd7a1f 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -168,22 +168,16 @@
         sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation);
     }
 
-    const Rect& displayBounds = getCompositionDisplay()->getState().displaySpace.bounds;
-    const int displayWidth = displayBounds.width();
-    const int displayHeight = displayBounds.height();
-
     if (!orientedDisplaySpaceRect.isValid()) {
-        // the destination frame can be invalid if it has never been set,
-        // in that case we assume the whole display frame.
-        orientedDisplaySpaceRect = Rect(displayWidth, displayHeight);
+        // The destination frame can be invalid if it has never been set,
+        // in that case we assume the whole display size.
+        orientedDisplaySpaceRect = getCompositionDisplay()->getState().displaySpace.bounds;
     }
 
     if (layerStackSpaceRect.isEmpty()) {
-        // layerStackSpaceRect can be invalid if it has never been set, in that case
-        // we assume the whole display size.
-        // It's also invalid to have an empty layerStackSpaceRect, so we handle that
-        // case in the same way.
-        layerStackSpaceRect = Rect(displayWidth, displayHeight);
+        // The layerStackSpaceRect can be invalid if it has never been set, in that case
+        // we assume the whole framebuffer size.
+        layerStackSpaceRect = getCompositionDisplay()->getState().framebufferSpace.bounds;
         if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
             std::swap(layerStackSpaceRect.right, layerStackSpaceRect.bottom);
         }
@@ -201,17 +195,12 @@
 }
 
 std::string DisplayDevice::getDebugName() const {
-    std::string displayId;
-    if (const auto id = getId()) {
-        displayId = to_string(*id) + ", ";
-    }
-
     const char* type = "virtual";
     if (mConnectionType) {
         type = *mConnectionType == DisplayConnectionType::Internal ? "internal" : "external";
     }
 
-    return base::StringPrintf("DisplayDevice{%s%s%s, \"%s\"}", displayId.c_str(), type,
+    return base::StringPrintf("DisplayDevice{%s, %s%s, \"%s\"}", to_string(getId()).c_str(), type,
                               isPrimary() ? ", primary" : "", mDisplayName.c_str());
 }
 
@@ -235,9 +224,7 @@
     return mCompositionDisplay->getDisplayColorProfile()->hasRenderIntent(intent);
 }
 
-// ----------------------------------------------------------------------------
-
-const std::optional<DisplayId>& DisplayDevice::getId() const {
+DisplayId DisplayDevice::getId() const {
     return mCompositionDisplay->getId();
 }
 
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 35a8b62..fa684c0 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -27,6 +27,7 @@
 #include <math/mat4.h>
 #include <renderengine/RenderEngine.h>
 #include <system/window.h>
+#include <ui/DisplayId.h>
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayState.h>
 #include <ui/GraphicTypes.h>
@@ -104,7 +105,15 @@
     bool needsFiltering() const;
     ui::LayerStack getLayerStack() const;
 
-    const std::optional<DisplayId>& getId() const;
+    // Returns the physical ID of this display. This function asserts the ID is physical and it
+    // shouldn't be called for other display types, e.g. virtual.
+    PhysicalDisplayId getPhysicalId() const {
+        const auto displayIdOpt = PhysicalDisplayId::tryCast(getId());
+        LOG_FATAL_IF(!displayIdOpt);
+        return *displayIdOpt;
+    }
+
+    DisplayId getId() const;
     const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
     int32_t getSequenceId() const { return mSequenceId; }
 
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 4c3b3e5..14b54cd 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -56,7 +56,7 @@
  *
  */
 
-FramebufferSurface::FramebufferSurface(HWComposer& hwc, DisplayId displayId,
+FramebufferSurface::FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
                                        const sp<IGraphicBufferConsumer>& consumer,
                                        uint32_t maxWidth, uint32_t maxHeight)
       : ConsumerBase(consumer),
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index a1859f3..759943a 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -23,6 +23,7 @@
 #include <compositionengine/DisplaySurface.h>
 #include <compositionengine/impl/HwcBufferCache.h>
 #include <gui/ConsumerBase.h>
+#include <ui/DisplayId.h>
 #include <ui/Size.h>
 
 #include "DisplayIdentification.h"
@@ -39,7 +40,7 @@
 
 class FramebufferSurface : public ConsumerBase, public compositionengine::DisplaySurface {
 public:
-    FramebufferSurface(HWComposer& hwc, DisplayId displayId,
+    FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
                        const sp<IGraphicBufferConsumer>& consumer, uint32_t maxWidth,
                        uint32_t maxHeight);
 
@@ -69,7 +70,7 @@
     status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer,
             sp<Fence>& outFence, ui::Dataspace& outDataspace);
 
-    const DisplayId mDisplayId;
+    const PhysicalDisplayId mDisplayId;
 
     // Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
     // the device.
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 1f03787..89df84b 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -240,15 +240,14 @@
 
 class Display : public HWC2::Display {
 public:
-    Display(android::Hwc2::Composer& composer,
-            const std::unordered_set<hal::Capability>& capabilities, hal::HWDisplayId id,
-            hal::DisplayType type);
+    Display(android::Hwc2::Composer&, const std::unordered_set<hal::Capability>&, hal::HWDisplayId,
+            hal::DisplayType);
     ~Display() override;
 
     // Required by HWC2
     hal::Error acceptChanges() override;
     hal::Error createLayer(Layer** outLayer) override;
-    hal::Error destroyLayer(Layer* layer) override;
+    hal::Error destroyLayer(Layer*) override;
     hal::Error getActiveConfig(std::shared_ptr<const Config>* outConfig) const override;
     hal::Error getActiveConfigIndex(int* outIndex) const override;
     hal::Error getChangedCompositionTypes(
@@ -258,8 +257,7 @@
     int32_t getSupportedPerFrameMetadata() const override;
     hal::Error getRenderIntents(hal::ColorMode colorMode,
                                 std::vector<hal::RenderIntent>* outRenderIntents) const override;
-    hal::Error getDataspaceSaturationMatrix(hal::Dataspace dataspace,
-                                            android::mat4* outMatrix) override;
+    hal::Error getDataspaceSaturationMatrix(hal::Dataspace, android::mat4* outMatrix) override;
 
     // Doesn't call into the HWC2 device, so no errors are possible
     std::vector<std::shared_ptr<const Config>> getConfigs() const override;
@@ -285,11 +283,11 @@
     hal::Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target,
                                const android::sp<android::Fence>& acquireFence,
                                hal::Dataspace dataspace) override;
-    hal::Error setColorMode(hal::ColorMode mode, hal::RenderIntent renderIntent) override;
+    hal::Error setColorMode(hal::ColorMode, hal::RenderIntent) override;
     hal::Error setColorTransform(const android::mat4& matrix, hal::ColorTransform hint) override;
-    hal::Error setOutputBuffer(const android::sp<android::GraphicBuffer>& buffer,
+    hal::Error setOutputBuffer(const android::sp<android::GraphicBuffer>&,
                                const android::sp<android::Fence>& releaseFence) override;
-    hal::Error setPowerMode(hal::PowerMode mode) override;
+    hal::Error setPowerMode(hal::PowerMode) override;
     hal::Error setVsyncEnabled(hal::Vsync enabled) override;
     hal::Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests) override;
     hal::Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
@@ -317,13 +315,13 @@
     virtual bool isVsyncPeriodSwitchSupported() const override;
 
 private:
-    int32_t getAttribute(hal::HWConfigId configId, hal::Attribute attribute);
-    void loadConfig(hal::HWConfigId configId);
+    int32_t getAttribute(hal::HWConfigId, hal::Attribute);
+    void loadConfig(hal::HWConfigId);
     void loadConfigs();
 
     // This may fail (and return a null pointer) if no layer with this ID exists
     // on this display
-    Layer* getLayerById(hal::HWLayerId id) const;
+    Layer* getLayerById(hal::HWLayerId) const;
 
     friend android::TestableSurfaceFlinger;
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 195182a..2fdbd3a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -186,7 +186,7 @@
     return mCapabilities.count(capability) > 0;
 }
 
-bool HWComposer::hasDisplayCapability(DisplayId displayId,
+bool HWComposer::hasDisplayCapability(HalDisplayId displayId,
                                       hal::DisplayCapability capability) const {
     RETURN_IF_INVALID_DISPLAY(displayId, false);
     return mDisplayData.at(displayId).hwcDisplay->getCapabilities().count(capability) > 0;
@@ -214,10 +214,8 @@
     RETURN_IF_INVALID_DISPLAY(*displayId, false);
 
     auto& displayData = mDisplayData[*displayId];
-    if (displayData.isVirtual) {
-        LOG_DISPLAY_ERROR(*displayId, "Invalid operation on virtual display");
-        return false;
-    }
+    LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
+                 __FUNCTION__, to_string(*displayId).c_str());
 
     {
         std::lock_guard lock(displayData.lastHwVsyncLock);
@@ -244,11 +242,6 @@
 
 std::optional<DisplayId> HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height,
                                                             ui::PixelFormat* format) {
-    if (mRemainingHwcVirtualDisplays == 0) {
-        ALOGE("%s: No remaining virtual displays", __FUNCTION__);
-        return {};
-    }
-
     if (SurfaceFlinger::maxVirtualDisplaySize != 0 &&
         (width > SurfaceFlinger::maxVirtualDisplaySize ||
          height > SurfaceFlinger::maxVirtualDisplaySize)) {
@@ -256,31 +249,28 @@
               height, SurfaceFlinger::maxVirtualDisplaySize);
         return {};
     }
+
+    const auto displayId = mVirtualIdGenerator.nextId();
+    if (!displayId) {
+        ALOGE("%s: No remaining virtual displays", __FUNCTION__);
+        return {};
+    }
+
     hal::HWDisplayId hwcDisplayId = 0;
     const auto error = static_cast<hal::Error>(
             mComposer->createVirtualDisplay(width, height, format, &hwcDisplayId));
     if (error != hal::Error::NONE) {
         ALOGE("%s: Failed to create HWC virtual display", __FUNCTION__);
+        mVirtualIdGenerator.markUnused(*displayId);
         return {};
     }
 
     auto display = std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities,
                                                          hwcDisplayId, hal::DisplayType::VIRTUAL);
     display->setConnected(true);
-
-    DisplayId displayId;
-    if (mFreeVirtualDisplayIds.empty()) {
-        displayId = getVirtualDisplayId(mNextVirtualDisplayId++);
-    } else {
-        displayId = *mFreeVirtualDisplayIds.begin();
-        mFreeVirtualDisplayIds.erase(displayId);
-    }
-
-    auto& displayData = mDisplayData[displayId];
+    auto& displayData = mDisplayData[*displayId];
     displayData.hwcDisplay = std::move(display);
     displayData.isVirtual = true;
-
-    --mRemainingHwcVirtualDisplays;
     return displayId;
 }
 
@@ -301,7 +291,7 @@
     mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
 }
 
-HWC2::Layer* HWComposer::createLayer(DisplayId displayId) {
+HWC2::Layer* HWComposer::createLayer(HalDisplayId displayId) {
     RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
 
     HWC2::Layer* layer;
@@ -310,14 +300,14 @@
     return layer;
 }
 
-void HWComposer::destroyLayer(DisplayId displayId, HWC2::Layer* layer) {
+void HWComposer::destroyLayer(HalDisplayId displayId, HWC2::Layer* layer) {
     RETURN_IF_INVALID_DISPLAY(displayId);
 
     auto error = mDisplayData[displayId].hwcDisplay->destroyLayer(layer);
     RETURN_IF_HWC_ERROR(error, displayId);
 }
 
-nsecs_t HWComposer::getRefreshTimestamp(DisplayId displayId) const {
+nsecs_t HWComposer::getRefreshTimestamp(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, 0);
     const auto& displayData = mDisplayData.at(displayId);
     // this returns the last refresh timestamp.
@@ -329,13 +319,13 @@
     return now - ((now - displayData.lastHwVsync) % vsyncPeriodNanos);
 }
 
-bool HWComposer::isConnected(DisplayId displayId) const {
+bool HWComposer::isConnected(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, false);
     return mDisplayData.at(displayId).hwcDisplay->isConnected();
 }
 
 std::vector<std::shared_ptr<const HWC2::Display::Config>> HWComposer::getConfigs(
-        DisplayId displayId) const {
+        PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, {});
 
     const auto& displayData = mDisplayData.at(displayId);
@@ -349,7 +339,7 @@
 }
 
 std::shared_ptr<const HWC2::Display::Config> HWComposer::getActiveConfig(
-        DisplayId displayId) const {
+        PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
 
     std::shared_ptr<const HWC2::Display::Config> config;
@@ -371,7 +361,7 @@
 
 // Composer 2.4
 
-DisplayConnectionType HWComposer::getDisplayConnectionType(DisplayId displayId) const {
+DisplayConnectionType HWComposer::getDisplayConnectionType(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, DisplayConnectionType::Internal);
     const auto& hwcDisplay = mDisplayData.at(displayId).hwcDisplay;
 
@@ -386,12 +376,12 @@
     return type;
 }
 
-bool HWComposer::isVsyncPeriodSwitchSupported(DisplayId displayId) const {
+bool HWComposer::isVsyncPeriodSwitchSupported(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, false);
     return mDisplayData.at(displayId).hwcDisplay->isVsyncPeriodSwitchSupported();
 }
 
-nsecs_t HWComposer::getDisplayVsyncPeriod(DisplayId displayId) const {
+nsecs_t HWComposer::getDisplayVsyncPeriod(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, 0);
 
     nsecs_t vsyncPeriodNanos;
@@ -400,7 +390,7 @@
     return vsyncPeriodNanos;
 }
 
-int HWComposer::getActiveConfigIndex(DisplayId displayId) const {
+int HWComposer::getActiveConfigIndex(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, -1);
 
     int index;
@@ -420,7 +410,7 @@
     return index;
 }
 
-std::vector<ui::ColorMode> HWComposer::getColorModes(DisplayId displayId) const {
+std::vector<ui::ColorMode> HWComposer::getColorModes(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, {});
 
     std::vector<ui::ColorMode> modes;
@@ -429,7 +419,7 @@
     return modes;
 }
 
-status_t HWComposer::setActiveColorMode(DisplayId displayId, ui::ColorMode mode,
+status_t HWComposer::setActiveColorMode(PhysicalDisplayId displayId, ui::ColorMode mode,
                                         ui::RenderIntent renderIntent) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
@@ -443,14 +433,12 @@
     return NO_ERROR;
 }
 
-void HWComposer::setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) {
+void HWComposer::setVsyncEnabled(PhysicalDisplayId displayId, hal::Vsync enabled) {
     RETURN_IF_INVALID_DISPLAY(displayId);
     auto& displayData = mDisplayData[displayId];
 
-    if (displayData.isVirtual) {
-        LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display");
-        return;
-    }
+    LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
+                 __FUNCTION__, to_string(displayId).c_str());
 
     // NOTE: we use our own internal lock here because we have to call
     // into the HWC with the lock held, and we want to make sure
@@ -471,7 +459,7 @@
     ATRACE_INT(tag.c_str(), enabled == hal::Vsync::ENABLE ? 1 : 0);
 }
 
-status_t HWComposer::setClientTarget(DisplayId displayId, uint32_t slot,
+status_t HWComposer::setClientTarget(HalDisplayId displayId, uint32_t slot,
                                      const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
                                      ui::Dataspace dataspace) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -484,7 +472,7 @@
 }
 
 status_t HWComposer::getDeviceCompositionChanges(
-        DisplayId displayId, bool frameUsesClientComposition,
+        HalDisplayId displayId, bool frameUsesClientComposition,
         std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
     ATRACE_CALL();
 
@@ -554,12 +542,12 @@
     return NO_ERROR;
 }
 
-sp<Fence> HWComposer::getPresentFence(DisplayId displayId) const {
+sp<Fence> HWComposer::getPresentFence(HalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
     return mDisplayData.at(displayId).lastPresentFence;
 }
 
-sp<Fence> HWComposer::getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const {
+sp<Fence> HWComposer::getLayerReleaseFence(HalDisplayId displayId, HWC2::Layer* layer) const {
     RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
     const auto& displayFences = mDisplayData.at(displayId).releaseFences;
     auto fence = displayFences.find(layer);
@@ -570,7 +558,7 @@
     return fence->second;
 }
 
-status_t HWComposer::presentAndGetReleaseFences(DisplayId displayId) {
+status_t HWComposer::presentAndGetReleaseFences(HalDisplayId displayId) {
     ATRACE_CALL();
 
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -598,14 +586,12 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setPowerMode(DisplayId displayId, hal::PowerMode mode) {
+status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mode) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     const auto& displayData = mDisplayData[displayId];
-    if (displayData.isVirtual) {
-        LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display");
-        return INVALID_OPERATION;
-    }
+    LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
+                 __FUNCTION__, to_string(displayId).c_str());
 
     if (mode == hal::PowerMode::OFF) {
         setVsyncEnabled(displayId, hal::Vsync::DISABLE);
@@ -654,7 +640,8 @@
 }
 
 status_t HWComposer::setActiveConfigWithConstraints(
-        DisplayId displayId, size_t configId, const hal::VsyncPeriodChangeConstraints& constraints,
+        PhysicalDisplayId displayId, size_t configId,
+        const hal::VsyncPeriodChangeConstraints& constraints,
         hal::VsyncPeriodChangeTimeline* outTimeline) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
@@ -671,7 +658,7 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setColorTransform(DisplayId displayId, const mat4& transform) {
+status_t HWComposer::setColorTransform(HalDisplayId displayId, const mat4& transform) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     auto& displayData = mDisplayData[displayId];
@@ -684,15 +671,14 @@
     return NO_ERROR;
 }
 
-void HWComposer::disconnectDisplay(DisplayId displayId) {
+void HWComposer::disconnectDisplay(HalDisplayId displayId) {
     RETURN_IF_INVALID_DISPLAY(displayId);
     auto& displayData = mDisplayData[displayId];
 
     // If this was a virtual display, add its slot back for reuse by future
     // virtual displays
     if (displayData.isVirtual) {
-        mFreeVirtualDisplayIds.insert(displayId);
-        ++mRemainingHwcVirtualDisplays;
+        mVirtualIdGenerator.markUnused(*HalVirtualDisplayId::tryCast(displayId));
     }
 
     const auto hwcDisplayId = displayData.hwcDisplay->getId();
@@ -707,27 +693,25 @@
     mDisplayData.erase(displayId);
 }
 
-status_t HWComposer::setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
+status_t HWComposer::setOutputBuffer(HalVirtualDisplayId displayId, const sp<Fence>& acquireFence,
                                      const sp<GraphicBuffer>& buffer) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto& displayData = mDisplayData[displayId];
 
-    if (!displayData.isVirtual) {
-        LOG_DISPLAY_ERROR(displayId, "Invalid operation on physical display");
-        return INVALID_OPERATION;
-    }
+    LOG_FATAL_IF(!displayData.isVirtual, "%s: Invalid operation on physical display with ID %s",
+                 __FUNCTION__, to_string(displayId).c_str());
 
     auto error = displayData.hwcDisplay->setOutputBuffer(buffer, acquireFence);
     RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
     return NO_ERROR;
 }
 
-void HWComposer::clearReleaseFences(DisplayId displayId) {
+void HWComposer::clearReleaseFences(HalDisplayId displayId) {
     RETURN_IF_INVALID_DISPLAY(displayId);
     mDisplayData[displayId].releaseFences.clear();
 }
 
-status_t HWComposer::getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) {
+status_t HWComposer::getHdrCapabilities(HalDisplayId displayId, HdrCapabilities* outCapabilities) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
@@ -736,12 +720,12 @@
     return NO_ERROR;
 }
 
-int32_t HWComposer::getSupportedPerFrameMetadata(DisplayId displayId) const {
+int32_t HWComposer::getSupportedPerFrameMetadata(HalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, 0);
     return mDisplayData.at(displayId).hwcDisplay->getSupportedPerFrameMetadata();
 }
 
-std::vector<ui::RenderIntent> HWComposer::getRenderIntents(DisplayId displayId,
+std::vector<ui::RenderIntent> HWComposer::getRenderIntents(HalDisplayId displayId,
                                                            ui::ColorMode colorMode) const {
     RETURN_IF_INVALID_DISPLAY(displayId, {});
 
@@ -751,7 +735,7 @@
     return renderIntents;
 }
 
-mat4 HWComposer::getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) {
+mat4 HWComposer::getDataspaceSaturationMatrix(HalDisplayId displayId, ui::Dataspace dataspace) {
     RETURN_IF_INVALID_DISPLAY(displayId, {});
 
     mat4 matrix;
@@ -761,7 +745,7 @@
     return matrix;
 }
 
-status_t HWComposer::getDisplayedContentSamplingAttributes(DisplayId displayId,
+status_t HWComposer::getDisplayedContentSamplingAttributes(HalDisplayId displayId,
                                                            ui::PixelFormat* outFormat,
                                                            ui::Dataspace* outDataspace,
                                                            uint8_t* outComponentMask) {
@@ -775,7 +759,7 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
+status_t HWComposer::setDisplayContentSamplingEnabled(HalDisplayId displayId, bool enabled,
                                                       uint8_t componentMask, uint64_t maxFrames) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto error =
@@ -789,7 +773,7 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames,
+status_t HWComposer::getDisplayedContentSample(HalDisplayId displayId, uint64_t maxFrames,
                                                uint64_t timestamp, DisplayedFrameStats* outStats) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto error =
@@ -799,7 +783,8 @@
     return NO_ERROR;
 }
 
-std::future<status_t> HWComposer::setDisplayBrightness(DisplayId displayId, float brightness) {
+std::future<status_t> HWComposer::setDisplayBrightness(PhysicalDisplayId displayId,
+                                                       float brightness) {
     RETURN_IF_INVALID_DISPLAY(displayId, promise::yield<status_t>(BAD_INDEX));
     auto& display = mDisplayData[displayId].hwcDisplay;
 
@@ -816,7 +801,7 @@
             });
 }
 
-status_t HWComposer::setAutoLowLatencyMode(DisplayId displayId, bool on) {
+status_t HWComposer::setAutoLowLatencyMode(PhysicalDisplayId displayId, bool on) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
     if (error == hal::Error::UNSUPPORTED) {
@@ -830,7 +815,7 @@
 }
 
 status_t HWComposer::getSupportedContentTypes(
-        DisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) {
+        PhysicalDisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto error =
             mDisplayData[displayId].hwcDisplay->getSupportedContentTypes(outSupportedContentTypes);
@@ -840,7 +825,7 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setContentType(DisplayId displayId, hal::ContentType contentType) {
+status_t HWComposer::setContentType(PhysicalDisplayId displayId, hal::ContentType contentType) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto error = mDisplayData[displayId].hwcDisplay->setContentType(contentType);
     if (error == hal::Error::UNSUPPORTED) {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 488cdc5..028a9a4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -38,6 +38,7 @@
 #include <utils/StrongPointer.h>
 #include <utils/Timers.h>
 
+#include "DisplayIdGenerator.h"
 #include "DisplayIdentification.h"
 #include "HWC2.h"
 #include "Hal.h"
@@ -88,7 +89,7 @@
                                               DisplayIdentificationData* outData) const = 0;
 
     virtual bool hasCapability(hal::Capability) const = 0;
-    virtual bool hasDisplayCapability(DisplayId, hal::DisplayCapability) const = 0;
+    virtual bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const = 0;
 
     // Attempts to allocate a virtual display and returns its ID if created on the HWC device.
     virtual std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
@@ -97,9 +98,9 @@
     virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0;
 
     // Attempts to create a new layer on this display
-    virtual HWC2::Layer* createLayer(DisplayId) = 0;
+    virtual HWC2::Layer* createLayer(HalDisplayId) = 0;
     // Destroy a previously created layer
-    virtual void destroyLayer(DisplayId, HWC2::Layer*) = 0;
+    virtual void destroyLayer(HalDisplayId, HWC2::Layer*) = 0;
 
     // Gets any required composition change requests from the HWC device.
     //
@@ -109,61 +110,60 @@
     // with fewer handshakes, but this does not work if client composition is
     // expected.
     virtual status_t getDeviceCompositionChanges(
-            DisplayId, bool frameUsesClientComposition,
+            HalDisplayId, bool frameUsesClientComposition,
             std::optional<DeviceRequestedChanges>* outChanges) = 0;
 
-    virtual status_t setClientTarget(DisplayId, uint32_t slot, const sp<Fence>& acquireFence,
+    virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
                                      const sp<GraphicBuffer>& target, ui::Dataspace) = 0;
 
     // Present layers to the display and read releaseFences.
-    virtual status_t presentAndGetReleaseFences(DisplayId) = 0;
+    virtual status_t presentAndGetReleaseFences(HalDisplayId) = 0;
 
     // set power mode
-    virtual status_t setPowerMode(DisplayId, hal::PowerMode) = 0;
+    virtual status_t setPowerMode(PhysicalDisplayId, hal::PowerMode) = 0;
 
     // Sets a color transform to be applied to the result of composition
-    virtual status_t setColorTransform(DisplayId, const mat4& transform) = 0;
+    virtual status_t setColorTransform(HalDisplayId, const mat4& transform) = 0;
 
-    // reset state when an external, non-virtual display is disconnected
-    virtual void disconnectDisplay(DisplayId) = 0;
+    // reset state when a display is disconnected
+    virtual void disconnectDisplay(HalDisplayId) = 0;
 
     // get the present fence received from the last call to present.
-    virtual sp<Fence> getPresentFence(DisplayId) const = 0;
+    virtual sp<Fence> getPresentFence(HalDisplayId) const = 0;
 
     // Get last release fence for the given layer
-    virtual sp<Fence> getLayerReleaseFence(DisplayId, HWC2::Layer*) const = 0;
+    virtual sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const = 0;
 
     // Set the output buffer and acquire fence for a virtual display.
-    // Returns INVALID_OPERATION if displayId is not a virtual display.
-    virtual status_t setOutputBuffer(DisplayId, const sp<Fence>& acquireFence,
+    virtual status_t setOutputBuffer(HalVirtualDisplayId, const sp<Fence>& acquireFence,
                                      const sp<GraphicBuffer>& buffer) = 0;
 
     // After SurfaceFlinger has retrieved the release fences for all the frames,
     // it can call this to clear the shared pointers in the release fence map
-    virtual void clearReleaseFences(DisplayId) = 0;
+    virtual void clearReleaseFences(HalDisplayId) = 0;
 
     // Fetches the HDR capabilities of the given display
-    virtual status_t getHdrCapabilities(DisplayId, HdrCapabilities* outCapabilities) = 0;
+    virtual status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) = 0;
 
-    virtual int32_t getSupportedPerFrameMetadata(DisplayId) const = 0;
+    virtual int32_t getSupportedPerFrameMetadata(HalDisplayId) const = 0;
 
     // Returns the available RenderIntent of the given display.
-    virtual std::vector<ui::RenderIntent> getRenderIntents(DisplayId, ui::ColorMode) const = 0;
+    virtual std::vector<ui::RenderIntent> getRenderIntents(HalDisplayId, ui::ColorMode) const = 0;
 
-    virtual mat4 getDataspaceSaturationMatrix(DisplayId, ui::Dataspace) = 0;
+    virtual mat4 getDataspaceSaturationMatrix(HalDisplayId, ui::Dataspace) = 0;
 
     // Returns the attributes of the color sampling engine.
-    virtual status_t getDisplayedContentSamplingAttributes(DisplayId, ui::PixelFormat* outFormat,
+    virtual status_t getDisplayedContentSamplingAttributes(HalDisplayId, ui::PixelFormat* outFormat,
                                                            ui::Dataspace* outDataspace,
                                                            uint8_t* outComponentMask) = 0;
-    virtual status_t setDisplayContentSamplingEnabled(DisplayId, bool enabled,
+    virtual status_t setDisplayContentSamplingEnabled(HalDisplayId, bool enabled,
                                                       uint8_t componentMask,
                                                       uint64_t maxFrames) = 0;
-    virtual status_t getDisplayedContentSample(DisplayId, uint64_t maxFrames, uint64_t timestamp,
+    virtual status_t getDisplayedContentSample(HalDisplayId, uint64_t maxFrames, uint64_t timestamp,
                                                DisplayedFrameStats* outStats) = 0;
 
     // Sets the brightness of a display.
-    virtual std::future<status_t> setDisplayBrightness(DisplayId, float brightness) = 0;
+    virtual std::future<status_t> setDisplayBrightness(PhysicalDisplayId, float brightness) = 0;
 
     // Events handling ---------------------------------------------------------
 
@@ -174,33 +174,35 @@
                                                                hal::Connection) = 0;
 
     virtual bool onVsync(hal::HWDisplayId, int64_t timestamp) = 0;
-    virtual void setVsyncEnabled(DisplayId, hal::Vsync enabled) = 0;
+    virtual void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) = 0;
 
-    virtual nsecs_t getRefreshTimestamp(DisplayId) const = 0;
-    virtual bool isConnected(DisplayId) const = 0;
+    virtual nsecs_t getRefreshTimestamp(PhysicalDisplayId) const = 0;
+    virtual bool isConnected(PhysicalDisplayId) const = 0;
 
     // Non-const because it can update configMap inside of mDisplayData
     virtual std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
-            DisplayId) const = 0;
+            PhysicalDisplayId) const = 0;
 
-    virtual std::shared_ptr<const HWC2::Display::Config> getActiveConfig(DisplayId) const = 0;
-    virtual int getActiveConfigIndex(DisplayId) const = 0;
+    virtual std::shared_ptr<const HWC2::Display::Config> getActiveConfig(
+            PhysicalDisplayId) const = 0;
+    virtual int getActiveConfigIndex(PhysicalDisplayId) const = 0;
 
-    virtual std::vector<ui::ColorMode> getColorModes(DisplayId) const = 0;
+    virtual std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const = 0;
 
-    virtual status_t setActiveColorMode(DisplayId, ui::ColorMode mode, ui::RenderIntent) = 0;
+    virtual status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode mode,
+                                        ui::RenderIntent) = 0;
 
     // Composer 2.4
-    virtual DisplayConnectionType getDisplayConnectionType(DisplayId) const = 0;
-    virtual bool isVsyncPeriodSwitchSupported(DisplayId) const = 0;
-    virtual nsecs_t getDisplayVsyncPeriod(DisplayId) const = 0;
+    virtual DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const = 0;
+    virtual bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const = 0;
+    virtual nsecs_t getDisplayVsyncPeriod(PhysicalDisplayId) const = 0;
     virtual status_t setActiveConfigWithConstraints(
-            DisplayId, size_t configId, const hal::VsyncPeriodChangeConstraints&,
+            PhysicalDisplayId, size_t configId, const hal::VsyncPeriodChangeConstraints&,
             hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
-    virtual status_t setAutoLowLatencyMode(DisplayId, bool on) = 0;
+    virtual status_t setAutoLowLatencyMode(PhysicalDisplayId, bool on) = 0;
     virtual status_t getSupportedContentTypes(
-            DisplayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0;
-    virtual status_t setContentType(DisplayId, hal::ContentType) = 0;
+            PhysicalDisplayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0;
+    virtual status_t setContentType(PhysicalDisplayId, hal::ContentType) = 0;
     virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata()
             const = 0;
 
@@ -232,7 +234,7 @@
                                       DisplayIdentificationData* outData) const override;
 
     bool hasCapability(hal::Capability) const override;
-    bool hasDisplayCapability(DisplayId, hal::DisplayCapability) const override;
+    bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const override;
 
     // Attempts to allocate a virtual display and returns its ID if created on the HWC device.
     std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
@@ -242,63 +244,62 @@
     void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override;
 
     // Attempts to create a new layer on this display
-    HWC2::Layer* createLayer(DisplayId) override;
+    HWC2::Layer* createLayer(HalDisplayId) override;
     // Destroy a previously created layer
-    void destroyLayer(DisplayId, HWC2::Layer*) override;
+    void destroyLayer(HalDisplayId, HWC2::Layer*) override;
 
     status_t getDeviceCompositionChanges(
-            DisplayId, bool frameUsesClientComposition,
+            HalDisplayId, bool frameUsesClientComposition,
             std::optional<DeviceRequestedChanges>* outChanges) override;
 
-    status_t setClientTarget(DisplayId, uint32_t slot, const sp<Fence>& acquireFence,
+    status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
                              const sp<GraphicBuffer>& target, ui::Dataspace) override;
 
     // Present layers to the display and read releaseFences.
-    status_t presentAndGetReleaseFences(DisplayId) override;
+    status_t presentAndGetReleaseFences(HalDisplayId) override;
 
     // set power mode
-    status_t setPowerMode(DisplayId, hal::PowerMode mode) override;
+    status_t setPowerMode(PhysicalDisplayId, hal::PowerMode mode) override;
 
     // Sets a color transform to be applied to the result of composition
-    status_t setColorTransform(DisplayId, const mat4& transform) override;
+    status_t setColorTransform(HalDisplayId, const mat4& transform) override;
 
-    // reset state when an external, non-virtual display is disconnected
-    void disconnectDisplay(DisplayId) override;
+    // reset state when a display is disconnected
+    void disconnectDisplay(HalDisplayId) override;
 
     // get the present fence received from the last call to present.
-    sp<Fence> getPresentFence(DisplayId) const override;
+    sp<Fence> getPresentFence(HalDisplayId) const override;
 
     // Get last release fence for the given layer
-    sp<Fence> getLayerReleaseFence(DisplayId, HWC2::Layer*) const override;
+    sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const override;
 
     // Set the output buffer and acquire fence for a virtual display.
-    // Returns INVALID_OPERATION if displayId is not a virtual display.
-    status_t setOutputBuffer(DisplayId, const sp<Fence>& acquireFence,
+    status_t setOutputBuffer(HalVirtualDisplayId, const sp<Fence>& acquireFence,
                              const sp<GraphicBuffer>& buffer) override;
 
     // After SurfaceFlinger has retrieved the release fences for all the frames,
     // it can call this to clear the shared pointers in the release fence map
-    void clearReleaseFences(DisplayId) override;
+    void clearReleaseFences(HalDisplayId) override;
 
     // Fetches the HDR capabilities of the given display
-    status_t getHdrCapabilities(DisplayId, HdrCapabilities* outCapabilities) override;
+    status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) override;
 
-    int32_t getSupportedPerFrameMetadata(DisplayId) const override;
+    int32_t getSupportedPerFrameMetadata(HalDisplayId) const override;
 
     // Returns the available RenderIntent of the given display.
-    std::vector<ui::RenderIntent> getRenderIntents(DisplayId, ui::ColorMode) const override;
+    std::vector<ui::RenderIntent> getRenderIntents(HalDisplayId, ui::ColorMode) const override;
 
-    mat4 getDataspaceSaturationMatrix(DisplayId, ui::Dataspace) override;
+    mat4 getDataspaceSaturationMatrix(HalDisplayId, ui::Dataspace) override;
 
     // Returns the attributes of the color sampling engine.
-    status_t getDisplayedContentSamplingAttributes(DisplayId, ui::PixelFormat* outFormat,
+    status_t getDisplayedContentSamplingAttributes(HalDisplayId, ui::PixelFormat* outFormat,
                                                    ui::Dataspace* outDataspace,
                                                    uint8_t* outComponentMask) override;
-    status_t setDisplayContentSamplingEnabled(DisplayId, bool enabled, uint8_t componentMask,
+    status_t setDisplayContentSamplingEnabled(HalDisplayId, bool enabled, uint8_t componentMask,
                                               uint64_t maxFrames) override;
-    status_t getDisplayedContentSample(DisplayId, uint64_t maxFrames, uint64_t timestamp,
+    status_t getDisplayedContentSample(HalDisplayId, uint64_t maxFrames, uint64_t timestamp,
                                        DisplayedFrameStats* outStats) override;
-    std::future<status_t> setDisplayBrightness(DisplayId, float brightness) override;
+    std::future<status_t> setDisplayBrightness(PhysicalDisplayId, float brightness) override;
 
     // Events handling ---------------------------------------------------------
 
@@ -307,31 +308,32 @@
     std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId, hal::Connection) override;
 
     bool onVsync(hal::HWDisplayId, int64_t timestamp) override;
-    void setVsyncEnabled(DisplayId, hal::Vsync enabled) override;
+    void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) override;
 
-    nsecs_t getRefreshTimestamp(DisplayId) const override;
-    bool isConnected(DisplayId) const override;
+    nsecs_t getRefreshTimestamp(PhysicalDisplayId) const override;
+    bool isConnected(PhysicalDisplayId) const override;
 
     // Non-const because it can update configMap inside of mDisplayData
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(DisplayId) const override;
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
+            PhysicalDisplayId) const override;
 
-    std::shared_ptr<const HWC2::Display::Config> getActiveConfig(DisplayId) const override;
-    int getActiveConfigIndex(DisplayId) const override;
+    std::shared_ptr<const HWC2::Display::Config> getActiveConfig(PhysicalDisplayId) const override;
+    int getActiveConfigIndex(PhysicalDisplayId) const override;
 
-    std::vector<ui::ColorMode> getColorModes(DisplayId) const override;
+    std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const override;
 
-    status_t setActiveColorMode(DisplayId, ui::ColorMode, ui::RenderIntent) override;
+    status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent) override;
 
     // Composer 2.4
-    DisplayConnectionType getDisplayConnectionType(DisplayId) const override;
-    bool isVsyncPeriodSwitchSupported(DisplayId) const override;
-    nsecs_t getDisplayVsyncPeriod(DisplayId) const override;
-    status_t setActiveConfigWithConstraints(DisplayId, size_t configId,
+    DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override;
+    bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override;
+    nsecs_t getDisplayVsyncPeriod(PhysicalDisplayId) const override;
+    status_t setActiveConfigWithConstraints(PhysicalDisplayId, size_t configId,
                                             const hal::VsyncPeriodChangeConstraints&,
                                             hal::VsyncPeriodChangeTimeline* outTimeline) override;
-    status_t setAutoLowLatencyMode(DisplayId, bool) override;
-    status_t getSupportedContentTypes(DisplayId, std::vector<hal::ContentType>*) override;
-    status_t setContentType(DisplayId, hal::ContentType) override;
+    status_t setAutoLowLatencyMode(PhysicalDisplayId, bool) override;
+    status_t getSupportedContentTypes(PhysicalDisplayId, std::vector<hal::ContentType>*) override;
+    status_t setContentType(PhysicalDisplayId, hal::ContentType) override;
 
     const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata() const override;
 
@@ -385,7 +387,7 @@
         nsecs_t lastHwVsync GUARDED_BY(lastHwVsyncLock) = 0;
     };
 
-    std::unordered_map<DisplayId, DisplayData> mDisplayData;
+    std::unordered_map<HalDisplayId, DisplayData> mDisplayData;
 
     std::unique_ptr<android::Hwc2::Composer> mComposer;
     std::unordered_set<hal::Capability> mCapabilities;
@@ -397,9 +399,7 @@
     std::optional<hal::HWDisplayId> mExternalHwcDisplayId;
     bool mHasMultiDisplaySupport = false;
 
-    std::unordered_set<DisplayId> mFreeVirtualDisplayIds;
-    uint32_t mNextVirtualDisplayId = 0;
-    uint32_t mRemainingHwcVirtualDisplays{getMaxVirtualDisplayCount()};
+    RandomDisplayIdGenerator<HalVirtualDisplayId> mVirtualIdGenerator{getMaxVirtualDisplayCount()};
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index fba3261..247ee23 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -57,8 +57,7 @@
     }
 }
 
-VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc,
-                                             const std::optional<DisplayId>& displayId,
+VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId displayId,
                                              const sp<IGraphicBufferProducer>& sink,
                                              const sp<IGraphicBufferProducer>& bqProducer,
                                              const sp<IGraphicBufferConsumer>& bqConsumer,
@@ -125,7 +124,7 @@
 }
 
 status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) {
-    if (!mDisplayId) {
+    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
         return NO_ERROR;
     }
 
@@ -139,7 +138,7 @@
 }
 
 status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) {
-    if (!mDisplayId) {
+    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
         return NO_ERROR;
     }
 
@@ -187,7 +186,7 @@
 }
 
 status_t VirtualDisplaySurface::advanceFrame() {
-    if (!mDisplayId) {
+    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
         return NO_ERROR;
     }
 
@@ -219,9 +218,11 @@
             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(*mDisplayId, mOutputFence, outBuffer);
+    mHwc.setOutputBuffer(*halDisplayId, mOutputFence, outBuffer);
 
     status_t result = NO_ERROR;
     if (fbBuffer != nullptr) {
@@ -230,7 +231,7 @@
         mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer, &hwcSlot, &hwcBuffer);
 
         // TODO: Correctly propagate the dataspace from GL composition
-        result = mHwc.setClientTarget(*mDisplayId, hwcSlot, mFbFence, hwcBuffer,
+        result = mHwc.setClientTarget(*halDisplayId, hwcSlot, mFbFence, hwcBuffer,
                                       ui::Dataspace::UNKNOWN);
     }
 
@@ -238,7 +239,8 @@
 }
 
 void VirtualDisplaySurface::onFrameCommitted() {
-    if (!mDisplayId) {
+    const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
+    if (!halDisplayId) {
         return;
     }
 
@@ -246,7 +248,7 @@
             "Unexpected onFrameCommitted() in %s state", dbgStateStr());
     mDbgState = DBG_STATE_IDLE;
 
-    sp<Fence> retireFence = mHwc.getPresentFence(*mDisplayId);
+    sp<Fence> retireFence = mHwc.getPresentFence(*halDisplayId);
     if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) {
         // release the scratch buffer back to the pool
         Mutex::Autolock lock(mMutex);
@@ -301,7 +303,7 @@
 
 status_t VirtualDisplaySurface::requestBuffer(int pslot,
         sp<GraphicBuffer>* outBuf) {
-    if (!mDisplayId) {
+    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
         return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
     }
 
@@ -323,7 +325,7 @@
 
 status_t VirtualDisplaySurface::dequeueBuffer(Source source,
         PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) {
-    LOG_FATAL_IF(!mDisplayId);
+    LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId));
 
     status_t result =
             mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight,
@@ -369,7 +371,7 @@
                                               PixelFormat format, uint64_t usage,
                                               uint64_t* outBufferAge,
                                               FrameEventHistoryDelta* outTimestamps) {
-    if (!mDisplayId) {
+    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
         return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage, outBufferAge,
                                                    outTimestamps);
     }
@@ -456,7 +458,7 @@
 
 status_t VirtualDisplaySurface::queueBuffer(int pslot,
         const QueueBufferInput& input, QueueBufferOutput* output) {
-    if (!mDisplayId) {
+    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
         return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
     }
 
@@ -514,7 +516,7 @@
 
 status_t VirtualDisplaySurface::cancelBuffer(int pslot,
         const sp<Fence>& fence) {
-    if (!mDisplayId) {
+    if (GpuVirtualDisplayId::tryCast(mDisplayId)) {
         return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
     }
 
@@ -626,7 +628,7 @@
 }
 
 status_t VirtualDisplaySurface::refreshOutputBuffer() {
-    LOG_FATAL_IF(!mDisplayId);
+    LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId));
 
     if (mOutputProducerSlot >= 0) {
         mSource[SOURCE_SINK]->cancelBuffer(
@@ -645,7 +647,9 @@
     // 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.
-    result = mHwc.setOutputBuffer(*mDisplayId, Fence::NO_FENCE,
+    const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
+    LOG_FATAL_IF(!halDisplayId);
+    result = mHwc.setOutputBuffer(*halDisplayId, Fence::NO_FENCE,
                                   mProducerBuffers[mOutputProducerSlot]);
 
     return result;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 3cbad8f..1974625 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -24,6 +24,7 @@
 #include <compositionengine/impl/HwcBufferCache.h>
 #include <gui/ConsumerBase.h>
 #include <gui/IGraphicBufferProducer.h>
+#include <ui/DisplayId.h>
 
 #include "DisplayIdentification.h"
 
@@ -77,8 +78,7 @@
                               public BnGraphicBufferProducer,
                               private ConsumerBase {
 public:
-    VirtualDisplaySurface(HWComposer& hwc, const std::optional<DisplayId>& displayId,
-                          const sp<IGraphicBufferProducer>& sink,
+    VirtualDisplaySurface(HWComposer&, VirtualDisplayId, const sp<IGraphicBufferProducer>& sink,
                           const sp<IGraphicBufferProducer>& bqProducer,
                           const sp<IGraphicBufferConsumer>& bqConsumer, const std::string& name);
 
@@ -86,7 +86,7 @@
     // DisplaySurface interface
     //
     virtual status_t beginFrame(bool mustRecompose);
-    virtual status_t prepareFrame(CompositionType compositionType);
+    virtual status_t prepareFrame(CompositionType);
     virtual status_t advanceFrame();
     virtual void onFrameCommitted();
     virtual void dumpAsString(String8& result) const;
@@ -104,25 +104,22 @@
     virtual status_t requestBuffer(int pslot, sp<GraphicBuffer>* outBuf);
     virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
     virtual status_t setAsyncMode(bool async);
-    virtual status_t dequeueBuffer(int* pslot, sp<Fence>* fence, uint32_t w, uint32_t h,
-                                   PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
+    virtual status_t dequeueBuffer(int* pslot, sp<Fence>*, uint32_t w, uint32_t h, PixelFormat,
+                                   uint64_t usage, uint64_t* outBufferAge,
                                    FrameEventHistoryDelta* outTimestamps);
     virtual status_t detachBuffer(int slot);
-    virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
-            sp<Fence>* outFence);
-    virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer);
-    virtual status_t queueBuffer(int pslot,
-            const QueueBufferInput& input, QueueBufferOutput* output);
-    virtual status_t cancelBuffer(int pslot, const sp<Fence>& fence);
+    virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence);
+    virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>&);
+    virtual status_t queueBuffer(int pslot, const QueueBufferInput&, QueueBufferOutput*);
+    virtual status_t cancelBuffer(int pslot, const sp<Fence>&);
     virtual int query(int what, int* value);
-    virtual status_t connect(const sp<IProducerListener>& listener,
-            int api, bool producerControlledByApp, QueueBufferOutput* output);
-    virtual status_t disconnect(int api, DisconnectMode mode);
+    virtual status_t connect(const sp<IProducerListener>&, int api, bool producerControlledByApp,
+                             QueueBufferOutput*);
+    virtual status_t disconnect(int api, DisconnectMode);
     virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
-    virtual void allocateBuffers(uint32_t width, uint32_t height,
-            PixelFormat format, uint64_t usage);
+    virtual void allocateBuffers(uint32_t width, uint32_t height, PixelFormat, uint64_t usage);
     virtual status_t allowAllocation(bool allow);
-    virtual status_t setGenerationNumber(uint32_t generationNumber);
+    virtual status_t setGenerationNumber(uint32_t);
     virtual String8 getConsumerName() const override;
     virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
     virtual status_t setAutoRefresh(bool autoRefresh) override;
@@ -135,10 +132,9 @@
     //
     // Utility methods
     //
-    static Source fbSourceForCompositionType(CompositionType type);
-    status_t dequeueBuffer(Source source, PixelFormat format, uint64_t usage,
-            int* sslot, sp<Fence>* fence);
-    void updateQueueBufferOutput(QueueBufferOutput&& qbo);
+    static Source fbSourceForCompositionType(CompositionType);
+    status_t dequeueBuffer(Source, PixelFormat, uint64_t usage, int* sslot, sp<Fence>*);
+    void updateQueueBufferOutput(QueueBufferOutput&&);
     void resetPerFrameState();
     status_t refreshOutputBuffer();
 
@@ -148,14 +144,14 @@
     // internally in the VirtualDisplaySurface. To minimize the number of times
     // a producer slot switches which source it comes from, we map source slot
     // numbers to producer slot numbers differently for each source.
-    static int mapSource2ProducerSlot(Source source, int sslot);
-    static int mapProducer2SourceSlot(Source source, int pslot);
+    static int mapSource2ProducerSlot(Source, int sslot);
+    static int mapProducer2SourceSlot(Source, int pslot);
 
     //
     // Immutable after construction
     //
     HWComposer& mHwc;
-    const std::optional<DisplayId> mDisplayId;
+    const VirtualDisplayId mDisplayId;
     const std::string mDisplayName;
     sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
     uint32_t mDefaultOutputFormat;
diff --git a/services/surfaceflinger/DisplayIdGenerator.h b/services/surfaceflinger/DisplayIdGenerator.h
new file mode 100644
index 0000000..e7c69a8
--- /dev/null
+++ b/services/surfaceflinger/DisplayIdGenerator.h
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <ui/DisplayId.h>
+
+#include <limits>
+#include <optional>
+#include <random>
+#include <unordered_set>
+
+#include <log/log.h>
+
+namespace android {
+
+template <typename T>
+class DisplayIdGenerator {
+public:
+    virtual std::optional<T> nextId() = 0;
+    virtual void markUnused(T id) = 0;
+
+protected:
+    ~DisplayIdGenerator() {}
+};
+
+template <typename T>
+class RandomDisplayIdGenerator final : public DisplayIdGenerator<T> {
+public:
+    explicit RandomDisplayIdGenerator(size_t maxIdsCount = std::numeric_limits<size_t>::max())
+          : mMaxIdsCount(maxIdsCount) {}
+
+    std::optional<T> nextId() override {
+        if (mUsedIds.size() >= mMaxIdsCount) {
+            return std::nullopt;
+        }
+
+        constexpr int kMaxAttempts = 1000;
+
+        for (int attempts = 0; attempts < kMaxAttempts; attempts++) {
+            const auto baseId = mDistribution(mGenerator);
+            const T id(baseId);
+            if (mUsedIds.count(id) == 0) {
+                mUsedIds.insert(id);
+                return id;
+            }
+        }
+
+        LOG_ALWAYS_FATAL("Couldn't generate ID after %d attempts", kMaxAttempts);
+    }
+
+    void markUnused(T id) override { mUsedIds.erase(id); }
+
+private:
+    const size_t mMaxIdsCount;
+
+    std::unordered_set<T> mUsedIds;
+    std::default_random_engine mGenerator{std::random_device()()};
+    std::uniform_int_distribution<typename T::BaseId> mDistribution;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 9985253..43176a3 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -22,12 +22,127 @@
 #include <android-base/stringprintf.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
+#include <chrono>
 #include <cinttypes>
+#include <numeric>
 
 namespace android::frametimeline::impl {
 
 using base::StringAppendF;
 
+void dumpTable(std::string& result, TimelineItem predictions, TimelineItem actuals,
+               const std::string& indent, PredictionState predictionState, nsecs_t baseTime) {
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "\t\t");
+    StringAppendF(&result, "    Start time\t\t|");
+    StringAppendF(&result, "    End time\t\t|");
+    StringAppendF(&result, "    Present time\n");
+    if (predictionState == PredictionState::Valid) {
+        // Dump the Predictions only if they are valid
+        StringAppendF(&result, "%s", indent.c_str());
+        StringAppendF(&result, "Expected\t|");
+        std::chrono::nanoseconds startTime(predictions.startTime - baseTime);
+        std::chrono::nanoseconds endTime(predictions.endTime - baseTime);
+        std::chrono::nanoseconds presentTime(predictions.presentTime - baseTime);
+        StringAppendF(&result, "\t%10.2f\t|\t%10.2f\t|\t%10.2f\n",
+                      std::chrono::duration<double, std::milli>(startTime).count(),
+                      std::chrono::duration<double, std::milli>(endTime).count(),
+                      std::chrono::duration<double, std::milli>(presentTime).count());
+    }
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Actual  \t|");
+
+    if (actuals.startTime == 0) {
+        StringAppendF(&result, "\t\tN/A\t|");
+    } else {
+        std::chrono::nanoseconds startTime(std::max<nsecs_t>(0, actuals.startTime - baseTime));
+        StringAppendF(&result, "\t%10.2f\t|",
+                      std::chrono::duration<double, std::milli>(startTime).count());
+    }
+    if (actuals.endTime == 0) {
+        StringAppendF(&result, "\t\tN/A\t|");
+    } else {
+        std::chrono::nanoseconds endTime(actuals.endTime - baseTime);
+        StringAppendF(&result, "\t%10.2f\t|",
+                      std::chrono::duration<double, std::milli>(endTime).count());
+    }
+    if (actuals.presentTime == 0) {
+        StringAppendF(&result, "\t\tN/A\n");
+    } else {
+        std::chrono::nanoseconds presentTime(std::max<nsecs_t>(0, actuals.presentTime - baseTime));
+        StringAppendF(&result, "\t%10.2f\n",
+                      std::chrono::duration<double, std::milli>(presentTime).count());
+    }
+
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "----------------------");
+    StringAppendF(&result, "----------------------");
+    StringAppendF(&result, "----------------------");
+    StringAppendF(&result, "----------------------\n");
+}
+
+std::string toString(PredictionState predictionState) {
+    switch (predictionState) {
+        case PredictionState::Valid:
+            return "Valid";
+        case PredictionState::Expired:
+            return "Expired";
+        case PredictionState::None:
+        default:
+            return "None";
+    }
+}
+
+std::string toString(JankType jankType) {
+    switch (jankType) {
+        case JankType::None:
+            return "None";
+        case JankType::Display:
+            return "Composer/Display - outside SF and App";
+        case JankType::SurfaceFlingerDeadlineMissed:
+            return "SurfaceFlinger Deadline Missed";
+        case JankType::AppDeadlineMissed:
+            return "App Deadline Missed";
+        case JankType::PredictionExpired:
+            return "Prediction Expired";
+        case JankType::SurfaceFlingerEarlyLatch:
+            return "SurfaceFlinger Early Latch";
+        default:
+            return "Unclassified";
+    }
+}
+
+std::string jankMetadataBitmaskToString(int32_t jankMetadata) {
+    std::vector<std::string> jankInfo;
+
+    if (jankMetadata & EarlyStart) {
+        jankInfo.emplace_back("Early Start");
+    } else if (jankMetadata & LateStart) {
+        jankInfo.emplace_back("Late Start");
+    }
+
+    if (jankMetadata & EarlyFinish) {
+        jankInfo.emplace_back("Early Finish");
+    } else if (jankMetadata & LateFinish) {
+        jankInfo.emplace_back("Late Finish");
+    }
+
+    if (jankMetadata & EarlyPresent) {
+        jankInfo.emplace_back("Early Present");
+    } else if (jankMetadata & LatePresent) {
+        jankInfo.emplace_back("Late Present");
+    }
+    // TODO(b/169876734): add GPU composition metadata here
+
+    if (jankInfo.empty()) {
+        return "None";
+    }
+    return std::accumulate(jankInfo.begin(), jankInfo.end(), std::string(),
+                           [](const std::string& l, const std::string& r) {
+                               return l.empty() ? r : l + ", " + r;
+                           });
+}
+
 int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
     ATRACE_CALL();
     std::lock_guard<std::mutex> lock(mMutex);
@@ -69,29 +184,26 @@
         mPredictionState(predictionState),
         mPredictions(predictions),
         mActuals({0, 0, 0}),
-        mActualQueueTime(0) {}
+        mActualQueueTime(0),
+        mJankType(JankType::None),
+        mJankMetadata(0) {}
 
 void SurfaceFrame::setPresentState(PresentState state) {
     std::lock_guard<std::mutex> lock(mMutex);
     mPresentState = state;
 }
 
-PredictionState SurfaceFrame::getPredictionState() {
-    std::lock_guard<std::mutex> lock(mMutex);
-    return mPredictionState;
-}
-
-SurfaceFrame::PresentState SurfaceFrame::getPresentState() {
+SurfaceFrame::PresentState SurfaceFrame::getPresentState() const {
     std::lock_guard<std::mutex> lock(mMutex);
     return mPresentState;
 }
 
-TimelineItem SurfaceFrame::getActuals() {
+TimelineItem SurfaceFrame::getActuals() const {
     std::lock_guard<std::mutex> lock(mMutex);
     return mActuals;
 }
 
-nsecs_t SurfaceFrame::getActualQueueTime() {
+nsecs_t SurfaceFrame::getActualQueueTime() const {
     std::lock_guard<std::mutex> lock(mMutex);
     return mActualQueueTime;
 }
@@ -115,25 +227,74 @@
     mActuals.presentTime = presentTime;
 }
 
-void SurfaceFrame::dump(std::string& result) {
+void SurfaceFrame::setJankInfo(JankType jankType, int32_t jankMetadata) {
     std::lock_guard<std::mutex> lock(mMutex);
-    StringAppendF(&result, "Present State : %d\n", static_cast<int>(mPresentState));
-    StringAppendF(&result, "Prediction State : %d\n", static_cast<int>(mPredictionState));
-    StringAppendF(&result, "Predicted Start Time : %" PRId64 "\n", mPredictions.startTime);
-    StringAppendF(&result, "Actual Start Time : %" PRId64 "\n", mActuals.startTime);
-    StringAppendF(&result, "Actual Queue Time : %" PRId64 "\n", mActualQueueTime);
-    StringAppendF(&result, "Predicted Render Complete Time : %" PRId64 "\n", mPredictions.endTime);
-    StringAppendF(&result, "Actual Render Complete Time : %" PRId64 "\n", mActuals.endTime);
-    StringAppendF(&result, "Predicted Present Time : %" PRId64 "\n", mPredictions.presentTime);
-    StringAppendF(&result, "Actual Present Time : %" PRId64 "\n", mActuals.presentTime);
+    mJankType = jankType;
+    mJankMetadata = jankMetadata;
 }
 
-FrameTimeline::FrameTimeline() : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()) {}
+JankType SurfaceFrame::getJankType() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mJankType;
+}
+
+nsecs_t SurfaceFrame::getBaseTime() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
+    if (mPredictionState == PredictionState::Valid) {
+        baseTime = std::min(baseTime, mPredictions.startTime);
+    }
+    if (mActuals.startTime != 0) {
+        baseTime = std::min(baseTime, mActuals.startTime);
+    }
+    baseTime = std::min(baseTime, mActuals.endTime);
+    return baseTime;
+}
+
+std::string presentStateToString(SurfaceFrame::PresentState presentState) {
+    using PresentState = SurfaceFrame::PresentState;
+    switch (presentState) {
+        case PresentState::Presented:
+            return "Presented";
+        case PresentState::Dropped:
+            return "Dropped";
+        case PresentState::Unknown:
+        default:
+            return "Unknown";
+    }
+}
+
+void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Layer - %s", mLayerName.c_str());
+    if (mJankType != JankType::None) {
+        // Easily identify a janky Surface Frame in the dump
+        StringAppendF(&result, " [*] ");
+    }
+    StringAppendF(&result, "\n");
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Present State : %s\n", presentStateToString(mPresentState).c_str());
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Jank Type : %s\n", toString(mJankType).c_str());
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Jank Metadata: %s\n",
+                  jankMetadataBitmaskToString(mJankMetadata).c_str());
+    dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
+}
+
+FrameTimeline::FrameTimeline()
+      : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()),
+        mMaxDisplayFrames(kDefaultMaxDisplayFrames) {}
 
 FrameTimeline::DisplayFrame::DisplayFrame()
       : surfaceFlingerPredictions(TimelineItem()),
         surfaceFlingerActuals(TimelineItem()),
-        predictionState(PredictionState::None) {
+        predictionState(PredictionState::None),
+        jankType(JankType::None),
+        jankMetadata(0) {
     this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
 }
 
@@ -200,10 +361,75 @@
         if (signalTime != Fence::SIGNAL_TIME_INVALID) {
             auto& displayFrame = pendingPresentFence.second;
             displayFrame->surfaceFlingerActuals.presentTime = signalTime;
+
+            // Jank Analysis for DisplayFrame
+            const auto& sfActuals = displayFrame->surfaceFlingerActuals;
+            const auto& sfPredictions = displayFrame->surfaceFlingerPredictions;
+            if (std::abs(sfActuals.presentTime - sfPredictions.presentTime) > kPresentThreshold) {
+                displayFrame->jankMetadata |= sfActuals.presentTime > sfPredictions.presentTime
+                        ? LatePresent
+                        : EarlyPresent;
+            }
+            if (std::abs(sfActuals.endTime - sfPredictions.endTime) > kDeadlineThreshold) {
+                if (sfActuals.endTime > sfPredictions.endTime) {
+                    displayFrame->jankMetadata |= LateFinish;
+                } else {
+                    displayFrame->jankMetadata |= EarlyFinish;
+                }
+
+                if (displayFrame->jankMetadata & EarlyFinish & EarlyPresent) {
+                    displayFrame->jankType = JankType::SurfaceFlingerEarlyLatch;
+                } else if (displayFrame->jankMetadata & LateFinish & LatePresent) {
+                    displayFrame->jankType = JankType::SurfaceFlingerDeadlineMissed;
+                } else if (displayFrame->jankMetadata & EarlyPresent ||
+                           displayFrame->jankMetadata & LatePresent) {
+                    // Cases where SF finished early but frame was presented late and vice versa
+                    displayFrame->jankType = JankType::Display;
+                }
+            }
+            if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) {
+                displayFrame->jankMetadata |=
+                        sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart;
+            }
+
             for (auto& surfaceFrame : displayFrame->surfaceFrames) {
                 if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
                     // Only presented SurfaceFrames need to be updated
                     surfaceFrame->setActualPresentTime(signalTime);
+
+                    // Jank Analysis for SurfaceFrame
+                    const auto& predictionState = surfaceFrame->getPredictionState();
+                    if (predictionState == PredictionState::Expired) {
+                        // Jank analysis cannot be done on apps that don't use predictions
+                        surfaceFrame->setJankInfo(JankType::PredictionExpired, 0);
+                        continue;
+                    } else if (predictionState == PredictionState::Valid) {
+                        const auto& actuals = surfaceFrame->getActuals();
+                        const auto& predictions = surfaceFrame->getPredictions();
+                        int32_t jankMetadata = 0;
+                        JankType jankType = JankType::None;
+                        if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) {
+                            jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish
+                                                                                  : EarlyFinish;
+                        }
+                        if (std::abs(actuals.presentTime - predictions.presentTime) >
+                            kPresentThreshold) {
+                            jankMetadata |= actuals.presentTime > predictions.presentTime
+                                    ? LatePresent
+                                    : EarlyPresent;
+                        }
+                        if (jankMetadata & EarlyPresent) {
+                            jankType = JankType::SurfaceFlingerEarlyLatch;
+                        } else if (jankMetadata & LatePresent) {
+                            if (jankMetadata & EarlyFinish) {
+                                // TODO(b/169890654): Classify this properly
+                                jankType = JankType::Display;
+                            } else {
+                                jankType = JankType::AppDeadlineMissed;
+                            }
+                        }
+                        surfaceFrame->setJankInfo(jankType, jankMetadata);
+                    }
                 }
             }
         }
@@ -214,7 +440,7 @@
 }
 
 void FrameTimeline::finalizeCurrentDisplayFrame() {
-    while (mDisplayFrames.size() >= kMaxDisplayFrames) {
+    while (mDisplayFrames.size() >= mMaxDisplayFrames) {
         // We maintain only a fixed number of frames' data. Pop older frames
         mDisplayFrames.pop_front();
     }
@@ -223,30 +449,100 @@
     mCurrentDisplayFrame = std::make_shared<DisplayFrame>();
 }
 
-void FrameTimeline::dump(std::string& result) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
-    for (const auto& displayFrame : mDisplayFrames) {
-        StringAppendF(&result, "---Display Frame---\n");
-        StringAppendF(&result, "Prediction State : %d\n",
-                      static_cast<int>(displayFrame->predictionState));
-        StringAppendF(&result, "Predicted SF wake time : %" PRId64 "\n",
-                      displayFrame->surfaceFlingerPredictions.startTime);
-        StringAppendF(&result, "Actual SF wake time : %" PRId64 "\n",
-                      displayFrame->surfaceFlingerActuals.startTime);
-        StringAppendF(&result, "Predicted SF Complete time : %" PRId64 "\n",
-                      displayFrame->surfaceFlingerPredictions.endTime);
-        StringAppendF(&result, "Actual SF Complete time : %" PRId64 "\n",
-                      displayFrame->surfaceFlingerActuals.endTime);
-        StringAppendF(&result, "Predicted Present time : %" PRId64 "\n",
-                      displayFrame->surfaceFlingerPredictions.presentTime);
-        StringAppendF(&result, "Actual Present time : %" PRId64 "\n",
-                      displayFrame->surfaceFlingerActuals.presentTime);
-        for (size_t i = 0; i < displayFrame->surfaceFrames.size(); i++) {
-            StringAppendF(&result, "Surface frame - %" PRId32 "\n", (int)i);
-            displayFrame->surfaceFrames[i]->dump(result);
+nsecs_t FrameTimeline::findBaseTime(const std::shared_ptr<DisplayFrame>& displayFrame) {
+    nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
+    if (displayFrame->predictionState == PredictionState::Valid) {
+        baseTime = std::min(baseTime, displayFrame->surfaceFlingerPredictions.startTime);
+    }
+    baseTime = std::min(baseTime, displayFrame->surfaceFlingerActuals.startTime);
+    for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+        nsecs_t surfaceFrameBaseTime = surfaceFrame->getBaseTime();
+        if (surfaceFrameBaseTime != 0) {
+            baseTime = std::min(baseTime, surfaceFrameBaseTime);
         }
     }
+    return baseTime;
+}
+
+void FrameTimeline::dumpDisplayFrame(std::string& result,
+                                     const std::shared_ptr<DisplayFrame>& displayFrame,
+                                     nsecs_t baseTime) {
+    if (displayFrame->jankType != JankType::None) {
+        // Easily identify a janky Display Frame in the dump
+        StringAppendF(&result, " [*] ");
+    }
+    StringAppendF(&result, "\n");
+    StringAppendF(&result, "Prediction State : %s\n",
+                  toString(displayFrame->predictionState).c_str());
+    StringAppendF(&result, "Jank Type : %s\n", toString(displayFrame->jankType).c_str());
+    StringAppendF(&result, "Jank Metadata: %s\n",
+                  jankMetadataBitmaskToString(displayFrame->jankMetadata).c_str());
+    dumpTable(result, displayFrame->surfaceFlingerPredictions, displayFrame->surfaceFlingerActuals,
+              "", displayFrame->predictionState, baseTime);
+    StringAppendF(&result, "\n");
+    std::string indent = "    "; // 4 spaces
+    for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+        surfaceFrame->dump(result, indent, baseTime);
+    }
+    StringAppendF(&result, "\n");
+}
+void FrameTimeline::dumpAll(std::string& result) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
+    nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+    for (size_t i = 0; i < mDisplayFrames.size(); i++) {
+        StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
+        dumpDisplayFrame(result, mDisplayFrames[i], baseTime);
+    }
+}
+
+void FrameTimeline::dumpJank(std::string& result) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+    for (size_t i = 0; i < mDisplayFrames.size(); i++) {
+        const auto& displayFrame = mDisplayFrames[i];
+        if (displayFrame->jankType == JankType::None) {
+            // Check if any Surface Frame has been janky
+            bool isJanky = false;
+            for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+                if (surfaceFrame->getJankType() != JankType::None) {
+                    isJanky = true;
+                    break;
+                }
+            }
+            if (!isJanky) {
+                continue;
+            }
+        }
+        StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
+        dumpDisplayFrame(result, displayFrame, baseTime);
+    }
+}
+void FrameTimeline::parseArgs(const Vector<String16>& args, std::string& result) {
+    ATRACE_CALL();
+    std::unordered_map<std::string, bool> argsMap;
+    for (size_t i = 0; i < args.size(); i++) {
+        argsMap[std::string(String8(args[i]).c_str())] = true;
+    }
+    if (argsMap.count("-jank")) {
+        dumpJank(result);
+    }
+    if (argsMap.count("-all")) {
+        dumpAll(result);
+    }
+}
+
+void FrameTimeline::setMaxDisplayFrames(uint32_t size) {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    // The size can either increase or decrease, clear everything, to be consistent
+    mDisplayFrames.clear();
+    mPendingPresentFences.clear();
+    mMaxDisplayFrames = size;
+}
+
+void FrameTimeline::reset() {
+    setMaxDisplayFrames(kDefaultMaxDisplayFrames);
 }
 
 } // namespace android::frametimeline::impl
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index cfe8170..bd637df 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -22,10 +22,48 @@
 #include <gui/ISurfaceComposer.h>
 #include <ui/FenceTime.h>
 #include <utils/RefBase.h>
+#include <utils/String16.h>
 #include <utils/Timers.h>
+#include <utils/Vector.h>
 
 namespace android::frametimeline {
 
+/*
+ * The type of jank that is associated with a Display/Surface frame
+ */
+enum class JankType {
+    // No Jank
+    None,
+    // Jank not related to SurfaceFlinger or the App
+    Display,
+    // SF took too long on the CPU
+    SurfaceFlingerDeadlineMissed,
+    // Either App or GPU took too long on the frame
+    AppDeadlineMissed,
+    // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a jank
+    // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
+    PredictionExpired,
+    // Latching a buffer early might cause an early present of the frame
+    SurfaceFlingerEarlyLatch,
+};
+
+enum JankMetadata {
+    // Frame was presented earlier than expected
+    EarlyPresent = 0x1,
+    // Frame was presented later than expected
+    LatePresent = 0x2,
+    // App/SF started earlier than expected
+    EarlyStart = 0x4,
+    // App/SF started later than expected
+    LateStart = 0x8,
+    // App/SF finished work earlier than the deadline
+    EarlyFinish = 0x10,
+    // App/SF finished work later than the deadline
+    LateFinish = 0x20,
+    // SF was in GPU composition
+    GpuComposition = 0x40,
+};
+
 class FrameTimelineTest;
 
 /*
@@ -39,6 +77,13 @@
     nsecs_t startTime;
     nsecs_t endTime;
     nsecs_t presentTime;
+
+    bool operator==(const TimelineItem& other) const {
+        return startTime == other.startTime && endTime == other.endTime &&
+                presentTime == other.presentTime;
+    }
+
+    bool operator!=(const TimelineItem& other) const { return !(*this == other); }
 };
 
 /*
@@ -75,11 +120,11 @@
 
     virtual ~SurfaceFrame() = default;
 
-    virtual TimelineItem getPredictions() = 0;
-    virtual TimelineItem getActuals() = 0;
-    virtual nsecs_t getActualQueueTime() = 0;
-    virtual PresentState getPresentState() = 0;
-    virtual PredictionState getPredictionState() = 0;
+    virtual TimelineItem getPredictions() const = 0;
+    virtual TimelineItem getActuals() const = 0;
+    virtual nsecs_t getActualQueueTime() const = 0;
+    virtual PresentState getPresentState() const = 0;
+    virtual PredictionState getPredictionState() const = 0;
 
     virtual void setPresentState(PresentState state) = 0;
 
@@ -120,7 +165,17 @@
     virtual void setSfPresent(nsecs_t sfPresentTime,
                               const std::shared_ptr<FenceTime>& presentFence) = 0;
 
-    virtual void dump(std::string& result) = 0;
+    // Args:
+    // -jank : Dumps only the Display Frames that are either janky themselves
+    //         or contain janky Surface Frames.
+    // -all : Dumps the entire list of DisplayFrames and the SurfaceFrames contained within
+    virtual void parseArgs(const Vector<String16>& args, std::string& result) = 0;
+
+    // Sets the max number of display frames that can be stored. Called by SF backdoor.
+    virtual void setMaxDisplayFrames(uint32_t size);
+
+    // Restores the max number of display frames to default. Called by SF backdoor.
+    virtual void reset() = 0;
 };
 
 namespace impl {
@@ -155,27 +210,33 @@
                  TimelineItem&& predictions);
     ~SurfaceFrame() = default;
 
-    TimelineItem getPredictions() override { return mPredictions; };
-    TimelineItem getActuals() override;
-    nsecs_t getActualQueueTime() override;
-    PresentState getPresentState() override;
-    PredictionState getPredictionState() override;
+    TimelineItem getPredictions() const override { return mPredictions; };
+    TimelineItem getActuals() const override;
+    nsecs_t getActualQueueTime() const override;
+    PresentState getPresentState() const override;
+    PredictionState getPredictionState() const override { return mPredictionState; };
 
     void setActualStartTime(nsecs_t actualStartTime) override;
     void setActualQueueTime(nsecs_t actualQueueTime) override;
     void setAcquireFenceTime(nsecs_t acquireFenceTime) override;
     void setPresentState(PresentState state) override;
     void setActualPresentTime(nsecs_t presentTime);
-    void dump(std::string& result);
+    void setJankInfo(JankType jankType, int32_t jankMetadata);
+    JankType getJankType() const;
+    nsecs_t getBaseTime() const;
+    // All the timestamps are dumped relative to the baseTime
+    void dump(std::string& result, const std::string& indent, nsecs_t baseTime);
 
 private:
     const std::string mLayerName;
     PresentState mPresentState GUARDED_BY(mMutex);
-    PredictionState mPredictionState GUARDED_BY(mMutex);
+    const PredictionState mPredictionState;
     const TimelineItem mPredictions;
     TimelineItem mActuals GUARDED_BY(mMutex);
-    nsecs_t mActualQueueTime;
-    std::mutex mMutex;
+    nsecs_t mActualQueueTime GUARDED_BY(mMutex);
+    mutable std::mutex mMutex;
+    JankType mJankType GUARDED_BY(mMutex);    // Enum for the type of jank
+    int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank
 };
 
 class FrameTimeline : public android::frametimeline::FrameTimeline {
@@ -191,7 +252,9 @@
     void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override;
     void setSfPresent(nsecs_t sfPresentTime,
                       const std::shared_ptr<FenceTime>& presentFence) override;
-    void dump(std::string& result) override;
+    void parseArgs(const Vector<String16>& args, std::string& result) override;
+    void setMaxDisplayFrames(uint32_t size) override;
+    void reset() override;
 
 private:
     // Friend class for testing
@@ -215,10 +278,19 @@
         std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames;
 
         PredictionState predictionState;
+        JankType jankType = JankType::None; // Enum for the type of jank
+        int32_t jankMetadata = 0x0;         // Additional details about the jank
     };
 
     void flushPendingPresentFences() REQUIRES(mMutex);
     void finalizeCurrentDisplayFrame() REQUIRES(mMutex);
+    // BaseTime is the smallest timestamp in a DisplayFrame.
+    // Used for dumping all timestamps relative to the oldest, making it easy to read.
+    nsecs_t findBaseTime(const std::shared_ptr<DisplayFrame>&) REQUIRES(mMutex);
+    void dumpDisplayFrame(std::string& result, const std::shared_ptr<DisplayFrame>&,
+                          nsecs_t baseTime) REQUIRES(mMutex);
+    void dumpAll(std::string& result);
+    void dumpJank(std::string& result);
 
     // Sliding window of display frames. TODO(b/168072834): compare perf with fixed size array
     std::deque<std::shared_ptr<DisplayFrame>> mDisplayFrames GUARDED_BY(mMutex);
@@ -227,12 +299,21 @@
     std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex);
     TokenManager mTokenManager;
     std::mutex mMutex;
-    static constexpr uint32_t kMaxDisplayFrames = 64;
+    uint32_t mMaxDisplayFrames;
+    static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
     // The initial container size for the vector<SurfaceFrames> inside display frame. Although this
     // number doesn't represent any bounds on the number of surface frames that can go in a display
     // frame, this is a good starting size for the vector so that we can avoid the internal vector
     // resizing that happens with push_back.
     static constexpr uint32_t kNumSurfaceFramesInitial = 10;
+    // The various thresholds for App and SF. If the actual timestamp falls within the threshold
+    // compared to prediction, we don't treat it as a jank.
+    static constexpr nsecs_t kPresentThreshold =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+    static constexpr nsecs_t kDeadlineThreshold =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+    static constexpr nsecs_t kSFStartThreshold =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count();
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 08801c9..a79cbe4 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2511,6 +2511,7 @@
     // InputDispatcher, and obviously if they aren't visible they can't occlude
     // anything.
     info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
+    info.alpha = getAlpha();
 
     auto cropLayer = mDrawingState.touchableRegionCrop.promote();
     if (info.replaceTouchableRegionWithCrop) {
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 99d061d..f676d5b 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -176,12 +176,13 @@
 }
 
 bool RefreshRateOverlay::createLayer() {
+    int32_t layerId;
     const status_t ret =
             mFlinger.createLayer(String8("RefreshRateOverlay"), mClient,
                                  SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(),
                                  PIXEL_FORMAT_RGBA_8888,
                                  ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(),
-                                 &mIBinder, &mGbp, nullptr);
+                                 &mIBinder, &mGbp, nullptr, &layerId);
     if (ret) {
         ALOGE("failed to create buffer state layer");
         return false;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 641a0a3..1343375 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <binder/IPCThreadState.h>
 
@@ -28,6 +26,7 @@
 #include <gui/IDisplayEventConnection.h>
 
 #include "EventThread.h"
+#include "FrameTimeline.h"
 #include "MessageQueue.h"
 #include "SurfaceFlinger.h"
 
@@ -68,15 +67,53 @@
     mHandler = new Handler(*this);
 }
 
+// TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly
+// and remove the EventThread from MessageQueue
 void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) {
     if (mEventTube.getFd() >= 0) {
         mLooper->removeFd(mEventTube.getFd());
     }
 
     mEvents = connection;
-    mEvents->stealReceiveChannel(&mEventTube);
-    mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
-                   this);
+    if (mEvents) {
+        mEvents->stealReceiveChannel(&mEventTube);
+        mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
+                       this);
+    }
+}
+
+void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
+    ATRACE_CALL();
+    // Trace VSYNC-sf
+    mVsync.value = (mVsync.value + 1) % 2;
+
+    {
+        std::lock_guard lock(mVsync.mutex);
+        mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
+    }
+    mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions(
+                                         {targetWakeupTime, readyTime, vsyncTime}),
+                                 vsyncTime);
+}
+
+void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch,
+                             frametimeline::TokenManager& tokenManager,
+                             std::chrono::nanoseconds workDuration) {
+    setDuration(workDuration);
+    mVsync.tokenManager = &tokenManager;
+    mVsync.registration = std::make_unique<
+            scheduler::VSyncCallbackRegistration>(dispatch,
+                                                  std::bind(&MessageQueue::vsyncCallback, this,
+                                                            std::placeholders::_1,
+                                                            std::placeholders::_2,
+                                                            std::placeholders::_3),
+                                                  "sf");
+}
+
+void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) {
+    ATRACE_CALL();
+    std::lock_guard lock(mVsync.mutex);
+    mVsync.workDuration = workDuration;
 }
 
 void MessageQueue::waitMessage() {
@@ -106,7 +143,18 @@
 }
 
 void MessageQueue::invalidate() {
-    mEvents->requestNextVsync();
+    ATRACE_CALL();
+    if (mEvents) {
+        mEvents->requestNextVsync();
+    } else {
+        const auto [workDuration, lastVsyncCallback] = [&] {
+            std::lock_guard lock(mVsync.mutex);
+            std::chrono::nanoseconds mWorkDurationNanos = mVsync.workDuration;
+            return std::make_pair(mWorkDurationNanos.count(), mVsync.lastCallbackTime.count());
+        }();
+
+        mVsync.registration->schedule({workDuration, /*readyDuration=*/0, lastVsyncCallback});
+    }
 }
 
 void MessageQueue::refresh() {
@@ -135,5 +183,3 @@
 
 } // namespace android::impl
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index e263b2f..139b38e 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -29,6 +29,8 @@
 #include <private/gui/BitTube.h>
 
 #include "EventThread.h"
+#include "TracedOrdinal.h"
+#include "VSyncDispatch.h"
 
 namespace android {
 
@@ -63,6 +65,9 @@
     virtual ~MessageQueue() = default;
 
     virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
+    virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+                           std::chrono::nanoseconds workDuration) = 0;
+    virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
     virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0;
     virtual void waitMessage() = 0;
     virtual void postMessage(sp<MessageHandler>&&) = 0;
@@ -74,7 +79,8 @@
 
 namespace impl {
 
-class MessageQueue final : public android::MessageQueue {
+class MessageQueue : public android::MessageQueue {
+protected:
     class Handler : public MessageHandler {
         enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 };
         MessageQueue& mQueue;
@@ -84,9 +90,9 @@
 
     public:
         explicit Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) {}
-        virtual void handleMessage(const Message& message);
-        void dispatchRefresh();
-        void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp);
+        void handleMessage(const Message& message) override;
+        virtual void dispatchRefresh();
+        virtual void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp);
     };
 
     friend class Handler;
@@ -94,15 +100,33 @@
     sp<SurfaceFlinger> mFlinger;
     sp<Looper> mLooper;
     sp<EventThreadConnection> mEvents;
+
+    struct Vsync {
+        frametimeline::TokenManager* tokenManager = nullptr;
+        std::unique_ptr<scheduler::VSyncCallbackRegistration> registration;
+
+        std::mutex mutex;
+        TracedOrdinal<std::chrono::nanoseconds> workDuration
+                GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
+        std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
+        TracedOrdinal<int> value = {"VSYNC-sf", 0};
+    };
+
+    Vsync mVsync;
+
     gui::BitTube mEventTube;
     sp<Handler> mHandler;
 
     static int cb_eventReceiver(int fd, int events, void* data);
     int eventReceiver(int fd, int events);
+    void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
 
 public:
     ~MessageQueue() override = default;
     void init(const sp<SurfaceFlinger>& flinger) override;
+    void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+                   std::chrono::nanoseconds workDuration) override;
+    void setDuration(std::chrono::nanoseconds workDuration) override;
     void setEventConnection(const sp<EventThreadConnection>& connection) override;
 
     void waitMessage() override;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 3ecda58..47ce4a4 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -137,6 +137,8 @@
 
     void setDisplayPowerState(bool normal);
 
+    scheduler::VSyncDispatch& getVsyncDispatch() { return *mVsyncSchedule.dispatch; }
+
     void dump(std::string&) const;
     void dump(ConnectionHandle, std::string&) const;
     void dumpVsync(std::string&) const;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 0407900..9d71103 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -94,6 +94,13 @@
         nsecs_t workDuration = 0;
         nsecs_t readyDuration = 0;
         nsecs_t earliestVsync = 0;
+
+        bool operator==(const ScheduleTiming& other) const {
+            return workDuration == other.workDuration && readyDuration == other.readyDuration &&
+                    earliestVsync == other.earliestVsync;
+        }
+
+        bool operator!=(const ScheduleTiming& other) const { return !(*this == other); }
     };
 
     /*
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index df87e3b..7edbcaa 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -717,7 +717,8 @@
     processDisplayHotplugEventsLocked();
     const auto display = getDefaultDisplayDeviceLocked();
     LOG_ALWAYS_FATAL_IF(!display, "Missing internal display after registering composer callback.");
-    LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(*display->getId()),
+    const auto displayId = display->getPhysicalId();
+    LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(displayId),
                         "Internal display is disconnected.");
 
     // initialize our drawing state
@@ -1078,8 +1079,8 @@
         const nsecs_t vsyncPeriod =
                 mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId)
                         .getVsyncPeriod();
-        mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle,
-                                                  static_cast<PhysicalDisplayId>(*display->getId()),
+        const auto physicalId = display->getPhysicalId();
+        mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, physicalId,
                                                   mUpcomingActiveConfig.configId, vsyncPeriod);
     }
 }
@@ -1128,8 +1129,7 @@
     }
 
     mUpcomingActiveConfig = *desiredActiveConfig;
-    const auto displayId = display->getId();
-    LOG_ALWAYS_FATAL_IF(!displayId);
+    const auto displayId = display->getPhysicalId();
 
     ATRACE_INT("ActiveConfigFPS_HWC", refreshRate.getFps());
 
@@ -1140,7 +1140,7 @@
 
     hal::VsyncPeriodChangeTimeline outTimeline;
     auto status =
-            getHwComposer().setActiveConfigWithConstraints(*displayId,
+            getHwComposer().setActiveConfigWithConstraints(displayId,
                                                            mUpcomingActiveConfig.configId.value(),
                                                            constraints, &outTimeline);
     if (status != NO_ERROR) {
@@ -1414,8 +1414,8 @@
         Mutex::Autolock lock(mStateLock);
 
         if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
-            mEventQueue->setEventConnection(
-                    mScheduler->getEventConnection(enable ? handle : mSfConnectionHandle));
+            mEventQueue->setEventConnection(enable ? mScheduler->getEventConnection(handle)
+                                                   : nullptr);
         }
     }).wait();
 
@@ -1665,7 +1665,7 @@
 
         if (const auto display = getDefaultDisplayDeviceLocked();
             display && display->isPoweredOn()) {
-            getHwComposer().setVsyncEnabled(*display->getId(), mHWCVsyncPendingState);
+            getHwComposer().setVsyncEnabled(display->getPhysicalId(), mHWCVsyncPendingState);
         }
     }));
 }
@@ -2120,7 +2120,7 @@
     getBE().mDisplayTimeline.updateSignalTimes();
     mPreviousPresentFences[1] = mPreviousPresentFences[0];
     mPreviousPresentFences[0] =
-            display ? getHwComposer().getPresentFence(*display->getId()) : Fence::NO_FENCE;
+            display ? getHwComposer().getPresentFence(display->getPhysicalId()) : Fence::NO_FENCE;
     auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFences[0]);
     getBE().mDisplayTimeline.push(presentFenceTime);
 
@@ -2154,7 +2154,8 @@
         mScheduler->addPresentFence(presentFenceTime);
     }
 
-    const bool isDisplayConnected = display && getHwComposer().isConnected(*display->getId());
+    const bool isDisplayConnected =
+            display && getHwComposer().isConnected(display->getPhysicalId());
 
     if (!hasSyncFramework) {
         if (isDisplayConnected && display->isPoweredOn()) {
@@ -2171,7 +2172,8 @@
         } else if (isDisplayConnected) {
             // The HWC doesn't support present fences, so use the refresh
             // timestamp instead.
-            const nsecs_t presentTime = getHwComposer().getRefreshTimestamp(*display->getId());
+            const nsecs_t presentTime =
+                    getHwComposer().getRefreshTimestamp(display->getPhysicalId());
             mAnimFrameTracker.setActualPresentTime(presentTime);
         }
         mAnimFrameTracker.advanceFrame();
@@ -2273,7 +2275,7 @@
 
 void SurfaceFlinger::postFrame() {
     const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
-    if (display && getHwComposer().isConnected(*display->getId())) {
+    if (display && getHwComposer().isConnected(display->getPhysicalId())) {
         uint32_t flipCount = display->getPageFlipCount();
         if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
             logFrameStats();
@@ -2378,7 +2380,6 @@
         const DisplayDeviceState& state,
         const sp<compositionengine::DisplaySurface>& displaySurface,
         const sp<IGraphicBufferProducer>& producer) {
-    auto displayId = compositionDisplay->getDisplayId();
     DisplayDeviceCreationArgs creationArgs(this, displayToken, compositionDisplay);
     creationArgs.sequenceId = state.sequenceId;
     creationArgs.isSecure = state.isSecure;
@@ -2390,26 +2391,26 @@
         creationArgs.connectionType = physical->type;
     }
 
-    const bool isInternalDisplay = displayId && displayId == getInternalDisplayIdLocked();
-    creationArgs.isPrimary = isInternalDisplay;
+    if (const auto id = PhysicalDisplayId::tryCast(compositionDisplay->getId())) {
+        creationArgs.isPrimary = id == getInternalDisplayIdLocked();
 
-    if (useColorManagement && displayId) {
-        std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId);
-        for (ColorMode colorMode : modes) {
-            if (isWideColorMode(colorMode)) {
-                creationArgs.hasWideColorGamut = true;
+        if (useColorManagement) {
+            std::vector<ColorMode> modes = getHwComposer().getColorModes(*id);
+            for (ColorMode colorMode : modes) {
+                if (isWideColorMode(colorMode)) {
+                    creationArgs.hasWideColorGamut = true;
+                }
+
+                std::vector<RenderIntent> renderIntents =
+                        getHwComposer().getRenderIntents(*id, colorMode);
+                creationArgs.hwcColorModes.emplace(colorMode, renderIntents);
             }
-
-            std::vector<RenderIntent> renderIntents =
-                    getHwComposer().getRenderIntents(*displayId, colorMode);
-            creationArgs.hwcColorModes.emplace(colorMode, renderIntents);
         }
     }
 
-    if (displayId) {
-        getHwComposer().getHdrCapabilities(*displayId, &creationArgs.hdrCapabilities);
-        creationArgs.supportedPerFrameMetadata =
-                getHwComposer().getSupportedPerFrameMetadata(*displayId);
+    if (const auto id = HalDisplayId::tryCast(compositionDisplay->getId())) {
+        getHwComposer().getHdrCapabilities(*id, &creationArgs.hdrCapabilities);
+        creationArgs.supportedPerFrameMetadata = getHwComposer().getSupportedPerFrameMetadata(*id);
     }
 
     auto nativeWindowSurface = getFactory().createNativeWindowSurface(producer);
@@ -2424,7 +2425,7 @@
     }
 
     creationArgs.physicalOrientation =
-            isInternalDisplay ? internalDisplayOrientation : ui::ROTATION_0;
+            creationArgs.isPrimary ? internalDisplayOrientation : ui::ROTATION_0;
 
     // virtual displays are always considered enabled
     creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
@@ -2446,8 +2447,8 @@
                                                     RenderIntent::COLORIMETRIC,
                                                     Dataspace::UNKNOWN});
     if (!state.isVirtual()) {
-        LOG_ALWAYS_FATAL_IF(!displayId);
-        auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(*displayId));
+        const auto physicalId = display->getPhysicalId();
+        auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(physicalId));
         display->setActiveConfig(activeConfigId);
         display->setDeviceProductInfo(state.physical->deviceProductInfo);
     }
@@ -2497,6 +2498,7 @@
     builder.setLayerStackId(state.layerStack);
     builder.setPowerAdvisor(&mPowerAdvisor);
     builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays);
+    builder.setGpuVirtualDisplayIdGenerator(mGpuVirtualDisplayIdGenerator);
     builder.setName(state.displayName);
     const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
 
@@ -2506,11 +2508,13 @@
     sp<IGraphicBufferConsumer> bqConsumer;
     getFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false);
 
-    std::optional<DisplayId> displayId = compositionDisplay->getId();
+    DisplayId displayId = compositionDisplay->getId();
 
     if (state.isVirtual()) {
+        const auto virtualId = VirtualDisplayId::tryCast(displayId);
+        LOG_FATAL_IF(!virtualId);
         sp<VirtualDisplaySurface> vds =
-                new VirtualDisplaySurface(getHwComposer(), displayId, state.surface, bqProducer,
+                new VirtualDisplaySurface(getHwComposer(), *virtualId, state.surface, bqProducer,
                                           bqConsumer, state.displayName);
 
         displaySurface = vds;
@@ -2520,9 +2524,9 @@
                  "adding a supported display, but rendering "
                  "surface is provided (%p), ignoring it",
                  state.surface.get());
-
-        LOG_ALWAYS_FATAL_IF(!displayId);
-        displaySurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer,
+        const auto physicalId = PhysicalDisplayId::tryCast(displayId);
+        LOG_FATAL_IF(!physicalId);
+        displaySurface = new FramebufferSurface(getHwComposer(), *physicalId, bqConsumer,
                                                 maxGraphicsWidth, maxGraphicsHeight);
         producer = bqProducer;
     }
@@ -2532,8 +2536,7 @@
                                                        displaySurface, producer);
     mDisplays.emplace(displayToken, display);
     if (!state.isVirtual()) {
-        LOG_FATAL_IF(!displayId);
-        dispatchDisplayHotplugEvent(static_cast<PhysicalDisplayId>(*displayId), true);
+        dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
     }
 
     if (display->isPrimary()) {
@@ -2543,13 +2546,9 @@
 
 void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) {
     if (const auto display = getDisplayDeviceLocked(displayToken)) {
-        // Save display ID before disconnecting.
-        const auto displayId = display->getId();
         display->disconnect();
-
         if (!display->isVirtual()) {
-            LOG_FATAL_IF(!displayId);
-            dispatchDisplayHotplugEvent(static_cast<PhysicalDisplayId>(*displayId), false);
+            dispatchDisplayHotplugEvent(display->getPhysicalId(), false);
         }
     }
 
@@ -2825,7 +2824,7 @@
 void SurfaceFlinger::updateCursorAsync() {
     compositionengine::CompositionRefreshArgs refreshArgs;
     for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
-        if (display->getId()) {
+        if (HalDisplayId::tryCast(display->getId())) {
             refreshArgs.outputs.push_back(display->getCompositionDisplay());
         }
     }
@@ -2867,19 +2866,23 @@
     // start the EventThread
     mScheduler = getFactory().createScheduler(*mRefreshRateConfigs, *this);
     const auto configs = mVsyncConfiguration->getCurrentConfigs();
+    const nsecs_t vsyncPeriod =
+            mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod();
     mAppConnectionHandle =
             mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),
                                          /*workDuration=*/configs.late.appWorkDuration,
                                          /*readyDuration=*/configs.late.sfWorkDuration,
                                          impl::EventThread::InterceptVSyncsCallback());
     mSfConnectionHandle =
-            mScheduler->createConnection("sf", mFrameTimeline->getTokenManager(),
-                                         /*workDuration=*/configs.late.sfWorkDuration,
-                                         /*readyDuration=*/0ns, [this](nsecs_t timestamp) {
+            mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(),
+                                         /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
+                                         /*readyDuration=*/configs.late.sfWorkDuration,
+                                         [this](nsecs_t timestamp) {
                                              mInterceptor->saveVSyncEvent(timestamp);
                                          });
 
-    mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
+    mEventQueue->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
+                           configs.late.sfWorkDuration);
 
     mRegionSamplingThread =
             new RegionSamplingThread(*this, *mScheduler,
@@ -2891,8 +2894,6 @@
     // This is a bit hacky, but this avoids a back-pointer into the main SF
     // classes from EventThread, and there should be no run-time binder cost
     // anyway since there are no connected apps at this point.
-    const nsecs_t vsyncPeriod =
-            mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod();
     mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId, currentConfig,
                                               vsyncPeriod);
     static auto ignorePresentFences =
@@ -2904,16 +2905,19 @@
 
 void SurfaceFlinger::updatePhaseConfiguration(const RefreshRate& refreshRate) {
     mVsyncConfiguration->setRefreshRateFps(refreshRate.getFps());
-    setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()));
+    setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
+                   refreshRate.getVsyncPeriod());
 }
 
-void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config) {
+void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config,
+                                    nsecs_t vsyncPeriod) {
     mScheduler->setDuration(mAppConnectionHandle,
                             /*workDuration=*/config.appWorkDuration,
                             /*readyDuration=*/config.sfWorkDuration);
     mScheduler->setDuration(mSfConnectionHandle,
-                            /*workDuration=*/config.sfWorkDuration,
-                            /*readyDuration=*/0ns);
+                            /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
+                            /*readyDuration=*/config.sfWorkDuration);
+    mEventQueue->setDuration(config.sfWorkDuration);
 }
 
 void SurfaceFlinger::commitTransaction() {
@@ -3841,7 +3845,7 @@
 }
 
 status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
-                                     sp<IBinder>* outHandle, int32_t* outId) {
+                                     sp<IBinder>* outHandle, int32_t* outLayerId) {
     if (!mirrorFromHandle) {
         return NAME_NOT_FOUND;
     }
@@ -3866,7 +3870,7 @@
         mirrorLayer->mClonedChild = mirrorFrom->createClone();
     }
 
-    *outId = mirrorLayer->sequence;
+    *outLayerId = mirrorLayer->sequence;
     return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false,
                           nullptr /* outTransformHint */);
 }
@@ -3875,8 +3879,8 @@
                                      uint32_t h, PixelFormat format, uint32_t flags,
                                      LayerMetadata metadata, sp<IBinder>* handle,
                                      sp<IGraphicBufferProducer>* gbp,
-                                     const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
-                                     int32_t* outId, uint32_t* outTransformHint) {
+                                     const sp<IBinder>& parentHandle, int32_t* outLayerId,
+                                     const sp<Layer>& parentLayer, uint32_t* outTransformHint) {
     if (int32_t(w|h) < 0) {
         ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
                 int(w), int(h));
@@ -3942,7 +3946,7 @@
     mInterceptor->saveSurfaceCreation(layer);
 
     setTransactionFlags(eTransactionNeeded);
-    *outId = layer->sequence;
+    *outLayerId = layer->sequence;
     return result;
 }
 
@@ -4120,10 +4124,8 @@
         return;
     }
 
-    const auto displayId = display->getId();
-    LOG_ALWAYS_FATAL_IF(!displayId);
-
-    ALOGD("Setting power mode %d on display %s", mode, to_string(*displayId).c_str());
+    const auto displayId = display->getPhysicalId();
+    ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str());
 
     const hal::PowerMode currentMode = display->getPowerMode();
     if (mode == currentMode) {
@@ -4140,9 +4142,9 @@
         if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
             ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
         }
-        getHwComposer().setPowerMode(*displayId, mode);
+        getHwComposer().setPowerMode(displayId, mode);
         if (display->isPrimary() && mode != hal::PowerMode::DOZE_SUSPEND) {
-            getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState);
+            getHwComposer().setVsyncEnabled(displayId, mHWCVsyncPendingState);
             mScheduler->onScreenAcquired(mAppConnectionHandle);
             mScheduler->resyncToHardwareVsync(true, vsyncPeriod);
         }
@@ -4161,14 +4163,14 @@
         }
 
         // Make sure HWVsync is disabled before turning off the display
-        getHwComposer().setVsyncEnabled(*displayId, hal::Vsync::DISABLE);
+        getHwComposer().setVsyncEnabled(displayId, hal::Vsync::DISABLE);
 
-        getHwComposer().setPowerMode(*displayId, mode);
+        getHwComposer().setPowerMode(displayId, mode);
         mVisibleRegionsDirty = true;
         // from this point on, SF will stop drawing on this display
     } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
         // Update display while dozing
-        getHwComposer().setPowerMode(*displayId, mode);
+        getHwComposer().setPowerMode(displayId, mode);
         if (display->isPrimary() && currentMode == hal::PowerMode::DOZE_SUSPEND) {
             mScheduler->onScreenAcquired(mAppConnectionHandle);
             mScheduler->resyncToHardwareVsync(true, vsyncPeriod);
@@ -4179,10 +4181,10 @@
             mScheduler->disableHardwareVsync(true);
             mScheduler->onScreenReleased(mAppConnectionHandle);
         }
-        getHwComposer().setPowerMode(*displayId, mode);
+        getHwComposer().setPowerMode(displayId, mode);
     } else {
         ALOGE("Attempting to set unknown power mode: %d\n", mode);
-        getHwComposer().setPowerMode(*displayId, mode);
+        getHwComposer().setPowerMode(displayId, mode);
     }
 
     if (display->isPrimary()) {
@@ -4191,7 +4193,7 @@
         mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON);
     }
 
-    ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
+    ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str());
 }
 
 void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
@@ -4232,7 +4234,7 @@
                 {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
                 {"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
                 {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
-                {"--frametimeline"s, dumper([this](std::string& s) { mFrameTimeline->dump(s); })},
+                {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
         };
 
         const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
@@ -4315,6 +4317,10 @@
     mTimeStats->parseArgs(asProto, args, result);
 }
 
+void SurfaceFlinger::dumpFrameTimeline(const DumpArgs& args, std::string& result) const {
+    mFrameTimeline->parseArgs(args, result);
+}
+
 // This should only be called from the main thread.  Otherwise it would need
 // the lock and should use mCurrentState rather than mDrawingState.
 void SurfaceFlinger::logFrameStats() {
@@ -4450,12 +4456,11 @@
 
 void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
     for (const auto& [token, display] : mDisplays) {
-        const auto displayId = display->getId();
+        const auto displayId = PhysicalDisplayId::tryCast(display->getId());
         if (!displayId) {
             continue;
         }
-        const auto hwcDisplayId =
-                getHwComposer().fromPhysicalDisplayId(static_cast<PhysicalDisplayId>(*displayId));
+        const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
         if (!hwcDisplayId) {
             continue;
         }
@@ -4508,7 +4513,7 @@
     // TODO: print out if wide-color mode is active or not
 
     for (const auto& [token, display] : mDisplays) {
-        const auto displayId = display->getId();
+        const auto displayId = PhysicalDisplayId::tryCast(display->getId());
         if (!displayId) {
             continue;
         }
@@ -4722,7 +4727,7 @@
      * HWC layer minidump
      */
     for (const auto& [token, display] : mDisplays) {
-        const auto displayId = display->getId();
+        const auto displayId = HalDisplayId::tryCast(display->getId());
         if (!displayId) {
             continue;
         }
@@ -4897,9 +4902,9 @@
         code == IBinder::SYSPROPS_TRANSACTION) {
         return OK;
     }
-    // Numbers from 1000 to 1037 are currently used for backdoors. The code
+    // Numbers from 1000 to 1038 are currently used for backdoors. The code
     // in onTransact verifies that the user is root, and has access to use SF.
-    if (code >= 1000 && code <= 1037) {
+    if (code >= 1000 && code <= 1038) {
         ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
         return OK;
     }
@@ -5241,18 +5246,27 @@
             // Inject a hotplug connected event for the primary display. This will deallocate and
             // reallocate the display state including framebuffers.
             case 1037: {
-                const auto token = getInternalDisplayToken();
-
-                sp<DisplayDevice> display;
+                std::optional<hal::HWDisplayId> hwcId;
                 {
                     Mutex::Autolock lock(mStateLock);
-                    display = getDisplayDeviceLocked(token);
+                    hwcId = getHwComposer().getInternalHwcDisplayId();
                 }
-                const auto hwcId =
-                        getHwComposer().fromPhysicalDisplayId(PhysicalDisplayId(*display->getId()));
-
                 onHotplugReceived(getBE().mComposerSequenceId, *hwcId, hal::Connection::CONNECTED);
-
+                return NO_ERROR;
+            }
+            // Modify the max number of display frames stored within FrameTimeline
+            case 1038: {
+                n = data.readInt32();
+                if (n < 0 || n > MAX_ALLOWED_DISPLAY_FRAMES) {
+                    ALOGW("Invalid max size. Maximum allowed is %d", MAX_ALLOWED_DISPLAY_FRAMES);
+                    return BAD_VALUE;
+                }
+                if (n == 0) {
+                    // restore to default
+                    mFrameTimeline->reset();
+                    return NO_ERROR;
+                }
+                mFrameTimeline->setMaxDisplayFrames(n);
                 return NO_ERROR;
             }
         }
@@ -5859,16 +5873,14 @@
         // as well. For now, just call directly to setActiveConfigWithConstraints but ideally
         // it should go thru setDesiredActiveConfig, similar to primary display.
         ALOGV("setAllowedDisplayConfigsInternal for non-primary display");
-        const auto displayId = display->getId();
-        LOG_ALWAYS_FATAL_IF(!displayId);
+        const auto displayId = display->getPhysicalId();
 
         hal::VsyncPeriodChangeConstraints constraints;
         constraints.desiredTimeNanos = systemTime();
         constraints.seamlessRequired = false;
 
         hal::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
-        if (getHwComposer().setActiveConfigWithConstraints(*displayId,
-                                                           policy->defaultConfig.value(),
+        if (getHwComposer().setActiveConfigWithConstraints(displayId, policy->defaultConfig.value(),
                                                            constraints, &timeline) < 0) {
             return BAD_VALUE;
         }
@@ -5878,11 +5890,9 @@
 
         display->setActiveConfig(policy->defaultConfig);
         const nsecs_t vsyncPeriod = getHwComposer()
-                                            .getConfigs(*displayId)[policy->defaultConfig.value()]
+                                            .getConfigs(displayId)[policy->defaultConfig.value()]
                                             ->getVsyncPeriod();
-        mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle,
-                                                     static_cast<PhysicalDisplayId>(
-                                                             *display->getId()),
+        mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle, displayId,
                                                      policy->defaultConfig, vsyncPeriod);
         return NO_ERROR;
     }
@@ -5914,8 +5924,8 @@
     const nsecs_t vsyncPeriod =
             mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig())
                     .getVsyncPeriod();
-    mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle,
-                                              static_cast<PhysicalDisplayId>(*display->getId()),
+    const auto physicalId = display->getPhysicalId();
+    mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, physicalId,
                                               display->getActiveConfig(), vsyncPeriod);
     toggleKernelIdleTimer();
 
@@ -6006,11 +6016,9 @@
     } else if (display->isVirtual()) {
         return INVALID_OPERATION;
     } else {
-        const auto displayId = display->getId();
-        LOG_FATAL_IF(!displayId);
-
-        *outDefaultConfig = getHwComposer().getActiveConfigIndex(*displayId);
-        auto vsyncPeriod = getHwComposer().getActiveConfig(*displayId)->getVsyncPeriod();
+        const auto displayId = display->getPhysicalId();
+        *outDefaultConfig = getHwComposer().getActiveConfigIndex(displayId);
+        auto vsyncPeriod = getHwComposer().getActiveConfig(displayId)->getVsyncPeriod();
         *outPrimaryRefreshRateMin = 1e9f / vsyncPeriod;
         *outPrimaryRefreshRateMax = 1e9f / vsyncPeriod;
         *outAppRequestRefreshRateMin = 1e9f / vsyncPeriod;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 9a552d8..f55dd90 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -52,6 +52,7 @@
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/PowerAdvisor.h"
+#include "DisplayIdGenerator.h"
 #include "Effects/Daltonizer.h"
 #include "FrameTracker.h"
 #include "LayerVector.h"
@@ -494,11 +495,14 @@
               typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
     void modulateVsync(Handler handler, Args... args) {
         if (const auto config = (*mVsyncModulator.*handler)(args...)) {
-            setVsyncConfig(*config);
+            const auto vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
+            setVsyncConfig(*config, vsyncPeriod);
         }
     }
 
     static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
+    // Maximum allowed number of display frames that can be set through backdoor
+    static const int MAX_ALLOWED_DISPLAY_FRAMES = 2048;
 
     // Implements IBinder.
     status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
@@ -701,7 +705,7 @@
 
     void initScheduler(PhysicalDisplayId primaryDisplayId);
     void updatePhaseConfiguration(const RefreshRate&);
-    void setVsyncConfig(const VsyncModulator::VsyncConfig&);
+    void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod);
 
     /* handlePageFlip - latch a new buffer if available and compute the dirty
      * region. Returns whether a new buffer has been latched, i.e., whether it
@@ -749,8 +753,9 @@
     status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h,
                          PixelFormat format, uint32_t flags, LayerMetadata metadata,
                          sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
-                         const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr,
-                         int32_t* outId = nullptr, uint32_t* outTransformHint = nullptr);
+                         const sp<IBinder>& parentHandle, int32_t* outLayerId,
+                         const sp<Layer>& parentLayer = nullptr,
+                         uint32_t* outTransformHint = nullptr);
 
     status_t createBufferQueueLayer(const sp<Client>& client, std::string name, uint32_t w,
                                     uint32_t h, uint32_t flags, LayerMetadata metadata,
@@ -770,7 +775,7 @@
                                   sp<IBinder>* outHandle, sp<Layer>* outLayer);
 
     status_t mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
-                         sp<IBinder>* outHandle, int32_t* outId);
+                         sp<IBinder>* outHandle, int32_t* outLayerId);
 
     std::string getUniqueLayerName(const char* name);
 
@@ -947,8 +952,8 @@
         return it != mPhysicalDisplayTokens.end() ? it->second : nullptr;
     }
 
-    std::optional<DisplayId> getPhysicalDisplayIdLocked(const sp<IBinder>& displayToken) const
-            REQUIRES(mStateLock) {
+    std::optional<PhysicalDisplayId> getPhysicalDisplayIdLocked(
+            const sp<IBinder>& displayToken) const REQUIRES(mStateLock) {
         for (const auto& [id, token] : mPhysicalDisplayTokens) {
             if (token == displayToken) {
                 return id;
@@ -978,6 +983,7 @@
     void dumpStatsLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
     void clearStatsLocked(const DumpArgs& args, std::string& result);
     void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const;
+    void dumpFrameTimeline(const DumpArgs& args, std::string& result) const;
     void logFrameStats();
 
     void dumpVSync(std::string& result) const REQUIRES(mStateLock);
@@ -1111,6 +1117,8 @@
     std::unordered_map<PhysicalDisplayId, sp<IBinder>> mPhysicalDisplayTokens
             GUARDED_BY(mStateLock);
 
+    RandomDisplayIdGenerator<GpuVirtualDisplayId> mGpuVirtualDisplayIdGenerator;
+
     std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken GUARDED_BY(mStateLock);
 
     // don't use a lock for these, we don't care
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 0d0de93..db5d820 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -34,6 +34,7 @@
         "LayerUpdate_test.cpp",
         "MirrorLayer_test.cpp",
         "MultiDisplayLayerBounds_test.cpp",
+        "RefreshRateOverlay_test.cpp",
         "RelativeZ_test.cpp",
         "ScreenCapture_test.cpp",
         "SetFrameRate_test.cpp",
diff --git a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
new file mode 100644
index 0000000..05858bf
--- /dev/null
+++ b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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 <thread>
+
+#include <gtest/gtest.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+
+static constexpr int kRefreshRateOverlayCode = 1034;
+static constexpr int kRefreshRateOverlayEnable = 1;
+static constexpr int kRefreshRateOverlayDisable = 0;
+static constexpr int kRefreshRateOverlayQuery = 2;
+
+// These values must match the ones we used for developer options in
+// com.android.settings.development.ShowRefreshRatePreferenceController
+static_assert(kRefreshRateOverlayCode == 1034);
+static_assert(kRefreshRateOverlayEnable == 1);
+static_assert(kRefreshRateOverlayDisable == 0);
+static_assert(kRefreshRateOverlayQuery == 2);
+
+namespace android {
+
+namespace {
+void sendCommandToSf(int command, Parcel& reply) {
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    Parcel request;
+    request.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+    request.writeInt32(command);
+    ASSERT_EQ(NO_ERROR,
+              IInterface::asBinder(sf)->transact(kRefreshRateOverlayCode, request, &reply));
+}
+
+bool isOverlayEnabled() {
+    Parcel reply;
+    sendCommandToSf(kRefreshRateOverlayQuery, reply);
+    return reply.readBool();
+}
+
+void waitForOverlay(bool enabled) {
+    static constexpr auto kTimeout = std::chrono::nanoseconds(1s);
+    static constexpr auto kIterations = 10;
+    for (int i = 0; i < kIterations; i++) {
+        if (enabled == isOverlayEnabled()) {
+            return;
+        }
+        std::this_thread::sleep_for(kTimeout / kIterations);
+    }
+}
+
+void toggleOverlay(bool enabled) {
+    if (enabled == isOverlayEnabled()) {
+        return;
+    }
+
+    Parcel reply;
+    const auto command = enabled ? kRefreshRateOverlayEnable : kRefreshRateOverlayDisable;
+    sendCommandToSf(command, reply);
+    waitForOverlay(enabled);
+    ASSERT_EQ(enabled, isOverlayEnabled());
+}
+
+} // namespace
+
+TEST(RefreshRateOverlayTest, enableOverlay) {
+    toggleOverlay(true);
+}
+
+TEST(RefreshRateOverlayTest, disableOverlay) {
+    toggleOverlay(false);
+}
+
+TEST(RefreshRateOverlayTest, enableAndDisableOverlay) {
+    toggleOverlay(true);
+    toggleOverlay(false);
+
+    toggleOverlay(true);
+    toggleOverlay(false);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 6f1f1f2..9655cf3 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -39,6 +39,7 @@
         "CompositionTest.cpp",
         "DispSyncSourceTest.cpp",
         "DisplayIdentificationTest.cpp",
+        "DisplayIdGeneratorTest.cpp",
         "DisplayTransactionTest.cpp",
         "EventThreadTest.cpp",
         "FrameTimelineTest.cpp",
@@ -47,6 +48,7 @@
         "LayerHistoryTest.cpp",
         "LayerHistoryTestV2.cpp",
         "LayerMetadataTest.cpp",
+        "MessageQueueTest.cpp",
         "PromiseTest.cpp",
         "SchedulerTest.cpp",
         "SchedulerUtilsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp
new file mode 100644
index 0000000..be7609a
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <vector>
+
+#include <ui/DisplayId.h>
+#include "DisplayIdGenerator.h"
+
+namespace android {
+
+template <typename T>
+void testNextId(DisplayIdGenerator<T>& generator) {
+    constexpr int kNumIds = 5;
+    std::vector<T> ids;
+    for (int i = 0; i < kNumIds; i++) {
+        const auto id = generator.nextId();
+        ASSERT_TRUE(id);
+        ids.push_back(*id);
+    }
+
+    // All IDs should be different.
+    for (size_t i = 0; i < kNumIds; i++) {
+        for (size_t j = i + 1; j < kNumIds; j++) {
+            EXPECT_NE(ids[i], ids[j]);
+        }
+    }
+}
+
+TEST(DisplayIdGeneratorTest, nextIdGpuVirtual) {
+    RandomDisplayIdGenerator<GpuVirtualDisplayId> generator;
+    testNextId(generator);
+}
+
+TEST(DisplayIdGeneratorTest, nextIdHalVirtual) {
+    RandomDisplayIdGenerator<HalVirtualDisplayId> generator;
+    testNextId(generator);
+}
+
+TEST(DisplayIdGeneratorTest, markUnused) {
+    constexpr size_t kMaxIdsCount = 5;
+    RandomDisplayIdGenerator<GpuVirtualDisplayId> generator(kMaxIdsCount);
+
+    const auto id = generator.nextId();
+    EXPECT_TRUE(id);
+
+    for (int i = 1; i < kMaxIdsCount; i++) {
+        EXPECT_TRUE(generator.nextId());
+    }
+
+    EXPECT_FALSE(generator.nextId());
+
+    generator.markUnused(*id);
+    EXPECT_TRUE(generator.nextId());
+}
+
+TEST(DisplayIdGeneratorTest, maxIdsCount) {
+    constexpr size_t kMaxIdsCount = 5;
+    RandomDisplayIdGenerator<GpuVirtualDisplayId> generator(kMaxIdsCount);
+
+    for (int i = 0; i < kMaxIdsCount; i++) {
+        EXPECT_TRUE(generator.nextId());
+    }
+
+    EXPECT_FALSE(generator.nextId());
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 5864d02..e055e95 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -338,9 +338,9 @@
 struct PhysicalDisplayIdType {};
 
 template <uint64_t displayId>
-using VirtualDisplayIdType = std::integral_constant<uint64_t, displayId>;
+using HalVirtualDisplayIdType = std::integral_constant<uint64_t, displayId>;
 
-struct NoDisplayId {};
+struct GpuVirtualDisplayIdType {};
 
 template <typename>
 struct IsPhysicalDisplayId : std::bool_constant<false> {};
@@ -353,7 +353,7 @@
 
 template <typename PhysicalDisplay>
 struct DisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
-    static std::optional<PhysicalDisplayId> get() {
+    static PhysicalDisplayId get() {
         if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
             return PhysicalDisplayId::fromPort(static_cast<bool>(PhysicalDisplay::PRIMARY)
                                                        ? LEGACY_DISPLAY_TYPE_PRIMARY
@@ -363,19 +363,18 @@
         const auto info =
                 parseDisplayIdentificationData(PhysicalDisplay::PORT,
                                                PhysicalDisplay::GET_IDENTIFICATION_DATA());
-        return info ? std::make_optional(info->id) : std::nullopt;
+        return info ? info->id : PhysicalDisplayId::fromPort(PhysicalDisplay::PORT);
     }
 };
 
 template <uint64_t displayId>
-struct DisplayIdGetter<VirtualDisplayIdType<displayId>> {
-    // TODO(b/160679868) Use VirtualDisplayId
-    static std::optional<PhysicalDisplayId> get() { return PhysicalDisplayId{displayId}; }
+struct DisplayIdGetter<HalVirtualDisplayIdType<displayId>> {
+    static HalVirtualDisplayId get() { return HalVirtualDisplayId(displayId); }
 };
 
 template <>
-struct DisplayIdGetter<NoDisplayId> {
-    static std::optional<DisplayId> get() { return {}; }
+struct DisplayIdGetter<GpuVirtualDisplayIdType> {
+    static GpuVirtualDisplayId get() { return GpuVirtualDisplayId(0); }
 };
 
 template <typename>
@@ -396,7 +395,7 @@
 constexpr HWDisplayId HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010;
 
 template <uint64_t displayId>
-struct HwcDisplayIdGetter<VirtualDisplayIdType<displayId>> {
+struct HwcDisplayIdGetter<HalVirtualDisplayIdType<displayId>> {
     static constexpr std::optional<HWDisplayId> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID;
 };
 
@@ -407,8 +406,8 @@
 
 // DisplayIdType can be:
 //     1) PhysicalDisplayIdType<...> for generated ID of physical display backed by HWC.
-//     2) VirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
-//     3) NoDisplayId for virtual display without HWC backing.
+//     2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
+//     3) GpuVirtualDisplayIdType for virtual display without HWC backing.
 template <typename DisplayIdType, int width, int height, Critical critical, Async async,
           Secure secure, Primary primary, int grallocUsage>
 struct DisplayVariant {
@@ -440,17 +439,36 @@
 
     static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
         auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
-        if (auto displayId = DISPLAY_ID::get()) {
+        if (auto displayId = PhysicalDisplayId::tryCast(DISPLAY_ID::get())) {
             ceDisplayArgs.setPhysical({*displayId, DisplayConnectionType::Internal});
         } else {
+            // We turn off the use of HwcVirtualDisplays, to prevent Composition Engine
+            // from calling into HWComposer. This way all virtual displays will get
+            // a GpuVirtualDisplayId, even if we are in the HwcVirtualDisplayVariant.
+            // In this case we later override it by calling display.setDisplayIdForTesting().
             ceDisplayArgs.setUseHwcVirtualDisplays(false);
+
+            GpuVirtualDisplayId desiredDisplayId = GpuVirtualDisplayId::tryCast(DISPLAY_ID::get())
+                                                           .value_or(GpuVirtualDisplayId(0));
+
+            ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
+                    .WillByDefault(Return(desiredDisplayId));
+
+            auto& generator = test->mFlinger.gpuVirtualDisplayIdGenerator();
+            ceDisplayArgs.setGpuVirtualDisplayIdGenerator(generator);
         }
-        ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor).build();
+        ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor);
 
         auto compositionDisplay =
                 compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
                                                        ceDisplayArgs.build());
 
+        if (HalVirtualDisplayId::tryCast(DISPLAY_ID::get())) {
+            // CompositionEngine has assigned a placeholder GpuVirtualDisplayId and we need to
+            // override it with the correct HalVirtualDisplayId.
+            compositionDisplay->setDisplayIdForTesting(DISPLAY_ID::get());
+        }
+
         auto injector = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
                                                   CONNECTION_TYPE::value, HWC_DISPLAY_ID_OPT::value,
                                                   static_cast<bool>(PRIMARY));
@@ -532,8 +550,8 @@
     // Called by tests to inject a HWC display setup
     static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
         const auto displayId = DisplayVariant::DISPLAY_ID::get();
-        ASSERT_TRUE(displayId);
-        FakeHwcDisplayInjector(*displayId, HWC_DISPLAY_TYPE,
+        ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
+        FakeHwcDisplayInjector(displayId, HWC_DISPLAY_TYPE,
                                static_cast<bool>(DisplayVariant::PRIMARY))
                 .setHwcDisplayId(HWC_DISPLAY_ID)
                 .setWidth(DisplayVariant::WIDTH)
@@ -559,7 +577,7 @@
                 ::testing::UnitTest::GetInstance()->current_test_info();
 
         auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
-                                     .setPhysical({*DisplayVariant::DISPLAY_ID::get(),
+                                     .setPhysical({DisplayVariant::DISPLAY_ID::get(),
                                                    PhysicalDisplay::CONNECTION_TYPE})
                                      .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
                                      .setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
@@ -686,10 +704,11 @@
 
 template <int width, int height, Secure secure>
 struct NonHwcVirtualDisplayVariant
-      : DisplayVariant<NoDisplayId, width, height, Critical::FALSE, Async::TRUE, secure,
+      : DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, secure,
                        Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> {
-    using Base = DisplayVariant<NoDisplayId, width, height, Critical::FALSE, Async::TRUE, secure,
-                                Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
+    using Base =
+            DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE,
+                           secure, Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
 
     static void injectHwcDisplay(DisplayTransactionTest*) {}
 
@@ -698,12 +717,17 @@
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
 
+        ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
+                .WillByDefault(Return(Base::DISPLAY_ID::get()));
+
         auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
                                      .setPixels({Base::WIDTH, Base::HEIGHT})
                                      .setIsSecure(static_cast<bool>(Base::SECURE))
                                      .setPowerAdvisor(&test->mPowerAdvisor)
                                      .setName(std::string("Injected display for ") +
                                               test_info->test_case_name() + "." + test_info->name())
+                                     .setGpuVirtualDisplayIdGenerator(
+                                             test->mFlinger.gpuVirtualDisplayIdGenerator())
                                      .build();
 
         return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
@@ -725,13 +749,13 @@
 
 template <int width, int height, Secure secure>
 struct HwcVirtualDisplayVariant
-      : DisplayVariant<VirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE,
+      : DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE,
                        secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
         HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
-                          DisplayVariant<VirtualDisplayIdType<42>, width, height, Critical::FALSE,
-                                         Async::TRUE, secure, Primary::FALSE,
+                          DisplayVariant<HalVirtualDisplayIdType<42>, width, height,
+                                         Critical::FALSE, Async::TRUE, secure, Primary::FALSE,
                                          GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
-    using Base = DisplayVariant<VirtualDisplayIdType<42>, width, height, Critical::FALSE,
+    using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
                                 Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
     using Self = HwcVirtualDisplayVariant<width, height, secure>;
 
@@ -740,6 +764,14 @@
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
 
+        // In order to prevent compostition engine calling into HWComposer, we
+        // 1. turn off the use of HWC virtual displays,
+        // 2. provide a GpuVirtualDisplayIdGenerator which always returns some fake ID
+        // 3. override the ID by calling setDisplayIdForTesting()
+
+        ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
+                .WillByDefault(Return(GpuVirtualDisplayId(0)));
+
         auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
                                      .setUseHwcVirtualDisplays(false)
                                      .setPixels({Base::WIDTH, Base::HEIGHT})
@@ -747,6 +779,8 @@
                                      .setPowerAdvisor(&test->mPowerAdvisor)
                                      .setName(std::string("Injected display for ") +
                                               test_info->test_case_name() + "." + test_info->name())
+                                     .setGpuVirtualDisplayIdGenerator(
+                                             test->mFlinger.gpuVirtualDisplayIdGenerator())
                                      .build();
 
         auto compositionDisplay =
@@ -755,8 +789,9 @@
         compositionDisplay->setDisplayIdForTesting(Base::DISPLAY_ID::get());
 
         // Insert display data so that the HWC thinks it created the virtual display.
-        if (const auto displayId = Base::DISPLAY_ID::get()) {
-            test->mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
+        if (const auto displayId = Base::DISPLAY_ID::get();
+            HalVirtualDisplayId::tryCast(displayId)) {
+            test->mFlinger.mutableHwcDisplayData().try_emplace(displayId);
         }
 
         return compositionDisplay;
@@ -1776,13 +1811,11 @@
 
     DisplayDeviceState state;
     if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
-        const auto displayId = Case::Display::DISPLAY_ID::get();
+        const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
-        state.physical = {.id = static_cast<PhysicalDisplayId>(*displayId),
-                          .type = *connectionType,
-                          .hwcDisplayId = *hwcDisplayId};
+        state.physical = {.id = *displayId, .type = *connectionType, .hwcDisplayId = *hwcDisplayId};
     }
 
     state.isSecure = static_cast<bool>(Case::Display::SECURE);
@@ -1954,11 +1987,11 @@
 
     std::optional<DisplayDeviceState::Physical> expectedPhysical;
     if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
-        const auto displayId = Case::Display::DISPLAY_ID::get();
+        const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
-        expectedPhysical = {.id = static_cast<PhysicalDisplayId>(*displayId),
+        expectedPhysical = {.id = *displayId,
                             .type = *connectionType,
                             .hwcDisplayId = *hwcDisplayId};
     }
@@ -1983,9 +2016,9 @@
 
     // SF should have a display token.
     const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(displayId);
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 1);
-    auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[*displayId];
+    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
+    auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[displayId];
 
     verifyDisplayIsConnected<Case>(displayToken);
 }
@@ -2088,8 +2121,8 @@
 
     // SF should not have a display token.
     const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(displayId);
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 0);
+    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
 
     // The existing token should have been removed
     verifyDisplayIsNotConnected(existing.token());
@@ -2174,8 +2207,8 @@
 
     // SF should not have a display token.
     const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(displayId);
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 0);
+    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
 }
 
 TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) {
@@ -2213,9 +2246,9 @@
     // The existing token should have been removed
     verifyDisplayIsNotConnected(existing.token());
     const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(displayId);
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 1);
-    EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[*displayId]);
+    ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
+    EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]);
 
     // A new display should be connected in its place
 
@@ -2349,8 +2382,8 @@
 
     // A virtual display is set up but is removed from the current state.
     const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(displayId);
-    mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
+    ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
+    mFlinger.mutableHwcDisplayData().try_emplace(displayId);
     Case::Display::injectHwcDisplay(this);
     auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
     existing.inject();
@@ -3485,8 +3518,8 @@
 
     // Insert display data so that the HWC thinks it created the virtual display.
     const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(displayId);
-    mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
+    ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
+    mFlinger.mutableHwcDisplayData().try_emplace(displayId);
 
     // A virtual display device is set up
     Case::Display::injectHwcDisplay(this);
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 617f95a..69efd7f 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -43,7 +43,7 @@
     void SetUp() override {
         mFrameTimeline = std::make_unique<impl::FrameTimeline>();
         mTokenManager = &mFrameTimeline->mTokenManager;
-        maxDisplayFrames = mFrameTimeline->kMaxDisplayFrames;
+        maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
         maxTokenRetentionTime = mTokenManager->kMaxRetentionTime;
     }
 
@@ -71,10 +71,15 @@
         return mTokenManager->mPredictions;
     }
 
+    uint32_t getNumberOfDisplayFrames() {
+        std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
+        return static_cast<uint32_t>(mFrameTimeline->mDisplayFrames.size());
+    }
+
     std::unique_ptr<impl::FrameTimeline> mFrameTimeline;
     impl::TokenManager* mTokenManager;
     FenceToFenceTimeMap fenceFactory;
-    uint64_t maxDisplayFrames;
+    uint32_t* maxDisplayFrames;
     nsecs_t maxTokenRetentionTime;
 };
 
@@ -178,7 +183,7 @@
 TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) {
     // Insert kMaxDisplayFrames' count of DisplayFrames to fill the deque
     int frameTimeFactor = 0;
-    for (size_t i = 0; i < maxDisplayFrames; i++) {
+    for (size_t i = 0; i < *maxDisplayFrames; i++) {
         auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
         int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions(
                 {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
@@ -233,4 +238,47 @@
     EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
 }
 
+TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) {
+    auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    presentFence->signalForTest(2);
+
+    // Size shouldn't exceed maxDisplayFrames - 64
+    for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
+        auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+        int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
+        mFrameTimeline->setSfWakeUp(sfToken, 22);
+        mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+                                        SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->setSfPresent(27, presentFence);
+    }
+    EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
+
+    // Increase the size to 256
+    mFrameTimeline->setMaxDisplayFrames(256);
+    EXPECT_EQ(*maxDisplayFrames, 256);
+
+    for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
+        auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+        int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
+        mFrameTimeline->setSfWakeUp(sfToken, 22);
+        mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+                                        SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->setSfPresent(27, presentFence);
+    }
+    EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
+
+    // Shrink the size to 128
+    mFrameTimeline->setMaxDisplayFrames(128);
+    EXPECT_EQ(*maxDisplayFrames, 128);
+
+    for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
+        auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+        int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
+        mFrameTimeline->setSfWakeUp(sfToken, 22);
+        mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+                                        SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->setSfPresent(27, presentFence);
+    }
+    EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
+}
 } // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
new file mode 100644
index 0000000..53dfe3f
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "FrameTimeline.h"
+#include "Scheduler/MessageQueue.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+
+using namespace std::chrono_literals;
+using namespace testing;
+
+using CallbackToken = scheduler::VSyncDispatch::CallbackToken;
+
+class TestableMessageQueue : public impl::MessageQueue {
+public:
+    class MockHandler : public MessageQueue::Handler {
+    public:
+        explicit MockHandler(MessageQueue& queue) : MessageQueue::Handler(queue) {}
+        ~MockHandler() override = default;
+        MOCK_METHOD2(dispatchInvalidate, void(int64_t vsyncId, nsecs_t expectedVSyncTimestamp));
+    };
+
+    TestableMessageQueue() = default;
+    ~TestableMessageQueue() override = default;
+
+    void initHandler(const sp<MockHandler>& handler) { mHandler = handler; }
+
+    void triggerVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
+        vsyncCallback(vsyncTime, targetWakeupTime, readyTime);
+    }
+};
+
+class MockVSyncDispatch : public scheduler::VSyncDispatch {
+public:
+    MockVSyncDispatch() = default;
+    ~MockVSyncDispatch() override = default;
+
+    MOCK_METHOD2(registerCallback,
+                 CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
+    MOCK_METHOD1(unregisterCallback, void(CallbackToken));
+    MOCK_METHOD2(schedule, scheduler::ScheduleResult(CallbackToken, ScheduleTiming));
+    MOCK_METHOD1(cancel, scheduler::CancelResult(CallbackToken token));
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+class MockTokenManager : public frametimeline::TokenManager {
+public:
+    MockTokenManager() = default;
+    ~MockTokenManager() override = default;
+
+    MOCK_METHOD1(generateTokenForPredictions, int64_t(frametimeline::TimelineItem&& prediction));
+};
+
+class MessageQueueTest : public testing::Test {
+public:
+    MessageQueueTest() = default;
+    ~MessageQueueTest() override = default;
+
+    void SetUp() override {
+        EXPECT_NO_FATAL_FAILURE(mEventQueue.initHandler(mHandler));
+
+        EXPECT_CALL(mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken));
+        EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, mDuration));
+        EXPECT_CALL(mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+    }
+
+    sp<TestableMessageQueue::MockHandler> mHandler =
+            new TestableMessageQueue::MockHandler(mEventQueue);
+    MockVSyncDispatch mVSyncDispatch;
+    MockTokenManager mTokenManager;
+    TestableMessageQueue mEventQueue;
+
+    const CallbackToken mCallbackToken{5};
+    constexpr static auto mDuration = std::chrono::nanoseconds(100ms);
+    constexpr static auto mDifferentDuration = std::chrono::nanoseconds(250ms);
+};
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(MessageQueueTest, invalidate) {
+    const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+                                                                 .readyDuration = 0,
+                                                                 .earliestVsync = 0};
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+TEST_F(MessageQueueTest, invalidateTwice) {
+    InSequence s;
+    const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+                                                                 .readyDuration = 0,
+                                                                 .earliestVsync = 0};
+
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+TEST_F(MessageQueueTest, invalidateTwiceWithCallback) {
+    InSequence s;
+    const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+                                                                 .readyDuration = 0,
+                                                                 .earliestVsync = 0};
+
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+
+    const auto startTime = 100;
+    const auto endTime = startTime + mDuration.count();
+    const auto presentTime = 500;
+    const auto vsyncId = 42;
+    EXPECT_CALL(mTokenManager,
+                generateTokenForPredictions(
+                        frametimeline::TimelineItem(startTime, endTime, presentTime)))
+            .WillOnce(Return(vsyncId));
+    EXPECT_CALL(*mHandler, dispatchInvalidate(vsyncId, presentTime)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.triggerVsyncCallback(presentTime, startTime, endTime));
+
+    const auto timingAfterCallback =
+            scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+                                                     .readyDuration = 0,
+                                                     .earliestVsync = presentTime};
+
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+TEST_F(MessageQueueTest, invalidateWithDurationChange) {
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.setDuration(mDifferentDuration));
+
+    const auto timing =
+            scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDifferentDuration.count(),
+                                                     .readyDuration = 0,
+                                                     .earliestVsync = 0};
+
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 96e4f5b..9ff8f4e 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -40,6 +40,7 @@
 #include "SurfaceInterceptor.h"
 #include "TestableScheduler.h"
 #include "mock/DisplayHardware/MockDisplay.h"
+#include "mock/MockDisplayIdGenerator.h"
 
 namespace android {
 
@@ -171,6 +172,9 @@
 
     SurfaceFlinger* flinger() { return mFlinger.get(); }
     TestableScheduler* scheduler() { return mScheduler; }
+    mock::DisplayIdGenerator<GpuVirtualDisplayId>& gpuVirtualDisplayIdGenerator() {
+        return mGpuVirtualDisplayIdGenerator;
+    }
 
     // Extend this as needed for accessing SurfaceFlinger private (and public)
     // functions.
@@ -463,7 +467,8 @@
         static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0;
         static constexpr hal::PowerMode DEFAULT_POWER_MODE = hal::PowerMode::ON;
 
-        FakeHwcDisplayInjector(DisplayId displayId, hal::DisplayType hwcDisplayType, bool isPrimary)
+        FakeHwcDisplayInjector(HalDisplayId displayId, hal::DisplayType hwcDisplayType,
+                               bool isPrimary)
               : mDisplayId(displayId), mHwcDisplayType(hwcDisplayType), mIsPrimary(isPrimary) {}
 
         auto& setHwcDisplayId(hal::HWDisplayId displayId) {
@@ -536,14 +541,16 @@
             flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
 
             if (mHwcDisplayType == hal::DisplayType::PHYSICAL) {
-                flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, mDisplayId);
+                const auto physicalId = PhysicalDisplayId::tryCast(mDisplayId);
+                LOG_ALWAYS_FATAL_IF(!physicalId);
+                flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId);
                 (mIsPrimary ? flinger->mutableInternalHwcDisplayId()
                             : flinger->mutableExternalHwcDisplayId()) = mHwcDisplayId;
             }
         }
 
     private:
-        const DisplayId mDisplayId;
+        const HalDisplayId mDisplayId;
         const hal::DisplayType mHwcDisplayType;
         const bool mIsPrimary;
 
@@ -635,10 +642,10 @@
             DisplayDeviceState state;
             if (const auto type = mCreationArgs.connectionType) {
                 LOG_ALWAYS_FATAL_IF(!displayId);
+                const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
+                LOG_ALWAYS_FATAL_IF(!physicalId);
                 LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
-                state.physical = {.id = static_cast<PhysicalDisplayId>(*displayId),
-                                  .type = *type,
-                                  .hwcDisplayId = *mHwcDisplayId};
+                state.physical = {.id = *physicalId, .type = *type, .hwcDisplayId = *mHwcDisplayId};
             }
 
             state.isSecure = mCreationArgs.isSecure;
@@ -672,6 +679,7 @@
     sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
     TestableScheduler* mScheduler = nullptr;
     Hwc2::mock::Display mDisplay;
+    mock::DisplayIdGenerator<GpuVirtualDisplayId> mGpuVirtualDisplayIdGenerator;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
index 0780af1..251ab36 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
@@ -20,17 +20,13 @@
 
 #include "mock/DisplayHardware/MockComposer.h"
 
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
 
 // Explicit default instantiation is recommended.
 Composer::Composer() = default;
 Composer::~Composer() = default;
 
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
+} // namespace android::Hwc2::mock
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index cd9b87a..1ba3c0f 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -24,8 +24,7 @@
 
 class GraphicBuffer;
 
-namespace Hwc2 {
-namespace mock {
+namespace Hwc2::mock {
 
 using android::hardware::graphics::common::V1_0::ColorTransform;
 using android::hardware::graphics::common::V1_0::Transform;
@@ -140,6 +139,5 @@
     MOCK_METHOD2(getClientTargetProperty, Error(Display, IComposerClient::ClientTargetProperty*));
 };
 
-} // namespace mock
-} // namespace Hwc2
+} // namespace Hwc2::mock
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
index 2ec37c1..c9788af 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp
@@ -16,14 +16,10 @@
 
 #include "mock/DisplayHardware/MockDisplay.h"
 
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
 
 // Explicit default instantiation is recommended.
 Display::Display() = default;
 Display::~Display() = default;
 
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
\ No newline at end of file
+} // namespace android::Hwc2::mock
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
index fe99e77..a96d9db 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
@@ -22,9 +22,7 @@
 
 using android::HWC2::Layer;
 
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
 
 namespace hal = android::hardware::graphics::composer::hal;
 
@@ -98,6 +96,4 @@
     MOCK_CONST_METHOD0(isVsyncPeriodSwitchSupported, bool());
 };
 
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
index 8be7077..1ba38a8 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
@@ -16,14 +16,10 @@
 
 #include "MockPowerAdvisor.h"
 
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
 
 // Explicit default instantiation is recommended.
 PowerAdvisor::PowerAdvisor() = default;
 PowerAdvisor::~PowerAdvisor() = default;
 
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index e22d0cf..7450b5d 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -20,9 +20,7 @@
 
 #include "DisplayHardware/PowerAdvisor.h"
 
-namespace android {
-namespace Hwc2 {
-namespace mock {
+namespace android::Hwc2::mock {
 
 class PowerAdvisor : public android::Hwc2::PowerAdvisor {
 public:
@@ -34,6 +32,4 @@
     MOCK_METHOD0(notifyDisplayUpdateImminent, void());
 };
 
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
+} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h
new file mode 100644
index 0000000..cfc37ea
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "DisplayIdGenerator.h"
+
+namespace android::mock {
+
+template <typename T>
+class DisplayIdGenerator : public android::DisplayIdGenerator<T> {
+public:
+    // Explicit default instantiation is recommended.
+    DisplayIdGenerator() = default;
+    virtual ~DisplayIdGenerator() = default;
+
+    MOCK_METHOD0(nextId, std::optional<T>());
+    MOCK_METHOD1(markUnused, void(T));
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp b/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp
index 408cd35..302dc01 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp
@@ -16,12 +16,10 @@
 
 #include "mock/MockEventThread.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 // Explicit default instantiation is recommended.
 EventThread::EventThread() = default;
 EventThread::~EventThread() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index eefdec1..b4594c1 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -20,8 +20,7 @@
 
 #include "Scheduler/EventThread.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 class EventThread : public android::EventThread {
 public:
@@ -47,5 +46,4 @@
     MOCK_METHOD0(getEventThreadConnectionCount, size_t());
 };
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
index 358dfdb..417dcb0 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
@@ -16,12 +16,10 @@
 
 #include "mock/MockFrameTracer.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 // Explicit default instantiation is recommended.
 FrameTracer::FrameTracer() = default;
 FrameTracer::~FrameTracer() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h
index f768b81..305cb1c 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h
@@ -20,8 +20,7 @@
 
 #include "FrameTracer/FrameTracer.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 class FrameTracer : public android::FrameTracer {
 public:
@@ -39,5 +38,4 @@
     MOCK_METHOD0(miniDump, std::string());
 };
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index a82b583..efaa9fa 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -18,6 +18,7 @@
 
 #include <gmock/gmock.h>
 
+#include "FrameTimeline.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/MessageQueue.h"
 
@@ -34,6 +35,10 @@
     MOCK_METHOD1(postMessage, void(sp<MessageHandler>&&));
     MOCK_METHOD0(invalidate, void());
     MOCK_METHOD0(refresh, void());
+    MOCK_METHOD3(initVsync,
+                 void(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+                      std::chrono::nanoseconds));
+    MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration));
 };
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
index 7e925b9..0a0e7b5 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
@@ -20,15 +20,13 @@
 
 #include "mock/MockSurfaceInterceptor.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 // Explicit default instantiation is recommended.
 SurfaceInterceptor::SurfaceInterceptor() = default;
 SurfaceInterceptor::~SurfaceInterceptor() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
index 03a04a9..b085027 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
@@ -20,8 +20,7 @@
 
 #include "SurfaceInterceptor.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 class SurfaceInterceptor : public android::SurfaceInterceptor {
 public:
@@ -48,5 +47,4 @@
     MOCK_METHOD1(saveVSyncEvent, void(nsecs_t));
 };
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
index d686939..f8e76b2 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
@@ -16,12 +16,10 @@
 
 #include "mock/MockTimeStats.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 // Explicit default instantiation is recommended.
 TimeStats::TimeStats() = default;
 TimeStats::~TimeStats() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 4186e2b..ff37ec8 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -20,8 +20,7 @@
 
 #include "TimeStats/TimeStats.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 class TimeStats : public android::TimeStats {
 public:
@@ -59,5 +58,4 @@
     MOCK_METHOD1(setPresentFenceGlobal, void(const std::shared_ptr<FenceTime>&));
 };
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
index 8a18123..bcccae5 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
@@ -15,15 +15,11 @@
  */
 
 #include "mock/MockVSyncTracker.h"
-#include <thread>
 
-using namespace std::chrono_literals;
-namespace android {
-namespace mock {
+namespace android::mock {
 
 // Explicit default instantiation is recommended.
 VSyncTracker::VSyncTracker() = default;
 VSyncTracker::~VSyncTracker() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
index 1d87546..94d9966 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -20,8 +20,7 @@
 
 #include "Scheduler/VsyncController.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 class VsyncController : public android::scheduler::VsyncController {
 public:
@@ -36,5 +35,4 @@
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock