Merge "Treat Bluetooth-smoothened events with modified timestmaps as synthetic"
diff --git a/include/input/Input.h b/include/input/Input.h
index dd74a51..d298d81 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -209,6 +209,8 @@
 
 bool isFromSource(uint32_t source, uint32_t test);
 
+bool isStylusToolType(uint32_t toolType);
+
 /*
  * Flags that flow alongside events in the input dispatch system to help with certain
  * policy decisions such as waking from device sleep.
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index bd72a53..e0b7d16 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -531,40 +531,23 @@
     LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str());
     LOG_ALWAYS_FATAL_IF(hasServer(), "Each RpcServer can only have one server.");
 
-    RpcTransportFd transportFd(unique_fd(TEMP_FAILURE_RETRY(
-            socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0))));
-    if (!transportFd.fd.ok()) {
+    unique_fd socket_fd(TEMP_FAILURE_RETRY(
+            socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)));
+    if (!socket_fd.ok()) {
         int savedErrno = errno;
         ALOGE("Could not create socket: %s", strerror(savedErrno));
         return -savedErrno;
     }
-
-    if (0 != TEMP_FAILURE_RETRY(bind(transportFd.fd.get(), addr.addr(), addr.addrSize()))) {
+    if (0 != TEMP_FAILURE_RETRY(bind(socket_fd.get(), addr.addr(), addr.addrSize()))) {
         int savedErrno = errno;
         ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
         return -savedErrno;
     }
 
-    // Right now, we create all threads at once, making accept4 slow. To avoid hanging the client,
-    // the backlog is increased to a large number.
-    // TODO(b/189955605): Once we create threads dynamically & lazily, the backlog can be reduced
-    //  to 1.
-    if (0 != TEMP_FAILURE_RETRY(listen(transportFd.fd.get(), 50 /*backlog*/))) {
-        int savedErrno = errno;
-        ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
-        return -savedErrno;
-    }
-
-    LOG_RPC_DETAIL("Successfully setup socket server %s", addr.toString().c_str());
-
-    if (status_t status = setupExternalServer(std::move(transportFd.fd)); status != OK) {
-        ALOGE("Another thread has set up server while calling setupSocketServer. Race?");
-        return status;
-    }
-    return OK;
+    return setupRawSocketServer(std::move(socket_fd));
 }
 
-status_t RpcServer::setupRawSocketServer(base::unique_fd socket_fd) {
+status_t RpcServer::setupRawSocketServer(unique_fd socket_fd) {
     LOG_ALWAYS_FATAL_IF(!socket_fd.ok(), "Socket must be setup to listen.");
     RpcTransportFd transportFd(std::move(socket_fd));
 
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index c320e8d..8693022 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -129,7 +129,13 @@
     }
 
     T* array;
-    if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY;
+    if (!allocator(arrayData, length, &array)) {
+        if (length < 0) {
+            return STATUS_UNEXPECTED_NULL;
+        } else {
+            return STATUS_NO_MEMORY;
+        }
+    }
 
     if (length <= 0) return STATUS_OK;
     if (array == nullptr) return STATUS_NO_MEMORY;
@@ -157,7 +163,13 @@
     }
 
     char16_t* array;
-    if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY;
+    if (!allocator(arrayData, length, &array)) {
+        if (length < 0) {
+            return STATUS_UNEXPECTED_NULL;
+        } else {
+            return STATUS_NO_MEMORY;
+        }
+    }
 
     if (length <= 0) return STATUS_OK;
     if (array == nullptr) return STATUS_NO_MEMORY;
@@ -204,7 +216,13 @@
         return status;
     }
 
-    if (!allocator(arrayData, length)) return STATUS_NO_MEMORY;
+    if (!allocator(arrayData, length)) {
+        if (length < 0) {
+            return STATUS_UNEXPECTED_NULL;
+        } else {
+            return STATUS_NO_MEMORY;
+        }
+    }
 
     if (length <= 0) return STATUS_OK;
 
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 03e4a23..a999d59 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -723,5 +723,7 @@
             "smoreland@google.com",
             "waghpawan@google.com",
         ],
+        // Adds bugs to hotlist "AIDL fuzzers bugs" on buganizer
+        hotlists: ["4637097"],
     },
 }
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index cf5a7e7..3685f54 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -238,6 +238,10 @@
     return (source & test) == test;
 }
 
+bool isStylusToolType(uint32_t toolType) {
+    return toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || toolType == AMOTION_EVENT_TOOL_TYPE_ERASER;
+}
+
 VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) {
     return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(),
              event.getSource(), event.getDisplayId()},
diff --git a/services/inputflinger/PreferStylusOverTouchBlocker.cpp b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
index beec2e1..ddd5146 100644
--- a/services/inputflinger/PreferStylusOverTouchBlocker.cpp
+++ b/services/inputflinger/PreferStylusOverTouchBlocker.cpp
@@ -25,8 +25,7 @@
     for (size_t i = 0; i < args.pointerCount; i++) {
         // Make sure we are canceling stylus pointers
         const int32_t toolType = args.pointerProperties[i].toolType;
-        if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
-            toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+        if (isStylusToolType(toolType)) {
             hasStylus = true;
         }
         if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp
index ec41025..c170b81 100644
--- a/services/inputflinger/UnwantedInteractionBlocker.cpp
+++ b/services/inputflinger/UnwantedInteractionBlocker.cpp
@@ -99,14 +99,17 @@
 }
 
 static int getLinuxToolCode(int toolType) {
-    if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) {
-        return BTN_TOOL_PEN;
+    switch (toolType) {
+        case AMOTION_EVENT_TOOL_TYPE_STYLUS:
+            return BTN_TOOL_PEN;
+        case AMOTION_EVENT_TOOL_TYPE_ERASER:
+            return BTN_TOOL_RUBBER;
+        case AMOTION_EVENT_TOOL_TYPE_FINGER:
+            return BTN_TOOL_FINGER;
+        default:
+            ALOGW("Got tool type %" PRId32 ", converting to BTN_TOOL_FINGER", toolType);
+            return BTN_TOOL_FINGER;
     }
-    if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
-        return BTN_TOOL_FINGER;
-    }
-    ALOGW("Got tool type %" PRId32 ", converting to BTN_TOOL_FINGER", toolType);
-    return BTN_TOOL_FINGER;
 }
 
 static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) {
@@ -195,7 +198,7 @@
 static std::optional<NotifyMotionArgs> removeStylusPointerIds(const NotifyMotionArgs& args) {
     std::set<int32_t> stylusPointerIds;
     for (uint32_t i = 0; i < args.pointerCount; i++) {
-        if (args.pointerProperties[i].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) {
+        if (isStylusToolType(args.pointerProperties[i].toolType)) {
             stylusPointerIds.insert(args.pointerProperties[i].id);
         }
     }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 7b7c42a..87a4ff4 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -481,8 +481,7 @@
 
 bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) {
     return isFromSource(entry.source, AINPUT_SOURCE_STYLUS) &&
-            (entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
-             entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER);
+            isStylusToolType(entry.pointerProperties[pointerIndex].toolType);
 }
 
 // Determines if the given window can be targeted as InputTarget::Flags::FOREGROUND.
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index f8b1b3f..f04a646 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -66,8 +66,7 @@
         return false;
     }
     const auto actionIndex = MotionEvent::getActionIndex(motionArgs.action);
-    return motionArgs.pointerProperties[actionIndex].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
-            motionArgs.pointerProperties[actionIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER;
+    return isStylusToolType(motionArgs.pointerProperties[actionIndex].toolType);
 }
 
 // --- InputReader ---
diff --git a/services/inputflinger/reader/include/StylusState.h b/services/inputflinger/reader/include/StylusState.h
index 8d14d3c..ff15e0c 100644
--- a/services/inputflinger/reader/include/StylusState.h
+++ b/services/inputflinger/reader/include/StylusState.h
@@ -24,27 +24,19 @@
 
 struct StylusState {
     /* Time the stylus event was received. */
-    nsecs_t when;
-    /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */
-    float pressure;
+    nsecs_t when{};
+    /*
+     * Pressure as reported by the stylus if supported, normalized to the range [0, 1.0].
+     * The presence of a pressure value indicates that the stylus is able to tell whether it is
+     * touching the display.
+     */
+    std::optional<float> pressure{};
     /* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */
-    uint32_t buttons;
+    uint32_t buttons{};
     /* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */
-    int32_t toolType;
+    int32_t toolType{AMOTION_EVENT_TOOL_TYPE_UNKNOWN};
 
-    void copyFrom(const StylusState& other) {
-        when = other.when;
-        pressure = other.pressure;
-        buttons = other.buttons;
-        toolType = other.toolType;
-    }
-
-    void clear() {
-        when = LLONG_MAX;
-        pressure = 0.f;
-        buttons = 0;
-        toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
-    }
+    void clear() { *this = StylusState{}; }
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 56fc5fa..2809939 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -32,8 +32,10 @@
 
 void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
     InputMapper::populateDeviceInfo(info);
-    info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f,
-                         0.0f);
+    if (mRawPressureAxis.valid) {
+        info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f,
+                             0.0f, 0.0f);
+    }
 }
 
 void ExternalStylusInputMapper::dump(std::string& dump) {
@@ -79,13 +81,12 @@
         mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
     }
 
-    int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure();
     if (mRawPressureAxis.valid) {
-        mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue;
-    } else if (mTouchButtonAccumulator.isToolActive()) {
-        mStylusState.pressure = 1.0f;
-    } else {
-        mStylusState.pressure = 0.0f;
+        auto rawPressure = static_cast<float>(mSingleTouchMotionAccumulator.getAbsolutePressure());
+        mStylusState.pressure = (rawPressure - mRawPressureAxis.minValue) /
+                static_cast<float>(mRawPressureAxis.maxValue - mRawPressureAxis.minValue);
+    } else if (mTouchButtonAccumulator.hasButtonTouch()) {
+        mStylusState.pressure = mTouchButtonAccumulator.isHovering() ? 0.0f : 1.0f;
     }
 
     mStylusState.buttons = mTouchButtonAccumulator.getButtonState();
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index 844afe0..8e3539c 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -19,6 +19,7 @@
 #include "InputMapper.h"
 
 #include "InputDevice.h"
+#include "input/PrintTools.h"
 
 namespace android {
 
@@ -129,7 +130,7 @@
 
 void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) {
     dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when);
-    dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure);
+    dump += StringPrintf(INDENT4 "Pressure: %s\n", toString(state.pressure).c_str());
     dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons);
     dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType);
 }
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 8e757a5..ccff353 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -200,8 +200,7 @@
 }
 
 bool MultiTouchInputMapper::hasStylus() const {
-    return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus() ||
-            shouldSimulateStylusWithTouch();
+    return mTouchButtonAccumulator.hasStylus() || shouldSimulateStylusWithTouch();
 }
 
 bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index d4c2e4f..bc55fe5 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -1599,8 +1599,7 @@
             uint32_t id = idBits.clearFirstMarkedBit();
             const RawPointerData::Pointer& pointer =
                     mCurrentRawState.rawPointerData.pointerForId(id);
-            if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
-                pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+            if (isStylusToolType(pointer.toolType)) {
                 mCurrentCookedState.stylusIdBits.markBit(id);
             } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER ||
                        pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
@@ -1613,8 +1612,7 @@
             uint32_t id = idBits.clearFirstMarkedBit();
             const RawPointerData::Pointer& pointer =
                     mCurrentRawState.rawPointerData.pointerForId(id);
-            if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS ||
-                pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {
+            if (isStylusToolType(pointer.toolType)) {
                 mCurrentCookedState.stylusIdBits.markBit(id);
             }
         }
@@ -1709,22 +1707,24 @@
 void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) {
     CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData;
     const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData;
+    if (!mFusedStylusPointerId || !currentPointerData.isTouching(*mFusedStylusPointerId)) {
+        return;
+    }
 
-    if (mFusedStylusPointerId && currentPointerData.isTouching(*mFusedStylusPointerId)) {
-        float pressure = mExternalStylusState.pressure;
-        if (pressure == 0.0f && lastPointerData.isTouching(*mFusedStylusPointerId)) {
-            const PointerCoords& coords =
-                    lastPointerData.pointerCoordsForId(*mFusedStylusPointerId);
-            pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
-        }
-        PointerCoords& coords = currentPointerData.editPointerCoordsWithId(*mFusedStylusPointerId);
-        coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+    float pressure = lastPointerData.isTouching(*mFusedStylusPointerId)
+            ? lastPointerData.pointerCoordsForId(*mFusedStylusPointerId)
+                      .getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)
+            : 0.f;
+    if (mExternalStylusState.pressure && *mExternalStylusState.pressure > 0.f) {
+        pressure = *mExternalStylusState.pressure;
+    }
+    PointerCoords& coords = currentPointerData.editPointerCoordsWithId(*mFusedStylusPointerId);
+    coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
 
+    if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
         PointerProperties& properties =
                 currentPointerData.editPointerPropertiesWithId(*mFusedStylusPointerId);
-        if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
-            properties.toolType = mExternalStylusState.toolType;
-        }
+        properties.toolType = mExternalStylusState.toolType;
     }
 }
 
@@ -1733,36 +1733,48 @@
         return false;
     }
 
-    const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 &&
-            state.rawPointerData.pointerCount != 0;
-    if (initialDown) {
-        if (mExternalStylusState.pressure != 0.0f) {
-            ALOGD_IF(DEBUG_STYLUS_FUSION, "Have both stylus and touch data, beginning fusion");
-            mFusedStylusPointerId = state.rawPointerData.touchingIdBits.firstMarkedBit();
-        } else if (timeout) {
-            ALOGD_IF(DEBUG_STYLUS_FUSION, "Timeout expired, assuming touch is not a stylus.");
-            mFusedStylusPointerId.reset();
-            mExternalStylusFusionTimeout = LLONG_MAX;
-        } else {
-            if (mExternalStylusFusionTimeout == LLONG_MAX) {
-                mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT;
-            }
-            ALOGD_IF(DEBUG_STYLUS_FUSION,
-                     "No stylus data but stylus is connected, requesting timeout (%" PRId64 "ms)",
-                     mExternalStylusFusionTimeout);
-            getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
-            return true;
-        }
-    }
-
     // Check if the stylus pointer has gone up.
     if (mFusedStylusPointerId &&
         !state.rawPointerData.touchingIdBits.hasBit(*mFusedStylusPointerId)) {
         ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus pointer is going up");
         mFusedStylusPointerId.reset();
+        return false;
     }
 
-    return false;
+    const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 &&
+            state.rawPointerData.pointerCount != 0;
+    if (!initialDown) {
+        return false;
+    }
+
+    if (!mExternalStylusState.pressure) {
+        ALOGD_IF(DEBUG_STYLUS_FUSION, "Stylus does not support pressure, no pointer fusion needed");
+        return false;
+    }
+
+    if (*mExternalStylusState.pressure != 0.0f) {
+        ALOGD_IF(DEBUG_STYLUS_FUSION, "Have both stylus and touch data, beginning fusion");
+        mFusedStylusPointerId = state.rawPointerData.touchingIdBits.firstMarkedBit();
+        return false;
+    }
+
+    if (timeout) {
+        ALOGD_IF(DEBUG_STYLUS_FUSION, "Timeout expired, assuming touch is not a stylus.");
+        mFusedStylusPointerId.reset();
+        mExternalStylusFusionTimeout = LLONG_MAX;
+        return false;
+    }
+
+    // We are waiting for the external stylus to report a pressure value. Withhold touches from
+    // being processed until we either get pressure data or timeout.
+    if (mExternalStylusFusionTimeout == LLONG_MAX) {
+        mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT;
+    }
+    ALOGD_IF(DEBUG_STYLUS_FUSION,
+             "No stylus data but stylus is connected, requesting timeout (%" PRId64 "ms)",
+             mExternalStylusFusionTimeout);
+    getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
+    return true;
 }
 
 std::list<NotifyArgs> TouchInputMapper::timeoutExpired(nsecs_t when) {
@@ -1786,7 +1798,7 @@
 std::list<NotifyArgs> TouchInputMapper::updateExternalStylusState(const StylusState& state) {
     std::list<NotifyArgs> out;
     const bool buttonsChanged = mExternalStylusState.buttons != state.buttons;
-    mExternalStylusState.copyFrom(state);
+    mExternalStylusState = state;
     if (mFusedStylusPointerId || mExternalStylusFusionTimeout != LLONG_MAX || buttonsChanged) {
         // The following three cases are handled here:
         // - We're in the middle of a fused stream of data;
@@ -3751,12 +3763,15 @@
     PointerCoords pointerCoords[MAX_POINTERS];
     PointerProperties pointerProperties[MAX_POINTERS];
     uint32_t pointerCount = 0;
+    bool stylusToolFound = false;
     while (!idBits.isEmpty()) {
         uint32_t id = idBits.clearFirstMarkedBit();
         uint32_t index = idToIndex[id];
         pointerProperties[pointerCount].copyFrom(properties[index]);
         pointerCoords[pointerCount].copyFrom(coords[index]);
 
+        stylusToolFound |= isStylusToolType(pointerProperties[pointerCount].toolType);
+
         if (changedId >= 0 && id == uint32_t(changedId)) {
             action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
         }
@@ -3788,6 +3803,12 @@
     if (mDeviceMode == DeviceMode::POINTER) {
         mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
     }
+    if (stylusToolFound) {
+        // Dynamically add the stylus source when there's a stylus tool being used to cover the case
+        // where we cannot reliably detect whether a multi-touch device will ever produce stylus
+        // events when it is initially being configured.
+        source |= AINPUT_SOURCE_STYLUS;
+    }
     const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
     const int32_t deviceId = getDeviceId();
     std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index fba7b79..788ec58 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -795,6 +795,8 @@
     [[nodiscard]] std::list<NotifyArgs> abortPointerSimple(nsecs_t when, nsecs_t readTime,
                                                            uint32_t policyFlags);
 
+    // Attempts to assign a pointer id to the external stylus. Returns true if the state should be
+    // withheld from further processing while waiting for data from the stylus.
     bool assignExternalStylusId(const RawState& state, bool timeout);
     void applyExternalStylusButtonState(nsecs_t when);
     void applyExternalStylusTouchState(nsecs_t when);
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
index b0cef67..8746729 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
@@ -24,12 +24,11 @@
 // --- MultiTouchMotionAccumulator ---
 
 MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
-      : mCurrentSlot(-1), mUsingSlotsProtocol(false), mHaveStylus(false) {}
+      : mCurrentSlot(-1), mUsingSlotsProtocol(false) {}
 
 void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount,
                                             bool usingSlotsProtocol) {
     mUsingSlotsProtocol = usingSlotsProtocol;
-    mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
     mSlots = std::vector<Slot>(slotCount);
 
     mCurrentSlot = -1;
@@ -146,10 +145,6 @@
     }
 }
 
-bool MultiTouchMotionAccumulator::hasStylus() const {
-    return mHaveStylus;
-}
-
 void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) {
     if (!slot.mInUse) {
         ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i",
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
index 625a00f..62bc780 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
@@ -75,7 +75,6 @@
     void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol);
     void process(const RawEvent* rawEvent);
     void finishSync();
-    bool hasStylus() const;
 
     inline size_t getSlotCount() const { return mSlots.size(); }
     inline const Slot& getSlot(size_t index) const {
@@ -87,7 +86,6 @@
     int32_t mCurrentSlot;
     std::vector<Slot> mSlots;
     bool mUsingSlotsProtocol;
-    bool mHaveStylus;
 
     void resetSlots();
     void warnIfNotInUse(const RawEvent& event, const Slot& slot);
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
index 1891205..bc23a8e 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -167,4 +167,8 @@
     return mHaveStylus;
 }
 
+bool TouchButtonAccumulator::hasButtonTouch() const {
+    return mHaveBtnTouch;
+}
+
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index 65b0a62..c2de23c 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -40,6 +40,7 @@
     bool isToolActive() const;
     bool isHovering() const;
     bool hasStylus() const;
+    bool hasButtonTouch() const;
 
 private:
     bool mHaveBtnTouch{};
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 1c0d8a0..5c5fc77 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -2577,6 +2577,14 @@
     InputDeviceInfo mDeviceInfo;
 };
 
+TEST_F(TouchIntegrationTest, MultiTouchDeviceSource) {
+    // The UinputTouchScreen is an MT device that supports MT_TOOL_TYPE and also supports stylus
+    // buttons. It should show up as a touchscreen, stylus, and keyboard (for reporting button
+    // presses).
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD,
+              mDeviceInfo.getSources());
+}
+
 TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
     NotifyMotionArgs args;
     const Point centerPoint = mDevice->getCenterPoint();
@@ -3162,7 +3170,10 @@
 
     // Set a pressure value of 0 on the stylus. It doesn't generate any events.
     const auto& RAW_PRESSURE_MAX = UinputExternalStylusWithPressure::RAW_PRESSURE_MAX;
+    // Send a non-zero value first to prevent the kernel from consuming the zero event.
+    stylus->setPressure(100);
     stylus->setPressure(0);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
 
     // Start a finger gesture. The touch device will withhold generating any touches for
     // up to 72 milliseconds while waiting for pressure data from the external stylus.
@@ -3209,6 +3220,45 @@
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
 }
 
+TEST_F(ExternalStylusIntegrationTest, UnfusedExternalStylus) {
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // Create an external stylus device that does not support pressure. It should not affect any
+    // touch pointers.
+    std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+    const auto stylusInfo = findDeviceByName(stylus->getName());
+    ASSERT_TRUE(stylusInfo);
+
+    ASSERT_EQ(AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_KEYBOARD, stylusInfo->getSources());
+
+    const auto touchscreenId = mDeviceInfo.getId();
+
+    // Start a finger gesture and ensure a finger pointer is generated for it, without waiting for
+    // pressure data from the external stylus.
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendTrackingId(FIRST_TRACKING_ID);
+    mDevice->sendToolType(MT_TOOL_FINGER);
+    mDevice->sendDown(centerPoint);
+    auto waitUntil = std::chrono::system_clock::now() +
+            std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT));
+    mDevice->sendSync();
+    ASSERT_NO_FATAL_FAILURE(
+            mTestListener
+                    ->assertNotifyMotionWasCalled(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+                                                        WithToolType(
+                                                                AMOTION_EVENT_TOOL_TYPE_FINGER),
+                                                        WithButtonState(0),
+                                                        WithDeviceId(touchscreenId),
+                                                        WithPressure(1.f)),
+                                                  waitUntil));
+
+    // The external stylus did not generate any events.
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasNotCalled());
+}
+
 // --- InputDeviceTest ---
 class InputDeviceTest : public testing::Test {
 protected:
@@ -10738,6 +10788,41 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
+TEST_F(MultiTouchInputMapperTest, ToolTypeSource) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | PRESSURE | TOOL_TYPE);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // Even if the device supports reporting the ABS_MT_TOOL_TYPE axis, which could give it the
+    // ability to report MT_TOOL_PEN, we do not report the device as coming from a stylus source.
+    // Due to limitations in the evdev protocol, we cannot say for certain that a device is capable
+    // of reporting stylus events just because it supports ABS_MT_TOOL_TYPE.
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
+
+    // However, if the device ever ends up reporting an event with MT_TOOL_PEN, it should be
+    // reported with the stylus source, even through the device doesn't support the stylus source.
+    processId(mapper, FIRST_TRACKING_ID);
+    processToolType(mapper, MT_TOOL_PEN);
+    processPosition(mapper, 100, 200);
+    processPressure(mapper, RAW_PRESSURE_MAX);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+                  WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS),
+                  WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+    processId(mapper, INVALID_TRACKING_ID);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+                  WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS),
+                  WithToolType(AMOTION_EVENT_TOOL_TYPE_STYLUS))));
+
+    // The mapper should still report only a touchscreen source.
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
+}
+
 // --- MultiTouchInputMapperTest_ExternalDevice ---
 
 class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index a1299ee..2801072 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -69,16 +69,18 @@
     ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be called."));
 }
 
-void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs) {
+void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs,
+                                                    std::optional<TimePoint> waitUntil) {
     ASSERT_NO_FATAL_FAILURE(
             assertCalled<NotifyMotionArgs>(outEventArgs,
-                                           "Expected notifyMotion() to have been called."));
+                                           "Expected notifyMotion() to have been called.",
+                                           waitUntil));
 }
 
 void TestInputListener::assertNotifyMotionWasCalled(
-        const ::testing::Matcher<NotifyMotionArgs>& matcher) {
+        const ::testing::Matcher<NotifyMotionArgs>& matcher, std::optional<TimePoint> waitUntil) {
     NotifyMotionArgs outEventArgs;
-    ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs));
+    ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs, waitUntil));
     ASSERT_THAT(outEventArgs, matcher);
 }
 
@@ -119,15 +121,18 @@
 }
 
 template <class NotifyArgsType>
-void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) {
+void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message,
+                                     std::optional<TimePoint> waitUntil) {
     std::unique_lock<std::mutex> lock(mLock);
     base::ScopedLockAssertion assumeLocked(mLock);
 
     std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
     if (queue.empty()) {
-        const bool eventReceived =
-                mCondition.wait_for(lock, mEventHappenedTimeout,
-                                    [&queue]() REQUIRES(mLock) { return !queue.empty(); });
+        const auto time =
+                waitUntil.value_or(std::chrono::system_clock::now() + mEventHappenedTimeout);
+        const bool eventReceived = mCondition.wait_until(lock, time, [&queue]() REQUIRES(mLock) {
+            return !queue.empty();
+        });
         if (!eventReceived) {
             FAIL() << "Timed out waiting for event: " << message.c_str();
         }
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index c53f8e0..9665f70 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -50,9 +50,11 @@
 
     void assertNotifyKeyWasNotCalled();
 
-    void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr);
+    void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr,
+                                     std::optional<TimePoint> waitUntil = {});
 
-    void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher);
+    void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher,
+                                     std::optional<TimePoint> waitUntil = {});
 
     void assertNotifyMotionWasNotCalled(std::optional<TimePoint> waitUntil = {});
 
@@ -65,7 +67,8 @@
 
 private:
     template <class NotifyArgsType>
-    void assertCalled(NotifyArgsType* outEventArgs, std::string message);
+    void assertCalled(NotifyArgsType* outEventArgs, std::string message,
+                      std::optional<TimePoint> waitUntil = {});
 
     template <class NotifyArgsType>
     void assertNotCalled(std::string message, std::optional<TimePoint> timeout = {});
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index e3fe7a7..be5fffc 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -195,8 +195,7 @@
         mDrawingState.color.b = -1.0_hf;
     }
 
-    mFrameTracker.setDisplayRefreshPeriod(
-            args.flinger->mScheduler->getVsyncPeriodFromRefreshRateSelector());
+    mFrameTracker.setDisplayRefreshPeriod(args.flinger->mScheduler->getLeaderVsyncPeriod());
 
     mOwnerUid = args.ownerUid;
     mOwnerPid = args.ownerPid;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 0e80817..65ee487 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -363,14 +363,16 @@
         }
     }
 
-    void resetIdleTimer(bool kernelOnly) {
-        if (!mIdleTimer) {
-            return;
+    void resetKernelIdleTimer() {
+        if (mIdleTimer && mConfig.kernelIdleTimerController) {
+            mIdleTimer->reset();
         }
-        if (kernelOnly && !mConfig.kernelIdleTimerController.has_value()) {
-            return;
+    }
+
+    void resetIdleTimer() {
+        if (mIdleTimer) {
+            mIdleTimer->reset();
         }
-        mIdleTimer->reset();
     }
 
     void dump(utils::Dumper&) const EXCLUDES(mLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 6108d92..f1fcc88 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -70,7 +70,7 @@
     mTouchTimer.reset();
 
     // Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler.
-    setRefreshRateSelector(nullptr);
+    demoteLeaderDisplay();
 }
 
 void Scheduler::startTimers() {
@@ -95,40 +95,29 @@
     }
 }
 
-void Scheduler::setRefreshRateSelector(RefreshRateSelectorPtr newSelectorPtr) {
-    // No need to lock for reads on kMainThreadContext.
-    if (const auto& selectorPtr = FTL_FAKE_GUARD(mRefreshRateSelectorLock, mRefreshRateSelector)) {
-        unbindIdleTimer(*selectorPtr);
-    }
+void Scheduler::setLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
+    demoteLeaderDisplay();
 
-    {
-        // Clear state that depends on the current RefreshRateSelector.
-        std::scoped_lock lock(mPolicyLock);
-        mPolicy = {};
-    }
-
-    std::scoped_lock lock(mRefreshRateSelectorLock);
-    mRefreshRateSelector = std::move(newSelectorPtr);
-
-    if (mRefreshRateSelector) {
-        bindIdleTimer(*mRefreshRateSelector);
-    }
+    std::scoped_lock lock(mDisplayLock);
+    promoteLeaderDisplay(leaderIdOpt);
 }
 
 void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
-    if (!mLeaderDisplayId) {
-        mLeaderDisplayId = displayId;
-    }
+    demoteLeaderDisplay();
 
+    std::scoped_lock lock(mDisplayLock);
     mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr));
+
+    promoteLeaderDisplay();
 }
 
 void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
-    if (mLeaderDisplayId == displayId) {
-        mLeaderDisplayId.reset();
-    }
+    demoteLeaderDisplay();
 
+    std::scoped_lock lock(mDisplayLock);
     mRefreshRateSelectors.erase(displayId);
+
+    promoteLeaderDisplay();
 }
 
 void Scheduler::run() {
@@ -163,7 +152,7 @@
 
 std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
     const bool supportsFrameRateOverrideByContent =
-            holdRefreshRateSelector()->supportsFrameRateOverrideByContent();
+            leaderSelectorPtr()->supportsFrameRateOverrideByContent();
     return mFrameRateOverrideMappings
             .getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
 }
@@ -178,8 +167,6 @@
 }
 
 impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
-    std::scoped_lock lock(mRefreshRateSelectorLock);
-
     return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
         return !isVsyncValid(TimePoint::fromNs(expectedVsyncTimestamp), uid);
     };
@@ -187,7 +174,7 @@
 
 impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
     return [this](uid_t uid) {
-        const Fps refreshRate = holdRefreshRateSelector()->getActiveModePtr()->getFps();
+        const Fps refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
         const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs();
 
         const auto frameRate = getFrameRateOverride(uid);
@@ -281,7 +268,7 @@
 
 void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
     const bool supportsFrameRateOverrideByContent =
-            holdRefreshRateSelector()->supportsFrameRateOverrideByContent();
+            leaderSelectorPtr()->supportsFrameRateOverrideByContent();
 
     std::vector<FrameRateOverride> overrides =
             mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
@@ -322,8 +309,7 @@
     // If the mode is not the current mode, this means that a
     // mode change is in progress. In that case we shouldn't dispatch an event
     // as it will be dispatched when the current mode changes.
-    if (std::scoped_lock lock(mRefreshRateSelectorLock);
-        mRefreshRateSelector->getActiveModePtr() != mPolicy.mode) {
+    if (leaderSelectorPtr()->getActiveModePtr() != mPolicy.mode) {
         return;
     }
 
@@ -416,10 +402,7 @@
     const nsecs_t last = mLastResyncTime.exchange(now);
 
     if (now - last > kIgnoreDelay) {
-        const auto refreshRate = [&] {
-            std::scoped_lock lock(mRefreshRateSelectorLock);
-            return mRefreshRateSelector->getActiveModePtr()->getFps();
-        }();
+        const auto refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
         resyncToHardwareVsync(false, refreshRate);
     }
 }
@@ -478,12 +461,9 @@
 
 void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
                                    LayerHistory::LayerUpdateType updateType) {
-    {
-        std::scoped_lock lock(mRefreshRateSelectorLock);
-        if (!mRefreshRateSelector->canSwitch()) return;
+    if (leaderSelectorPtr()->canSwitch()) {
+        mLayerHistory.record(layer, presentTime, systemTime(), updateType);
     }
-
-    mLayerHistory.record(layer, presentTime, systemTime(), updateType);
 }
 
 void Scheduler::setModeChangePending(bool pending) {
@@ -496,7 +476,7 @@
 }
 
 void Scheduler::chooseRefreshRateForContent() {
-    const auto selectorPtr = holdRefreshRateSelector();
+    const auto selectorPtr = leaderSelectorPtr();
     if (!selectorPtr->canSwitch()) return;
 
     ATRACE_CALL();
@@ -506,16 +486,13 @@
 }
 
 void Scheduler::resetIdleTimer() {
-    std::scoped_lock lock(mRefreshRateSelectorLock);
-    mRefreshRateSelector->resetIdleTimer(/*kernelOnly*/ false);
+    leaderSelectorPtr()->resetIdleTimer();
 }
 
 void Scheduler::onTouchHint() {
     if (mTouchTimer) {
         mTouchTimer->reset();
-
-        std::scoped_lock lock(mRefreshRateSelectorLock);
-        mRefreshRateSelector->resetIdleTimer(/*kernelOnly*/ true);
+        leaderSelectorPtr()->resetKernelIdleTimer();
     }
 }
 
@@ -535,30 +512,12 @@
     mLayerHistory.clear();
 }
 
-void Scheduler::bindIdleTimer(RefreshRateSelector& selector) {
-    selector.setIdleTimerCallbacks(
-            {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
-                          .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
-             .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
-                        .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
-
-    selector.startIdleTimer();
-}
-
-void Scheduler::unbindIdleTimer(RefreshRateSelector& selector) {
-    selector.stopIdleTimer();
-    selector.clearIdleTimerCallbacks();
-}
-
 void Scheduler::kernelIdleTimerCallback(TimerState state) {
     ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
 
     // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
     // magic number
-    const Fps refreshRate = [&] {
-        std::scoped_lock lock(mRefreshRateSelectorLock);
-        return mRefreshRateSelector->getActiveModePtr()->getFps();
-    }();
+    const Fps refreshRate = leaderSelectorPtr()->getActiveModePtr()->getFps();
 
     constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
     using namespace fps_approx_ops;
@@ -614,7 +573,11 @@
     }
     {
         utils::Dumper::Section section(dumper, "Policy"sv);
-
+        {
+            std::scoped_lock lock(mDisplayLock);
+            ftl::FakeGuard guard(kMainThreadContext);
+            dumper.dump("leaderDisplayId"sv, mLeaderDisplayId);
+        }
         dumper.dump("layerHistory"sv, mLayerHistory.dump());
         dumper.dump("touchTimer"sv, mTouchTimer.transform(&OneShotTimer::interval));
         dumper.dump("displayPowerTimer"sv, mDisplayPowerTimer.transform(&OneShotTimer::interval));
@@ -638,17 +601,44 @@
 }
 
 bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
-    // we always update mFrameRateOverridesByContent here
-    // supportsFrameRateOverridesByContent will be checked
-    // when getting FrameRateOverrides from mFrameRateOverrideMappings
-    if (!consideredSignals.idle) {
-        const auto frameRateOverrides =
-                holdRefreshRateSelector()->getFrameRateOverrides(mPolicy.contentRequirements,
-                                                                 displayRefreshRate,
-                                                                 consideredSignals);
-        return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
+    if (consideredSignals.idle) return false;
+
+    const auto frameRateOverrides =
+            leaderSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements,
+                                                       displayRefreshRate, consideredSignals);
+
+    // Note that RefreshRateSelector::supportsFrameRateOverrideByContent is checked when querying
+    // the FrameRateOverrideMappings rather than here.
+    return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
+}
+
+void Scheduler::promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) {
+    // TODO(b/241286431): Choose the leader display.
+    mLeaderDisplayId = leaderIdOpt.value_or(mRefreshRateSelectors.begin()->first);
+    ALOGI("Display %s is the leader", to_string(*mLeaderDisplayId).c_str());
+
+    if (const auto leaderPtr = leaderSelectorPtrLocked()) {
+        leaderPtr->setIdleTimerCallbacks(
+                {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
+                              .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
+                 .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
+                            .onExpired =
+                                    [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
+
+        leaderPtr->startIdleTimer();
     }
-    return false;
+}
+
+void Scheduler::demoteLeaderDisplay() {
+    // No need to lock for reads on kMainThreadContext.
+    if (const auto leaderPtr = FTL_FAKE_GUARD(mDisplayLock, leaderSelectorPtrLocked())) {
+        leaderPtr->stopIdleTimer();
+        leaderPtr->clearIdleTimerCallbacks();
+    }
+
+    // Clear state that depends on the leader's RefreshRateSelector.
+    std::scoped_lock lock(mPolicyLock);
+    mPolicy = {};
 }
 
 template <typename S, typename T>
@@ -660,23 +650,29 @@
     bool frameRateOverridesChanged;
 
     {
-        std::lock_guard<std::mutex> lock(mPolicyLock);
+        std::scoped_lock lock(mPolicyLock);
 
         auto& currentState = mPolicy.*statePtr;
         if (currentState == newState) return {};
         currentState = std::forward<T>(newState);
 
-        auto modeChoices = chooseDisplayModes();
-
-        // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest to go
-        // through. Fix this by tracking per-display Scheduler::Policy and timers.
+        DisplayModeChoiceMap modeChoices;
         DisplayModePtr modePtr;
-        std::tie(modePtr, consideredSignals) =
-                modeChoices.get(*mLeaderDisplayId)
-                        .transform([](const DisplayModeChoice& choice) {
-                            return std::make_pair(choice.modePtr, choice.consideredSignals);
-                        })
-                        .value();
+        {
+            std::scoped_lock lock(mDisplayLock);
+            ftl::FakeGuard guard(kMainThreadContext);
+
+            modeChoices = chooseDisplayModes();
+
+            // TODO(b/240743786): The leader display's mode must change for any DisplayModeRequest
+            // to go through. Fix this by tracking per-display Scheduler::Policy and timers.
+            std::tie(modePtr, consideredSignals) =
+                    modeChoices.get(*mLeaderDisplayId)
+                            .transform([](const DisplayModeChoice& choice) {
+                                return std::make_pair(choice.modePtr, choice.consideredSignals);
+                            })
+                            .value();
+        }
 
         modeRequests.reserve(modeChoices.size());
         for (auto& [id, choice] : modeChoices) {
@@ -807,7 +803,7 @@
     // Make sure the stored mode is up to date.
     if (mPolicy.mode) {
         const auto ranking =
-                holdRefreshRateSelector()
+                leaderSelectorPtr()
                         ->getRankedRefreshRates(mPolicy.contentRequirements, makeGlobalSignals())
                         .ranking;
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 04f3b69..fb23071 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -22,7 +22,6 @@
 #include <future>
 #include <memory>
 #include <mutex>
-#include <optional>
 #include <unordered_map>
 #include <utility>
 
@@ -33,6 +32,8 @@
 #include <ui/GraphicTypes.h>
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
+#include <ftl/fake_guard.h>
+#include <ftl/optional.h>
 #include <scheduler/Features.h>
 #include <scheduler/Time.h>
 #include <ui/DisplayId.h>
@@ -108,12 +109,15 @@
 
     void startTimers();
 
-    using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
-    void setRefreshRateSelector(RefreshRateSelectorPtr) REQUIRES(kMainThreadContext)
-            EXCLUDES(mRefreshRateSelectorLock);
+    // TODO(b/241285191): Remove this API by promoting leader in onScreen{Acquired,Released}.
+    void setLeaderDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext)
+            EXCLUDES(mDisplayLock);
 
-    void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr);
-    void unregisterDisplay(PhysicalDisplayId);
+    using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
+
+    void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr) REQUIRES(kMainThreadContext)
+            EXCLUDES(mDisplayLock);
+    void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
 
     void run();
 
@@ -165,7 +169,7 @@
     // Otherwise, if hardware vsync is not already enabled then this method will
     // no-op.
     void resyncToHardwareVsync(bool makeAvailable, Fps refreshRate);
-    void resync() EXCLUDES(mRefreshRateSelectorLock);
+    void resync() EXCLUDES(mDisplayLock);
     void forceNextResync() { mLastResyncTime = 0; }
 
     // Passes a vsync sample to VsyncController. periodFlushed will be true if
@@ -176,14 +180,14 @@
 
     // Layers are registered on creation, and unregistered when the weak reference expires.
     void registerLayer(Layer*);
-    void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType)
-            EXCLUDES(mRefreshRateSelectorLock);
+    void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType)
+            EXCLUDES(mDisplayLock);
     void setModeChangePending(bool pending);
     void setDefaultFrameRateCompatibility(Layer*);
     void deregisterLayer(Layer*);
 
     // Detects content using layer history, and selects a matching refresh rate.
-    void chooseRefreshRateForContent() EXCLUDES(mRefreshRateSelectorLock);
+    void chooseRefreshRateForContent() EXCLUDES(mDisplayLock);
 
     void resetIdleTimer();
 
@@ -228,11 +232,10 @@
     void setGameModeRefreshRateForUid(FrameRateOverride);
 
     // Retrieves the overridden refresh rate for a given uid.
-    std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mRefreshRateSelectorLock);
+    std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock);
 
-    nsecs_t getVsyncPeriodFromRefreshRateSelector() const EXCLUDES(mRefreshRateSelectorLock) {
-        std::scoped_lock lock(mRefreshRateSelectorLock);
-        return mRefreshRateSelector->getActiveModePtr()->getFps().getPeriodNsecs();
+    nsecs_t getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) {
+        return leaderSelectorPtr()->getActiveModePtr()->getFps().getPeriodNsecs();
     }
 
     // Returns the framerate of the layer with the given sequence ID
@@ -255,21 +258,23 @@
     sp<EventThreadConnection> createConnectionInternal(
             EventThread*, EventRegistrationFlags eventRegistration = {});
 
-    void bindIdleTimer(RefreshRateSelector&) REQUIRES(kMainThreadContext, mRefreshRateSelectorLock);
-
-    // Blocks until the timer thread exits. `mRefreshRateSelectorLock` must not be locked by the
-    // caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
-    static void unbindIdleTimer(RefreshRateSelector&) REQUIRES(kMainThreadContext)
-            EXCLUDES(mRefreshRateSelectorLock);
-
     // Update feature state machine to given state when corresponding timer resets or expires.
-    void kernelIdleTimerCallback(TimerState) EXCLUDES(mRefreshRateSelectorLock);
+    void kernelIdleTimerCallback(TimerState) EXCLUDES(mDisplayLock);
     void idleTimerCallback(TimerState);
     void touchTimerCallback(TimerState);
     void displayPowerTimerCallback(TimerState);
 
     void setVsyncPeriod(nsecs_t period);
 
+    // Chooses a leader among the registered displays, unless `leaderIdOpt` is specified. The new
+    // `mLeaderDisplayId` is never `std::nullopt`.
+    void promoteLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt = std::nullopt)
+            REQUIRES(kMainThreadContext, mDisplayLock);
+
+    // Blocks until the leader's idle timer thread exits. `mDisplayLock` must not be locked by the
+    // caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
+    void demoteLeaderDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock);
+
     struct Policy;
 
     // Sets the S state of the policy to the T value under mPolicyLock, and chooses a display mode
@@ -296,23 +301,20 @@
     };
 
     using DisplayModeChoiceMap = display::PhysicalDisplayMap<PhysicalDisplayId, DisplayModeChoice>;
-    DisplayModeChoiceMap chooseDisplayModes() const REQUIRES(mPolicyLock);
+
+    // See mDisplayLock for thread safety.
+    DisplayModeChoiceMap chooseDisplayModes() const
+            REQUIRES(mPolicyLock, mDisplayLock, kMainThreadContext);
 
     GlobalSignals makeGlobalSignals() const REQUIRES(mPolicyLock);
 
     bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock);
 
-    void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateSelectorLock);
+    void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
 
-    android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
-            EXCLUDES(mRefreshRateSelectorLock);
+    android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const;
     android::impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
 
-    RefreshRateSelectorPtr holdRefreshRateSelector() const EXCLUDES(mRefreshRateSelectorLock) {
-        std::scoped_lock lock(mRefreshRateSelectorLock);
-        return mRefreshRateSelector;
-    }
-
     // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
     struct Connection {
         sp<EventThreadConnection> connection;
@@ -342,10 +344,34 @@
 
     ISchedulerCallback& mSchedulerCallback;
 
+    // mDisplayLock may be locked while under mPolicyLock.
     mutable std::mutex mPolicyLock;
 
-    display::PhysicalDisplayMap<PhysicalDisplayId, RefreshRateSelectorPtr> mRefreshRateSelectors;
-    std::optional<PhysicalDisplayId> mLeaderDisplayId;
+    // Only required for reads outside kMainThreadContext. kMainThreadContext is the only writer, so
+    // must lock for writes but not reads. See also mPolicyLock for locking order.
+    mutable std::mutex mDisplayLock;
+
+    display::PhysicalDisplayMap<PhysicalDisplayId, RefreshRateSelectorPtr> mRefreshRateSelectors
+            GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext);
+
+    ftl::Optional<PhysicalDisplayId> mLeaderDisplayId GUARDED_BY(mDisplayLock)
+            GUARDED_BY(kMainThreadContext);
+
+    RefreshRateSelectorPtr leaderSelectorPtr() const EXCLUDES(mDisplayLock) {
+        std::scoped_lock lock(mDisplayLock);
+        return leaderSelectorPtrLocked();
+    }
+
+    RefreshRateSelectorPtr leaderSelectorPtrLocked() const REQUIRES(mDisplayLock) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        const RefreshRateSelectorPtr noLeader;
+        return mLeaderDisplayId
+                .and_then([this](PhysicalDisplayId leaderId)
+                                  REQUIRES(mDisplayLock, kMainThreadContext) {
+                                      return mRefreshRateSelectors.get(leaderId);
+                                  })
+                .value_or(std::cref(noLeader));
+    }
 
     struct Policy {
         // Policy for choosing the display mode.
@@ -367,10 +393,6 @@
         std::optional<ModeChangedParams> cachedModeChangedParams;
     } mPolicy GUARDED_BY(mPolicyLock);
 
-    // TODO(b/255635821): Remove this by instead looking up the `mLeaderDisplayId` selector.
-    mutable std::mutex mRefreshRateSelectorLock;
-    RefreshRateSelectorPtr mRefreshRateSelector GUARDED_BY(mRefreshRateSelectorLock);
-
     std::mutex mVsyncTimelineLock;
     std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
             GUARDED_BY(mVsyncTimelineLock);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 992760f..dc9129c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2959,18 +2959,16 @@
                                                  displaySurface, producer);
 
     if (mScheduler && !display->isVirtual()) {
-        auto selectorPtr = display->holdRefreshRateSelector();
-
-        // Display modes are reloaded on hotplug reconnect.
-        if (display->isPrimary()) {
+        const auto displayId = display->getPhysicalId();
+        {
             // TODO(b/241285876): Annotate `processDisplayAdded` instead.
             ftl::FakeGuard guard(kMainThreadContext);
-            mScheduler->setRefreshRateSelector(selectorPtr);
+
+            // For hotplug reconnect, renew the registration since display modes have been reloaded.
+            mScheduler->registerDisplay(displayId, display->holdRefreshRateSelector());
         }
 
-        const auto displayId = display->getPhysicalId();
-        mScheduler->registerDisplay(displayId, std::move(selectorPtr));
-        dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
+        dispatchDisplayHotplugEvent(displayId, true);
     }
 
     mDisplays.try_emplace(displayToken, std::move(display));
@@ -3429,9 +3427,7 @@
         !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
         features |= Feature::kPresentFences;
     }
-
-    auto selectorPtr = display->holdRefreshRateSelector();
-    if (selectorPtr->kernelIdleTimerController()) {
+    if (display->refreshRateSelector().kernelIdleTimerController()) {
         features |= Feature::kKernelIdleTimer;
     }
 
@@ -3439,8 +3435,7 @@
                                                         static_cast<ISchedulerCallback&>(*this),
                                                         features);
     mScheduler->createVsyncSchedule(features);
-    mScheduler->setRefreshRateSelector(selectorPtr);
-    mScheduler->registerDisplay(display->getPhysicalId(), std::move(selectorPtr));
+    mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
 
     setVsyncEnabled(false);
     mScheduler->startTimers();
@@ -6999,9 +6994,11 @@
     }
     mActiveDisplayId = activeDisplay->getPhysicalId();
     activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
+
     updateInternalDisplayVsyncLocked(activeDisplay);
     mScheduler->setModeChangePending(false);
-    mScheduler->setRefreshRateSelector(activeDisplay->holdRefreshRateSelector());
+    mScheduler->setLeaderDisplay(mActiveDisplayId);
+
     onActiveDisplaySizeChanged(activeDisplay);
     mActiveDisplayTransformHint = activeDisplay->getTransformHint();
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 0706598..c2d3343 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -464,8 +464,7 @@
               typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
     void modulateVsync(Handler handler, Args... args) {
         if (const auto config = (*mVsyncModulator.*handler)(args...)) {
-            const auto vsyncPeriod = mScheduler->getVsyncPeriodFromRefreshRateSelector();
-            setVsyncConfig(*config, vsyncPeriod);
+            setVsyncConfig(*config, mScheduler->getLeaderVsyncPeriod());
         }
     }
 
@@ -928,7 +927,8 @@
             const sp<compositionengine::DisplaySurface>& displaySurface,
             const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock);
     void processDisplayChangesLocked() REQUIRES(mStateLock, kMainThreadContext);
-    void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock);
+    void processDisplayRemoved(const wp<IBinder>& displayToken)
+            REQUIRES(mStateLock, kMainThreadContext);
     void processDisplayChanged(const wp<IBinder>& displayToken,
                                const DisplayDeviceState& currentState,
                                const DisplayDeviceState& drawingState)
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index ee5392e..577f84e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -230,7 +230,10 @@
                       ISchedulerCallback& callback)
           : Scheduler(*this, callback, Feature::kContentDetection) {
         mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
-        setRefreshRateSelector(std::move(selectorPtr));
+
+        const auto displayId = FTL_FAKE_GUARD(kMainThreadContext,
+                                              selectorPtr->getActiveMode().getPhysicalDisplayId());
+        registerDisplay(displayId, std::move(selectorPtr));
     }
 
     ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
@@ -242,7 +245,7 @@
 
     auto &mutableLayerHistory() { return mLayerHistory; }
 
-    auto refreshRateSelector() { return holdRefreshRateSelector(); }
+    auto refreshRateSelector() { return leaderSelectorPtr(); }
 
     void replaceTouchTimer(int64_t millis) {
         if (mTouchTimer) {
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index ea4666e..8e333a3 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -190,8 +190,10 @@
     sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
     ASSERT_EQ(1u, mScheduler->layerHistorySize());
 
-    mScheduler->setRefreshRateSelector(
-            std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode60->getId()));
+    // Replace `mSelector` with a new `RefreshRateSelector` that has different display modes.
+    mScheduler->registerDisplay(kDisplayId1,
+                                std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+                                                                      kDisplay1Mode60->getId()));
 
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
     mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
@@ -234,11 +236,9 @@
 }
 
 TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
-    const auto selectorPtr =
-            std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode60->getId());
-
-    mScheduler->registerDisplay(kDisplayId1, selectorPtr);
-    mScheduler->setRefreshRateSelector(selectorPtr);
+    mScheduler->registerDisplay(kDisplayId1,
+                                std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+                                                                      kDisplay1Mode60->getId()));
 
     const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
     EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index ba214d5..3f8fe0d 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -43,7 +43,10 @@
                       ISchedulerCallback& callback)
           : Scheduler(*this, callback, Feature::kContentDetection) {
         mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
-        setRefreshRateSelector(std::move(selectorPtr));
+
+        const auto displayId = FTL_FAKE_GUARD(kMainThreadContext,
+                                              selectorPtr->getActiveMode().getPhysicalDisplayId());
+        registerDisplay(displayId, std::move(selectorPtr));
 
         ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
             // Execute task to prevent broken promise exception on destruction.
@@ -67,12 +70,27 @@
     auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; }
     auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
 
-    auto refreshRateSelector() { return holdRefreshRateSelector(); }
-    bool hasRefreshRateSelectors() const { return !mRefreshRateSelectors.empty(); }
+    auto refreshRateSelector() { return leaderSelectorPtr(); }
 
-    void setRefreshRateSelector(RefreshRateSelectorPtr selectorPtr) {
+    const auto& refreshRateSelectors() const NO_THREAD_SAFETY_ANALYSIS {
+        return mRefreshRateSelectors;
+    }
+
+    bool hasRefreshRateSelectors() const { return !refreshRateSelectors().empty(); }
+
+    void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
         ftl::FakeGuard guard(kMainThreadContext);
-        return Scheduler::setRefreshRateSelector(std::move(selectorPtr));
+        Scheduler::registerDisplay(displayId, std::move(selectorPtr));
+    }
+
+    void unregisterDisplay(PhysicalDisplayId displayId) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        Scheduler::unregisterDisplay(displayId);
+    }
+
+    void setLeaderDisplay(PhysicalDisplayId displayId) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        Scheduler::setLeaderDisplay(displayId);
     }
 
     auto& mutableLayerHistory() { return mLayerHistory; }
@@ -115,14 +133,13 @@
     using Scheduler::DisplayModeChoice;
     using Scheduler::DisplayModeChoiceMap;
 
-    DisplayModeChoiceMap chooseDisplayModes() {
-        std::lock_guard<std::mutex> lock(mPolicyLock);
+    DisplayModeChoiceMap chooseDisplayModes() NO_THREAD_SAFETY_ANALYSIS {
         return Scheduler::chooseDisplayModes();
     }
 
     void dispatchCachedReportedMode() {
         std::lock_guard<std::mutex> lock(mPolicyLock);
-        return Scheduler::dispatchCachedReportedMode();
+        Scheduler::dispatchCachedReportedMode();
     }
 
     void clearCachedReportedMode() {
@@ -131,7 +148,7 @@
     }
 
     void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
-        return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
+        Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
     }
 
 private:
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index dbb8bd8..c15b3c8 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -843,33 +843,36 @@
 
             sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs);
             mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display);
-            if (mFlinger.scheduler()) {
-                mFlinger.scheduler()->registerDisplay(display->getPhysicalId(),
-                                                      display->holdRefreshRateSelector());
-            }
 
             DisplayDeviceState state;
             state.isSecure = mCreationArgs.isSecure;
 
             if (mConnectionType) {
                 LOG_ALWAYS_FATAL_IF(!displayId);
-                const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
-                LOG_ALWAYS_FATAL_IF(!physicalId);
+                const auto physicalIdOpt = PhysicalDisplayId::tryCast(*displayId);
+                LOG_ALWAYS_FATAL_IF(!physicalIdOpt);
+                const auto physicalId = *physicalIdOpt;
+
                 LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
 
                 const auto activeMode = modes.get(activeModeId);
                 LOG_ALWAYS_FATAL_IF(!activeMode);
 
-                state.physical = {.id = *physicalId,
+                state.physical = {.id = physicalId,
                                   .hwcDisplayId = *mHwcDisplayId,
                                   .activeMode = activeMode->get()};
 
                 const auto it = mFlinger.mutablePhysicalDisplays()
-                                        .emplace_or_replace(*physicalId, mDisplayToken, *physicalId,
+                                        .emplace_or_replace(physicalId, mDisplayToken, physicalId,
                                                             *mConnectionType, std::move(modes),
                                                             ui::ColorModes(), std::nullopt)
                                         .first;
 
+                if (mFlinger.scheduler()) {
+                    mFlinger.scheduler()->registerDisplay(physicalId,
+                                                          display->holdRefreshRateSelector());
+                }
+
                 display->setActiveMode(activeModeId, it->second.snapshot());
             }
 
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index ba98696..40cf9fb 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -49,7 +49,13 @@
  * in VkBindImageMemorySwapchainInfoKHR will be additionally chained to the
  * pNext chain of VkBindImageMemoryInfo and passed down to the driver.
  */
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8
+/*
+ * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 9
+ *
+ * This version of the extension is largely designed to clean up the mix of
+ * GrallocUsage and GrallocUsage2
+ */
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 9
 #define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
 
 #define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \
@@ -61,6 +67,8 @@
     VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1)
 #define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID \
     VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2)
+#define VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID \
+    VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 3)
 
 /* clang-format off */
 typedef enum VkSwapchainImageUsageFlagBitsANDROID {
@@ -90,6 +98,7 @@
  * format: gralloc format requested when the buffer was allocated
  * usage: gralloc usage requested when the buffer was allocated
  * usage2: gralloc usage requested when the buffer was allocated
+ * usage3: gralloc usage requested when the buffer was allocated
  */
 typedef struct {
     VkStructureType                   sType;
@@ -98,7 +107,8 @@
     int                               stride;
     int                               format;
     int                               usage; /* DEPRECATED in SPEC_VERSION 6 */
-    VkNativeBufferUsage2ANDROID       usage2; /* ADDED in SPEC_VERSION 6 */
+    VkNativeBufferUsage2ANDROID       usage2; /* DEPRECATED in SPEC_VERSION 9 */
+    uint64_t                          usage3; /* ADDED in SPEC_VERSION 9 */
 } VkNativeBufferANDROID;
 
 /*
@@ -127,6 +137,21 @@
     VkBool32                          sharedImage;
 } VkPhysicalDevicePresentationPropertiesANDROID;
 
+/*
+ * struct VkGrallocUsageInfoANDROID
+ *
+ * sType: VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID
+ * pNext: NULL or a pointer to a structure extending this structure
+ * format: value specifying the format the image will be created with
+ * imageUsage: bitmask of VkImageUsageFlagBits describing intended usage
+ */
+typedef struct {
+    VkStructureType                   sType;
+    const void*                       pNext;
+    VkFormat                          format;
+    VkImageUsageFlags                 imageUsage;
+} VkGrallocUsageInfoANDROID;
+
 /* DEPRECATED in SPEC_VERSION 6 */
 typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(
     VkDevice                          device,
@@ -134,7 +159,7 @@
     VkImageUsageFlags                 imageUsage,
     int*                              grallocUsage);
 
-/* ADDED in SPEC_VERSION 6 */
+/* DEPRECATED in SPEC_VERSION 9 */
 typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(
     VkDevice                          device,
     VkFormat                          format,
@@ -143,6 +168,12 @@
     uint64_t*                         grallocConsumerUsage,
     uint64_t*                         grallocProducerUsage);
 
+/* ADDED in SPEC_VERSION 9 */
+typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage3ANDROID)(
+    VkDevice                          device,
+    const VkGrallocUsageInfoANDROID*  grallocUsageInfo,
+    uint64_t*                         grallocUsage);
+
 typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(
     VkDevice                          device,
     VkImage                           image,
@@ -167,7 +198,7 @@
     int*                              grallocUsage
 );
 
-/* ADDED in SPEC_VERSION 6 */
+/* DEPRECATED in SPEC_VERSION 9 */
 VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID(
     VkDevice                          device,
     VkFormat                          format,
@@ -177,6 +208,13 @@
     uint64_t*                         grallocProducerUsage
 );
 
+/* ADDED in SPEC_VERSION 9 */
+VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage3ANDROID(
+    VkDevice                          device,
+    const VkGrallocUsageInfoANDROID*  grallocUsageInfo,
+    uint64_t*                         grallocUsage
+);
+
 VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID(
     VkDevice                          device,
     VkImage                           image,
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 7664518..4927150 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1027,6 +1027,39 @@
     }
 }
 
+bool GetAndroidNativeBufferSpecVersion9Support(
+    VkPhysicalDevice physicalDevice) {
+    const InstanceData& data = GetData(physicalDevice);
+
+    // Call to get propertyCount
+    uint32_t propertyCount = 0;
+    ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
+    VkResult result = data.driver.EnumerateDeviceExtensionProperties(
+        physicalDevice, nullptr, &propertyCount, nullptr);
+    ATRACE_END();
+
+    // Call to enumerate properties
+    std::vector<VkExtensionProperties> properties(propertyCount);
+    ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
+    result = data.driver.EnumerateDeviceExtensionProperties(
+        physicalDevice, nullptr, &propertyCount, properties.data());
+    ATRACE_END();
+
+    for (uint32_t i = 0; i < propertyCount; i++) {
+        auto& prop = properties[i];
+
+        if (strcmp(prop.extensionName,
+                   VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME) != 0)
+            continue;
+
+        if (prop.specVersion >= 9) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 VkResult EnumerateDeviceExtensionProperties(
     VkPhysicalDevice physicalDevice,
     const char* pLayerName,
@@ -1061,6 +1094,37 @@
                 VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION});
     }
 
+    // Conditionally add VK_EXT_IMAGE_COMPRESSION_CONTROL* if feature and ANB
+    // support is provided by the driver
+    VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT
+        swapchainCompFeats = {};
+    swapchainCompFeats.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT;
+    swapchainCompFeats.pNext = nullptr;
+    VkPhysicalDeviceImageCompressionControlFeaturesEXT imageCompFeats = {};
+    imageCompFeats.sType =
+        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
+    imageCompFeats.pNext = &swapchainCompFeats;
+
+    VkPhysicalDeviceFeatures2 feats2 = {};
+    feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+    feats2.pNext = &imageCompFeats;
+
+    GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+
+    bool anb9 = GetAndroidNativeBufferSpecVersion9Support(physicalDevice);
+
+    if (anb9 && imageCompFeats.imageCompressionControl) {
+        loader_extensions.push_back(
+            {VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME,
+             VK_EXT_IMAGE_COMPRESSION_CONTROL_SPEC_VERSION});
+    }
+    if (anb9 && swapchainCompFeats.imageCompressionControlSwapchain) {
+        loader_extensions.push_back(
+            {VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME,
+             VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_SPEC_VERSION});
+    }
+
     // enumerate our extensions first
     if (!pLayerName && pProperties) {
         uint32_t count = std::min(
@@ -1254,15 +1318,18 @@
         return VK_ERROR_INCOMPATIBLE_DRIVER;
     }
 
-    // sanity check ANDROID_native_buffer implementation, whose set of
+    // Confirming ANDROID_native_buffer implementation, whose set of
     // entrypoints varies according to the spec version.
     if ((wrapper.GetHalExtensions()[ProcHook::ANDROID_native_buffer]) &&
         !data->driver.GetSwapchainGrallocUsageANDROID &&
-        !data->driver.GetSwapchainGrallocUsage2ANDROID) {
-        ALOGE("Driver's implementation of ANDROID_native_buffer is broken;"
-              " must expose at least one of "
-              "vkGetSwapchainGrallocUsageANDROID or "
-              "vkGetSwapchainGrallocUsage2ANDROID");
+        !data->driver.GetSwapchainGrallocUsage2ANDROID &&
+        !data->driver.GetSwapchainGrallocUsage3ANDROID) {
+        ALOGE(
+            "Driver's implementation of ANDROID_native_buffer is broken;"
+            " must expose at least one of "
+            "vkGetSwapchainGrallocUsageANDROID or "
+            "vkGetSwapchainGrallocUsage2ANDROID or "
+            "vkGetSwapchainGrallocUsage3ANDROID");
 
         data->driver.DestroyDevice(dev, pAllocator);
         FreeDeviceData(data, data_allocator);
@@ -1441,10 +1508,83 @@
 
     if (driver.GetPhysicalDeviceFeatures2) {
         driver.GetPhysicalDeviceFeatures2(physicalDevice, pFeatures);
+    } else {
+        driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures);
+    }
+
+    // Conditionally add imageCompressionControlSwapchain if
+    // imageCompressionControl is supported Check for imageCompressionControl in
+    // the pChain
+    bool imageCompressionControl = false;
+    bool imageCompressionControlInChain = false;
+    bool imageCompressionControlSwapchainInChain = false;
+    VkPhysicalDeviceFeatures2* pFeats = pFeatures;
+    while (pFeats) {
+        switch (pFeats->sType) {
+            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT: {
+                const VkPhysicalDeviceImageCompressionControlFeaturesEXT*
+                    compressionFeat = reinterpret_cast<
+                        const VkPhysicalDeviceImageCompressionControlFeaturesEXT*>(
+                        pFeats);
+                imageCompressionControl =
+                    compressionFeat->imageCompressionControl;
+                imageCompressionControlInChain = true;
+            } break;
+
+            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT: {
+                imageCompressionControlSwapchainInChain = true;
+            } break;
+
+            default:
+                break;
+        }
+        pFeats = reinterpret_cast<VkPhysicalDeviceFeatures2*>(pFeats->pNext);
+    }
+
+    if (!imageCompressionControlSwapchainInChain) {
         return;
     }
 
-    driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures);
+    // If not in pchain, explicitly query for imageCompressionControl
+    if (!imageCompressionControlInChain) {
+        VkPhysicalDeviceImageCompressionControlFeaturesEXT imageCompFeats = {};
+        imageCompFeats.sType =
+            VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
+        imageCompFeats.pNext = nullptr;
+
+        VkPhysicalDeviceFeatures2 feats2 = {};
+        feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+        feats2.pNext = &imageCompFeats;
+
+        if (driver.GetPhysicalDeviceFeatures2) {
+            driver.GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+        } else {
+            driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, &feats2);
+        }
+
+        imageCompressionControl = imageCompFeats.imageCompressionControl;
+    }
+
+    // Only enumerate imageCompressionControlSwapchin if imageCompressionControl
+    if (imageCompressionControl) {
+        pFeats = pFeatures;
+        while (pFeats) {
+            switch (pFeats->sType) {
+                case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT: {
+                    VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*
+                        compressionFeat = reinterpret_cast<
+                            VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*>(
+                            pFeats);
+                    compressionFeat->imageCompressionControlSwapchain = true;
+                } break;
+
+                default:
+                    break;
+            }
+            pFeats =
+                reinterpret_cast<VkPhysicalDeviceFeatures2*>(pFeats->pNext);
+        }
+    }
 }
 
 void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice,
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 14c516b..4d2bbd6 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -107,6 +107,8 @@
     VkPhysicalDevice physicalDevice,
     VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties);
 
+bool GetAndroidNativeBufferSpecVersion9Support(VkPhysicalDevice physicalDevice);
+
 VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance,
                                                   const char* pName);
 VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device,
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index b436db1..de98aa7 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -496,6 +496,13 @@
         nullptr,
     },
     {
+        "vkGetSwapchainGrallocUsage3ANDROID",
+        ProcHook::DEVICE,
+        ProcHook::ANDROID_native_buffer,
+        nullptr,
+        nullptr,
+    },
+    {
         "vkGetSwapchainGrallocUsageANDROID",
         ProcHook::DEVICE,
         ProcHook::ANDROID_native_buffer,
@@ -664,6 +671,7 @@
     INIT_PROC(false, dev, GetDeviceQueue2);
     INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID);
     INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
+    INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage3ANDROID);
     INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID);
     INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID);
     // clang-format on
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 079f9cc..2f60086 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -123,6 +123,7 @@
     PFN_vkGetDeviceQueue2 GetDeviceQueue2;
     PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
     PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID;
+    PFN_vkGetSwapchainGrallocUsage3ANDROID GetSwapchainGrallocUsage3ANDROID;
     PFN_vkAcquireImageANDROID AcquireImageANDROID;
     PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
     // clang-format on
diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt
index f49e8f3..b189c68 100644
--- a/vulkan/libvulkan/libvulkan.map.txt
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -178,6 +178,7 @@
     vkGetImageSparseMemoryRequirements;
     vkGetImageSparseMemoryRequirements2; # introduced=28
     vkGetImageSubresourceLayout;
+    vkGetImageSubresourceLayout2EXT; # introduced=UpsideDownCake
     vkGetInstanceProcAddr;
     vkGetMemoryAndroidHardwareBufferANDROID; # introduced=28
     vkGetPhysicalDeviceExternalBufferProperties; # introduced=28
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 87b3a89..4f7b923 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -948,11 +948,60 @@
             surface_formats.data());
 
         if (result == VK_SUCCESS || result == VK_INCOMPLETE) {
+            const auto& driver = GetData(physicalDevice).driver;
+
             // marshal results individually due to stride difference.
-            // completely ignore any chained extension structs.
             uint32_t formats_to_marshal = *pSurfaceFormatCount;
             for (uint32_t i = 0u; i < formats_to_marshal; i++) {
                 pSurfaceFormats[i].surfaceFormat = surface_formats[i];
+
+                // Query the compression properties for the surface format
+                if (pSurfaceFormats[i].pNext) {
+                    VkImageCompressionPropertiesEXT* surfaceCompressionProps =
+                        reinterpret_cast<VkImageCompressionPropertiesEXT*>(
+                            pSurfaceFormats[i].pNext);
+
+                    if (surfaceCompressionProps &&
+                        driver.GetPhysicalDeviceImageFormatProperties2KHR) {
+                        VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {};
+                        imageFormatInfo.sType =
+                            VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
+                        imageFormatInfo.format =
+                            pSurfaceFormats[i].surfaceFormat.format;
+                        imageFormatInfo.pNext = nullptr;
+
+                        VkImageCompressionControlEXT compressionControl = {};
+                        compressionControl.sType =
+                            VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT;
+                        compressionControl.pNext = imageFormatInfo.pNext;
+
+                        imageFormatInfo.pNext = &compressionControl;
+
+                        VkImageCompressionPropertiesEXT compressionProps = {};
+                        compressionProps.sType =
+                            VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT;
+                        compressionProps.pNext = nullptr;
+
+                        VkImageFormatProperties2KHR imageFormatProps = {};
+                        imageFormatProps.sType =
+                            VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR;
+                        imageFormatProps.pNext = &compressionProps;
+
+                        VkResult compressionRes =
+                            driver.GetPhysicalDeviceImageFormatProperties2KHR(
+                                physicalDevice, &imageFormatInfo,
+                                &imageFormatProps);
+                        if (compressionRes == VK_SUCCESS) {
+                            surfaceCompressionProps->imageCompressionFlags =
+                                compressionProps.imageCompressionFlags;
+                            surfaceCompressionProps
+                                ->imageCompressionFixedRateFlags =
+                                compressionProps.imageCompressionFixedRateFlags;
+                        } else {
+                            return compressionRes;
+                        }
+                    }
+                }
             }
         }
 
@@ -1370,8 +1419,48 @@
         num_images = 1;
     }
 
+    void* usage_info_pNext = nullptr;
+    VkImageCompressionControlEXT image_compression = {};
     uint64_t native_usage = 0;
-    if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
+    if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
+        ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID");
+        VkGrallocUsageInfoANDROID gralloc_usage_info = {};
+        gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID;
+        gralloc_usage_info.format = create_info->imageFormat;
+        gralloc_usage_info.imageUsage = create_info->imageUsage;
+
+        // Look through the pNext chain for an image compression control struct
+        // if one is found AND the appropriate extensions are enabled,
+        // append it to be the gralloc usage pNext chain
+        const VkSwapchainCreateInfoKHR* create_infos = create_info;
+        while (create_infos->pNext) {
+            create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
+                create_infos->pNext);
+            switch (create_infos->sType) {
+                case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+                    const VkImageCompressionControlEXT* compression_infos =
+                        reinterpret_cast<const VkImageCompressionControlEXT*>(
+                            create_infos);
+                    image_compression = *compression_infos;
+                    image_compression.pNext = nullptr;
+                    usage_info_pNext = &image_compression;
+                } break;
+
+                default:
+                    // Ignore all other info structs
+                    break;
+            }
+        }
+        gralloc_usage_info.pNext = usage_info_pNext;
+
+        result = dispatch.GetSwapchainGrallocUsage3ANDROID(
+            device, &gralloc_usage_info, &native_usage);
+        ATRACE_END();
+        if (result != VK_SUCCESS) {
+            ALOGE("vkGetSwapchainGrallocUsage3ANDROID failed: %d", result);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+    } else if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
         uint64_t consumer_usage, producer_usage;
         ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID");
         result = dispatch.GetSwapchainGrallocUsage2ANDROID(
@@ -1383,7 +1472,7 @@
             return VK_ERROR_SURFACE_LOST_KHR;
         }
         native_usage =
-            convertGralloc1ToBufferUsage(consumer_usage, producer_usage);
+            convertGralloc1ToBufferUsage(producer_usage, consumer_usage);
     } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
         ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID");
         int32_t legacy_usage = 0;
@@ -1437,7 +1526,7 @@
 #pragma clang diagnostic ignored "-Wold-style-cast"
         .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID,
 #pragma clang diagnostic pop
-        .pNext = nullptr,
+        .pNext = usage_info_pNext,
         .usage = swapchain_image_usage,
     };
     VkNativeBufferANDROID image_native_buffer = {
@@ -1495,6 +1584,7 @@
         android_convertGralloc0To1Usage(int(img.buffer->usage),
             &image_native_buffer.usage2.producer,
             &image_native_buffer.usage2.consumer);
+        image_native_buffer.usage3 = img.buffer->usage;
 
         ATRACE_BEGIN("CreateImage");
         result =
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 3c91150..f998b1a 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -948,6 +948,17 @@
     return VK_SUCCESS;
 }
 
+VkResult GetSwapchainGrallocUsage3ANDROID(
+    VkDevice,
+    const VkGrallocUsageInfoANDROID* grallocUsageInfo,
+    uint64_t* grallocUsage) {
+    // The null driver never reads or writes the gralloc buffer
+    ALOGV("TODO: vk%s - grallocUsageInfo->format:%i", __FUNCTION__,
+          grallocUsageInfo->format);
+    *grallocUsage = 0;
+    return VK_SUCCESS;
+}
+
 VkResult AcquireImageANDROID(VkDevice,
                              VkImage,
                              int fence,
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index f6dcf09..0cb7bd3 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -261,6 +261,7 @@
     {"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))},
     {"vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSemaphoreCounterValue>(GetSemaphoreCounterValue))},
     {"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))},
+    {"vkGetSwapchainGrallocUsage3ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage3ANDROID>(GetSwapchainGrallocUsage3ANDROID))},
     {"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))},
     {"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))},
     {"vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory>(MapMemory))},
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 3e003e3..5c7fea0 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -209,6 +209,7 @@
 VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport);
 VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
 VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsage3ANDROID(VkDevice device, const VkGrallocUsageInfoANDROID* grallocUsageInfo, uint64_t* grallocUsage);
 VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
 VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
 VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index 4176509..c25c6cb 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -69,6 +69,7 @@
 _OPTIONAL_COMMANDS = [
     'vkGetSwapchainGrallocUsageANDROID',
     'vkGetSwapchainGrallocUsage2ANDROID',
+    'vkGetSwapchainGrallocUsage3ANDROID',
 ]
 
 # Dict for mapping dispatch table to a type.