Merge "Add new surface binding for ADPF Timeline API" into main
diff --git a/data/etc/android.hardware.vulkan.version-1_4.xml b/data/etc/android.hardware.vulkan.version-1_4.xml
new file mode 100644
index 0000000..010f6da
--- /dev/null
+++ b/data/etc/android.hardware.vulkan.version-1_4.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device has a Vulkan
+     driver that supports API version 1.4 (0x00404000) -->
+<permissions>
+    <feature name="android.hardware.vulkan.version" version="4210688" />
+</permissions>
diff --git a/include/android/display_luts.h b/include/android/display_luts.h
new file mode 100644
index 0000000..08dfb12
--- /dev/null
+++ b/include/android/display_luts.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+
+/**
+ * @file display_luts.h
+ */
+#pragma once
+
+#include <inttypes.h>
+
+__BEGIN_DECLS
+
+/**
+ * The dimension of the lut
+ */
+enum ADisplayLuts_Dimension : int32_t {
+    ADISPLAYLUTS_ONE_DIMENSION = 1,
+    ADISPLAYLUTS_THREE_DIMENSION = 3,
+};
+typedef enum ADisplayLuts_Dimension ADisplayLuts_Dimension;
+
+/**
+ * The sampling key used by the lut
+ */
+enum ADisplayLuts_SamplingKey : int32_t {
+    ADISPLAYLUTS_SAMPLINGKEY_RGB = 0,
+    ADISPLAYLUTS_SAMPLINGKEY_MAX_RGB = 1,
+};
+typedef enum ADisplayLuts_SamplingKey ADisplayLuts_SamplingKey;
+
+/**
+ * Used to get and set display luts entry
+ */
+typedef struct ADisplayLutsEntry ADisplayLutsEntry;
+
+/**
+ * Used to get and set display luts
+ */
+typedef struct ADisplayLuts ADisplayLuts;
+
+/**
+ * Creates a \a ADisplayLutsEntry entry.
+ *
+ * You are responsible for mamanging the memory of the returned object.
+ * Always call \a ADisplayLutsEntry_destroy to release it after use.
+ *
+ * Functions like \a ADisplayLuts_set create their own copies of entries,
+ * therefore they don't take the ownership of the instance created by
+ * \a ADisplayLutsEntry_create.
+ *
+ * @param buffer The lut raw buffer. The function creates a copy of it and does not need to
+ * outlive the life of the ADisplayLutsEntry.
+ * @param length The length of lut raw buffer
+ * @param dimension The dimension of the lut. see \a ADisplayLuts_Dimension
+ * @param key The sampling key used by the lut. see \a ADisplayLuts_SamplingKey
+ * @return a new \a ADisplayLutsEntry instance.
+ */
+ADisplayLutsEntry* _Nonnull ADisplayLutsEntry_createEntry(float* _Nonnull buffer,
+        int32_t length, int32_t dimension, int32_t key) __INTRODUCED_IN(36);
+
+/**
+ * Destroy the \a ADisplayLutsEntry instance.
+ *
+ * @param entry The entry to be destroyed
+ */
+void ADisplayLutsEntry_destroy(ADisplayLutsEntry* _Nullable entry) __INTRODUCED_IN(36);
+
+/**
+ * Gets the dimension of the entry.
+ *
+ * The function is only valid for the lifetime of the `entry`.
+ *
+ * @param entry The entry to query
+ * @return the dimension of the lut
+ */
+ADisplayLuts_Dimension ADisplayLutsEntry_getDimension(const ADisplayLutsEntry* _Nonnull entry)
+        __INTRODUCED_IN(36);
+
+/**
+ * Gets the size for each dimension of the entry.
+ *
+ * The function is only valid for the lifetime of the `entry`.
+ *
+ * @param entry The entry to query
+ * @return the size of each dimension of the lut
+ */
+int32_t ADisplayLutsEntry_getSize(const ADisplayLutsEntry* _Nonnull entry)
+        __INTRODUCED_IN(36);
+
+/**
+ * Gets the sampling key used by the entry.
+ *
+ * The function is only valid for the lifetime of the `entry`.
+ *
+ * @param entry The entry to query
+ * @return the sampling key used by the lut
+ */
+ADisplayLuts_SamplingKey ADisplayLutsEntry_getSamplingKey(const ADisplayLutsEntry* _Nonnull entry)
+        __INTRODUCED_IN(36);
+
+/**
+ * Gets the lut buffer of the entry.
+ *
+ * The function is only valid for the lifetime of the `entry`.
+ *
+ * @param entry The entry to query
+ * @return a pointer to the raw lut buffer
+ */
+const float* _Nonnull ADisplayLutsEntry_getBuffer(const ADisplayLutsEntry* _Nonnull entry)
+        __INTRODUCED_IN(36);
+
+/**
+ * Creates a \a ADisplayLuts instance.
+ *
+ * You are responsible for mamanging the memory of the returned object.
+ * Always call \a ADisplayLuts_destroy to release it after use. E.g., after calling
+ * the function \a ASurfaceTransaction_setLuts.
+ *
+ * @return a new \a ADisplayLuts instance
+ */
+ADisplayLuts* _Nonnull ADisplayLuts_create() __INTRODUCED_IN(36);
+
+/**
+ * Sets Luts in order to be applied.
+ *
+ * The function accepts a single 1D Lut, or a single 3D Lut or both 1D and 3D Lut in order.
+ * And the function will replace any previously set lut(s).
+ * If you want to clear the previously set lut(s), set `entries` to be nullptr,
+ * and `numEntries` will be internally ignored.
+ *
+ * @param luts the pointer of the \a ADisplayLuts instance
+ * @param entries the pointer of the array of lut entries to be applied
+ * @param numEntries the number of lut entries. The value should be either 1 or 2.
+ */
+void ADisplayLuts_setEntries(ADisplayLuts* _Nonnull luts,
+        ADisplayLutsEntry* _Nullable *_Nullable entries, int32_t numEntries) __INTRODUCED_IN(36);
+
+/**
+ * Deletes the \a ADisplayLuts instance.
+ *
+ * @param luts The luts to be destroyed
+ */
+void ADisplayLuts_destroy(ADisplayLuts* _Nullable luts) __INTRODUCED_IN(36);
+
+__END_DECLS
+
+/** @} */
\ No newline at end of file
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index fe38e86..9554015 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -28,6 +28,7 @@
 
 #include <sys/cdefs.h>
 
+#include <android/display_luts.h>
 #include <android/choreographer.h>
 #include <android/data_space.h>
 #include <android/hardware_buffer.h>
@@ -713,6 +714,23 @@
         __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
+ * Sets the Lut(s) to be applied for the layer.
+ *
+ * The function makes a deep copy of the provided `luts`.
+ * Any modifications made to the `luts` object after calling this function
+ * will not affect the Lut(s) applied to the layer.
+ *
+ * @param surface_control The layer where Lut(s) is being applied
+ * @param luts The Lut(s) to be applied
+ *
+ * Available since API level 36.
+ */
+void ASurfaceTransaction_setLuts(ASurfaceTransaction* _Nonnull transaction,
+                                 ASurfaceControl* _Nonnull surface_control,
+                                 const struct ADisplayLuts* _Nullable luts)
+        __INTRODUCED_IN(36);
+
+/**
  * Same as ASurfaceTransaction_setFrameRateWithChangeStrategy(transaction, surface_control,
  * frameRate, compatibility, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS).
  *
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
index 917d9a7..203623d 100644
--- a/include/audiomanager/AudioManager.h
+++ b/include/audiomanager/AudioManager.h
@@ -22,6 +22,7 @@
 // must be kept in sync with definitions in AudioPlaybackConfiguration.java
 #define PLAYER_PIID_INVALID -1
 
+// TODO (b/309532236) remove manual IAudioManager impl in favor of AIDL.
 typedef enum {
     PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE = 11,
     PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD = 12,
@@ -60,6 +61,7 @@
     PLAYER_MUTE_CLIENT_VOLUME = (1 << 4),
     PLAYER_MUTE_VOLUME_SHAPER = (1 << 5),
     PLAYER_MUTE_PORT_VOLUME = (1 << 6),
+    PLAYER_MUTE_OP_AUDIO_CONTROL = (1 << 7),
 };
 
 struct mute_state_t {
@@ -77,6 +79,8 @@
     bool muteFromVolumeShaper = false;
     /** Flag used when volume is muted by port volume. */
     bool muteFromPortVolume = false;
+    /** Flag used when volume is muted by audio control op. */
+    bool muteFromOpAudioControl = false;
 
     explicit operator int() const
     {
@@ -87,6 +91,7 @@
         result |= muteFromClientVolume * PLAYER_MUTE_CLIENT_VOLUME;
         result |= muteFromVolumeShaper * PLAYER_MUTE_VOLUME_SHAPER;
         result |= muteFromPortVolume * PLAYER_MUTE_PORT_VOLUME;
+        result |= muteFromOpAudioControl * PLAYER_MUTE_OP_AUDIO_CONTROL;
         return result;
     }
 
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 6b45dd3..ea1e4ae 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -111,12 +111,12 @@
 };
 
 enum class InputDeviceSensorAccuracy : int32_t {
-    ACCURACY_NONE = 0,
-    ACCURACY_LOW = 1,
-    ACCURACY_MEDIUM = 2,
-    ACCURACY_HIGH = 3,
+    NONE = 0,
+    LOW = 1,
+    MEDIUM = 2,
+    HIGH = 3,
 
-    ftl_last = ACCURACY_HIGH,
+    ftl_last = HIGH,
 };
 
 enum class InputDeviceSensorReportingMode : int32_t {
diff --git a/include/private/display_luts_private.h b/include/private/display_luts_private.h
new file mode 100644
index 0000000..240e1f9
--- /dev/null
+++ b/include/private/display_luts_private.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <vector>
+#include <utils/RefBase.h>
+
+using namespace android;
+
+__BEGIN_DECLS
+
+struct ADisplayLutsEntry_buffer {
+    std::vector<float> data;
+};
+
+struct ADisplayLutsEntry_properties {
+    int32_t dimension;
+    int32_t size;
+    int32_t samplingKey;
+};
+
+struct ADisplayLutsEntry: public RefBase {
+    struct ADisplayLutsEntry_buffer buffer;
+    struct ADisplayLutsEntry_properties properties;
+    ADisplayLutsEntry() {}
+
+    // copy constructor
+    ADisplayLutsEntry(const ADisplayLutsEntry& other) :
+        buffer(other.buffer),
+        properties(other.properties) {}
+
+    // copy operator
+    ADisplayLutsEntry& operator=(const ADisplayLutsEntry& other) {
+        if (this != &other) { // Protect against self-assignment
+            buffer = other.buffer;
+            properties = other.properties;
+        }
+        return *this;
+    }
+};
+
+struct ADisplayLuts: public RefBase {
+    int32_t totalBufferSize;
+    std::vector<int32_t> offsets;
+    std::vector<sp<ADisplayLutsEntry>> entries;
+
+    ADisplayLuts() : totalBufferSize(0) {}
+};
+
+__END_DECLS
\ No newline at end of file
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 96d821e..a5f416f 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -2223,9 +2223,7 @@
         const char* eos = reinterpret_cast<const char*>(memchr(str, 0, avail));
         if (eos) {
             const size_t len = eos - str;
-            mDataPos += pad_size(len+1);
-            ALOGV("readCString Setting data pos of %p to %zu", this, mDataPos);
-            return str;
+            return static_cast<const char*>(readInplace(len + 1));
         }
     }
     return nullptr;
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index ceab20a..0c7366e 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -178,7 +178,8 @@
     LIBBINDER_EXPORTED status_t writeUint64(uint64_t val);
     LIBBINDER_EXPORTED status_t writeFloat(float val);
     LIBBINDER_EXPORTED status_t writeDouble(double val);
-    LIBBINDER_EXPORTED status_t writeCString(const char* str);
+    LIBBINDER_EXPORTED status_t writeCString(const char* str)
+            __attribute__((deprecated("use AIDL, writeString* instead")));
     LIBBINDER_EXPORTED status_t writeString8(const String8& str);
     LIBBINDER_EXPORTED status_t writeString8(const char* str, size_t len);
     LIBBINDER_EXPORTED status_t writeString16(const String16& str);
@@ -434,7 +435,8 @@
     LIBBINDER_EXPORTED status_t readUtf8FromUtf16(std::unique_ptr<std::string>* str) const
             __attribute__((deprecated("use std::optional version instead")));
 
-    LIBBINDER_EXPORTED const char* readCString() const;
+    LIBBINDER_EXPORTED const char* readCString() const
+            __attribute__((deprecated("use AIDL, use readString*")));
     LIBBINDER_EXPORTED String8 readString8() const;
     LIBBINDER_EXPORTED status_t readString8(String8* pArg) const;
     LIBBINDER_EXPORTED const char* readString8Inplace(size_t* outLen) const;
diff --git a/libs/binder/rust/src/system_only.rs b/libs/binder/rust/src/system_only.rs
index 3da59ab..1a58d6b 100644
--- a/libs/binder/rust/src/system_only.rs
+++ b/libs/binder/rust/src/system_only.rs
@@ -91,6 +91,32 @@
         Accessor { accessor }
     }
 
+    /// Creates a new Accessor instance based on an existing Accessor's binder.
+    /// This is useful when the Accessor instance is hosted in another process
+    /// that has the permissions to create the socket connection FD.
+    ///
+    /// The `instance` argument must match the instance that the original Accessor
+    /// is responsible for.
+    /// `instance` must not contain null bytes and is used to create a CString to
+    /// pass through FFI.
+    /// The `binder` argument must be a valid binder from an Accessor
+    pub fn from_binder(instance: &str, binder: SpIBinder) -> Option<Accessor> {
+        let inst = CString::new(instance).unwrap();
+
+        // Safety: All `SpIBinder` objects (the `binder` argument) hold a valid pointer
+        // to an `AIBinder` that is guaranteed to remain valid for the lifetime of the
+        // SpIBinder. `ABinderRpc_Accessor_fromBinder` creates a new pointer to that binder
+        // that it is responsible for.
+        // The `inst` argument is a new CString that will copied by
+        // `ABinderRpc_Accessor_fromBinder` and not modified.
+        let accessor =
+            unsafe { sys::ABinderRpc_Accessor_fromBinder(inst.as_ptr(), binder.as_raw()) };
+        if accessor.is_null() {
+            return None;
+        }
+        Some(Accessor { accessor })
+    }
+
     /// Get the underlying binder for this Accessor for when it needs to be either
     /// registered with service manager or sent to another process.
     pub fn as_binder(&self) -> Option<SpIBinder> {
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 0e793e5..da4f128 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -1038,6 +1038,34 @@
         assert!(deleted.load(Ordering::Relaxed));
     }
 
+    #[test]
+    fn test_accessor_from_accessor_binder() {
+        let get_connection_info = move |_instance: &str| None;
+        let accessor = Accessor::new("foo.service", get_connection_info);
+        let accessor2 =
+            Accessor::from_binder("foo.service", accessor.as_binder().unwrap()).unwrap();
+        assert_eq!(accessor.as_binder(), accessor2.as_binder());
+    }
+
+    #[test]
+    fn test_accessor_from_non_accessor_binder() {
+        let service_name = "rust_test_ibinder";
+        let _process = ScopedServiceProcess::new(service_name);
+        let binder = binder::get_service(service_name).unwrap();
+        assert!(binder.is_binder_alive());
+
+        let accessor = Accessor::from_binder("rust_test_ibinder", binder);
+        assert!(accessor.is_none());
+    }
+
+    #[test]
+    fn test_accessor_from_wrong_accessor_binder() {
+        let get_connection_info = move |_instance: &str| None;
+        let accessor = Accessor::new("foo.service", get_connection_info);
+        let accessor2 = Accessor::from_binder("NOT.foo.service", accessor.as_binder().unwrap());
+        assert!(accessor2.is_none());
+    }
+
     #[tokio::test]
     async fn reassociate_rust_binder_async() {
         let service_name = "testing_service";
diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
index 32a70e5..6259d9d 100644
--- a/libs/binder/tests/binderParcelUnitTest.cpp
+++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -33,6 +33,38 @@
 using android::binder::Status;
 using android::binder::unique_fd;
 
+static void checkCString(const char* str) {
+    for (size_t i = 0; i < 3; i++) {
+        Parcel p;
+
+        for (size_t j = 0; j < i; j++) p.writeInt32(3);
+
+        p.writeCString(str);
+        int32_t pos = p.dataPosition();
+
+        p.setDataPosition(0);
+
+        for (size_t j = 0; j < i; j++) p.readInt32();
+        const char* str2 = p.readCString();
+
+        ASSERT_EQ(std::string(str), str2);
+        ASSERT_EQ(pos, p.dataPosition());
+    }
+}
+
+TEST(Parcel, TestReadCString) {
+    // we should remove the *CString APIs, but testing them until
+    // they are deleted.
+    checkCString("");
+    checkCString("a");
+    checkCString("\n");
+    checkCString("32");
+    checkCString("321");
+    checkCString("3210");
+    checkCString("3210b");
+    checkCString("123434");
+}
+
 TEST(Parcel, NonNullTerminatedString8) {
     String8 kTestString = String8("test-is-good");
 
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 1e33abb..052b519 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -264,6 +264,7 @@
         "DisplayEventDispatcher.cpp",
         "DisplayEventReceiver.cpp",
         "FenceMonitor.cpp",
+        "Flags.cpp",
         "GLConsumer.cpp",
         "IConsumerListener.cpp",
         "IGraphicBufferConsumer.cpp",
diff --git a/libs/gui/Flags.cpp b/libs/gui/Flags.cpp
new file mode 100644
index 0000000..85ee2cd
--- /dev/null
+++ b/libs/gui/Flags.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/Flags.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <gui/view/Surface.h>
+
+namespace android {
+namespace flagtools {
+sp<SurfaceType> surfaceToSurfaceType(const sp<Surface>& surface) {
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+    return surface;
+#else
+    return surface->getIGraphicBufferProducer();
+#endif
+}
+
+sp<IGraphicBufferProducer> surfaceTypeToIGBP(const sp<SurfaceType>& surface) {
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+    return surface->getIGraphicBufferProducer();
+#else
+    return surface;
+#endif
+}
+
+bool isSurfaceTypeValid(const sp<SurfaceType>& surface) {
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+    return Surface::isValid(surface);
+#else
+    return surface != nullptr;
+#endif
+}
+
+ParcelableSurfaceType toParcelableSurfaceType(const view::Surface& surface) {
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+    return surface;
+#else
+    return surface.graphicBufferProducer;
+#endif
+}
+
+ParcelableSurfaceType convertSurfaceTypeToParcelable(sp<SurfaceType> surface) {
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+    return view::Surface::fromSurface(surface);
+#else
+    return surface;
+#endif
+}
+
+sp<SurfaceType> convertParcelableSurfaceTypeToSurface(const ParcelableSurfaceType& surface) {
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+    return surface.toSurface();
+#else
+    return surface;
+#endif
+}
+
+} // namespace flagtools
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/include/gui/Flags.h b/libs/gui/include/gui/Flags.h
index 34350d2..845bc54 100644
--- a/libs/gui/include/gui/Flags.h
+++ b/libs/gui/include/gui/Flags.h
@@ -17,8 +17,15 @@
 #pragma once
 
 #include <com_android_graphics_libgui_flags.h>
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/Surface.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class IGraphicBufferProducer;
+class Surface;
+namespace view {
+class Surface;
+}
 
 #define WB_CAMERA3_AND_PROCESSORS_WITH_DEPENDENCIES                  \
     (COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CAMERA3_AND_PROCESSORS) && \
@@ -31,6 +38,19 @@
 
 #if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
 typedef android::Surface SurfaceType;
+typedef android::view::Surface ParcelableSurfaceType;
 #else
 typedef android::IGraphicBufferProducer SurfaceType;
-#endif
\ No newline at end of file
+typedef android::sp<android::IGraphicBufferProducer> ParcelableSurfaceType;
+#endif
+
+namespace flagtools {
+sp<SurfaceType> surfaceToSurfaceType(const sp<Surface>& surface);
+ParcelableSurfaceType toParcelableSurfaceType(const view::Surface& surface);
+sp<IGraphicBufferProducer> surfaceTypeToIGBP(const sp<SurfaceType>& surface);
+bool isSurfaceTypeValid(const sp<SurfaceType>& surface);
+ParcelableSurfaceType convertSurfaceTypeToParcelable(sp<SurfaceType> surface);
+sp<SurfaceType> convertParcelableSurfaceTypeToSurface(const ParcelableSurfaceType& surface);
+} // namespace flagtools
+
+} // namespace android
diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h
index 7c762d3..bd8704d 100644
--- a/libs/gui/include/gui/view/Surface.h
+++ b/libs/gui/include/gui/view/Surface.h
@@ -54,6 +54,22 @@
     sp<android::Surface> toSurface() const;
 
     status_t getUniqueId(/* out */ uint64_t* id) const;
+
+    bool isEmpty() const;
+
+    bool operator==(const Surface& other) const {
+        return graphicBufferProducer == other.graphicBufferProducer;
+    }
+    bool operator!=(const Surface& other) const { return !(*this == other); }
+    bool operator==(const sp<android::Surface> other) const {
+        if (other == nullptr) return graphicBufferProducer == nullptr;
+        return graphicBufferProducer == other->getIGraphicBufferProducer();
+    }
+    bool operator!=(const sp<android::Surface> other) const { return !(*this == other); }
+    bool operator<(const Surface& other) const {
+        return graphicBufferProducer < other.graphicBufferProducer;
+    }
+    bool operator>(const Surface& other) const { return other < *this; }
 #endif
 
     virtual status_t writeToParcel(Parcel* parcel) const override;
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 9f57923..2cf7081 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -151,6 +151,10 @@
     }
     return OK;
 }
+
+bool Surface::isEmpty() const {
+    return graphicBufferProducer == nullptr;
+}
 #endif
 
 std::string Surface::toString() const {
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index 2c0f77a..cd85821 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -169,6 +169,12 @@
     msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = presentTime;
     return msg;
 }
+
+std::ostream& operator<<(std::ostream& out, const InputMessage& msg) {
+    out << ftl::enum_string(msg.header.type);
+    return out;
+}
+
 } // namespace
 
 // --- InputConsumerNoResampling ---
@@ -272,6 +278,15 @@
             return; // try again later
         }
 
+        if (result == DEAD_OBJECT) {
+            // If there's no one to receive events in the channel, there's no point in sending them.
+            // Drop all outbound events.
+            LOG(INFO) << "Channel " << mChannel->getName() << " died. Dropping outbound event "
+                      << outboundMsg;
+            mOutboundQueue.pop();
+            setFdEvents(0);
+            continue;
+        }
         // Some other error. Give up
         LOG(FATAL) << "Failed to send outbound event on channel '" << mChannel->getName()
                    << "'.  status=" << statusToString(result) << "(" << result << ")";
diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
index 39bb841..1dadae9 100644
--- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
@@ -319,6 +319,8 @@
 
 protected:
     // Interaction with the looper thread
+    void blockLooper();
+    void unblockLooper();
     enum class LooperMessage : int {
         CALL_PROBABLY_HAS_INPUT,
         CREATE_CONSUMER,
@@ -389,6 +391,26 @@
     };
 };
 
+void InputPublisherAndConsumerNoResamplingTest::blockLooper() {
+    {
+        std::scoped_lock l(mLock);
+        mLooperMayProceed = false;
+    }
+    sendMessage(LooperMessage::BLOCK_LOOPER);
+    {
+        std::unique_lock l(mLock);
+        mNotifyLooperWaiting.wait(l, [this] { return mLooperIsBlocked; });
+    }
+}
+
+void InputPublisherAndConsumerNoResamplingTest::unblockLooper() {
+    {
+        std::scoped_lock l(mLock);
+        mLooperMayProceed = true;
+    }
+    mNotifyLooperMayProceed.notify_all();
+}
+
 void InputPublisherAndConsumerNoResamplingTest::sendMessage(LooperMessage message) {
     Message msg{ftl::to_underlying(message)};
     mLooper->sendMessage(mMessageHandler, msg);
@@ -600,15 +622,7 @@
     std::queue<uint32_t> publishedSequenceNumbers;
 
     // Block Looper to increase the chance of batching events
-    {
-        std::scoped_lock l(mLock);
-        mLooperMayProceed = false;
-    }
-    sendMessage(LooperMessage::BLOCK_LOOPER);
-    {
-        std::unique_lock l(mLock);
-        mNotifyLooperWaiting.wait(l, [this] { return mLooperIsBlocked; });
-    }
+    blockLooper();
 
     uint32_t firstSampleId;
     for (size_t i = 0; i < nSamples; ++i) {
@@ -629,12 +643,7 @@
 
     std::vector<MotionEvent> singleSampledMotionEvents;
 
-    // Unblock Looper
-    {
-        std::scoped_lock l(mLock);
-        mLooperMayProceed = true;
-    }
-    mNotifyLooperMayProceed.notify_all();
+    unblockLooper();
 
     // We have no control over the socket behavior, so the consumer can receive
     // the motion as a batched event, or as a sequence of multiple single-sample MotionEvents (or a
@@ -809,6 +818,15 @@
     verifyFinishedSignal(*mPublisher, seq, publishTime);
 }
 
+/**
+ * If the publisher has died, consumer should not crash when trying to send an outgoing message.
+ */
+TEST_F(InputPublisherAndConsumerNoResamplingTest, ConsumerWritesAfterPublisherDies) {
+    mPublisher.reset(); // The publisher has died
+    mReportTimelineArgs.emplace(/*inputEventId=*/10, /*gpuCompletedTime=*/20, /*presentTime=*/30);
+    sendMessage(LooperMessage::CALL_REPORT_TIMELINE);
+}
+
 TEST_F(InputPublisherAndConsumerNoResamplingTest, SendTimeline) {
     const int32_t inputEventId = 20;
     const nsecs_t gpuCompletedTime = 30;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 0fd982e..95c4d03 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -38,6 +38,14 @@
 #define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend"
 
 /**
+ * Allows opting particular devices into an initial preview rollout of RenderEngine on Graphite.
+ *
+ * Only applicable within SurfaceFlinger, and if relevant aconfig flags are enabled.
+ */
+#define PROPERTY_DEBUG_RENDERENGINE_GRAPHITE_PREVIEW_OPTIN \
+    "debug.renderengine.graphite_preview_optin"
+
+/**
  * Turns on recording of skia commands in SkiaGL version of the RE. This property
  * defines number of milliseconds for the recording to take place. A non zero value
  * turns on the recording.
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 006d507..38e5974 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -139,23 +139,24 @@
         mShowTouchesEnabled(false),
         mStylusPointerIconEnabled(false),
         mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT),
+        mIsWindowInfoListenerRegistered(false),
+        mWindowInfoListener(sp<PointerChoreographerDisplayInfoListener>::make(this)),
         mRegisterListener(registerListener),
         mUnregisterListener(unregisterListener) {}
 
 PointerChoreographer::~PointerChoreographer() {
-    std::scoped_lock _l(mLock);
-    if (mWindowInfoListener == nullptr) {
-        return;
+    if (mIsWindowInfoListenerRegistered) {
+        mUnregisterListener(mWindowInfoListener);
+        mIsWindowInfoListenerRegistered = false;
     }
     mWindowInfoListener->onPointerChoreographerDestroyed();
-    mUnregisterListener(mWindowInfoListener);
 }
 
 void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
     PointerDisplayChange pointerDisplayChange;
 
     { // acquire lock
-        std::scoped_lock _l(mLock);
+        std::scoped_lock _l(getLock());
 
         mInputDeviceInfos = args.inputDeviceInfos;
         pointerDisplayChange = updatePointerControllersLocked();
@@ -191,7 +192,7 @@
         return;
     }
 
-    std::scoped_lock _l(mLock);
+    std::scoped_lock _l(getLock());
     ui::LogicalDisplayId targetDisplay = args.displayId;
     if (targetDisplay == ui::LogicalDisplayId::INVALID) {
         targetDisplay = mCurrentFocusedDisplay;
@@ -204,7 +205,7 @@
 }
 
 NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
-    std::scoped_lock _l(mLock);
+    std::scoped_lock _l(getLock());
 
     if (isFromMouse(args)) {
         return processMouseEventLocked(args);
@@ -242,14 +243,7 @@
         pc.setPosition(args.xCursorPosition, args.yCursorPosition);
     } else {
         // This is a relative mouse, so move the cursor by the specified amount.
-        const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
-        const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
-        pc.move(deltaX, deltaY);
-        const auto [x, y] = pc.getPosition();
-        newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
-        newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-        newArgs.xCursorPosition = x;
-        newArgs.yCursorPosition = y;
+        processPointerDeviceMotionEventLocked(/*byref*/ newArgs, /*byref*/ pc);
     }
     if (canUnfadeOnDisplay(displayId)) {
         pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
@@ -265,24 +259,9 @@
     newArgs.displayId = displayId;
     if (args.getPointerCount() == 1 && args.classification == MotionClassification::NONE) {
         // This is a movement of the mouse pointer.
-        const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
-        const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
-        pc.move(deltaX, deltaY);
-        if (canUnfadeOnDisplay(displayId)) {
-            pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
-        }
-
-        const auto [x, y] = pc.getPosition();
-        newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
-        newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-        newArgs.xCursorPosition = x;
-        newArgs.yCursorPosition = y;
+        processPointerDeviceMotionEventLocked(/*byref*/ newArgs, /*byref*/ pc);
     } else {
         // This is a trackpad gesture with fake finger(s) that should not move the mouse pointer.
-        if (canUnfadeOnDisplay(displayId)) {
-            pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
-        }
-
         const auto [x, y] = pc.getPosition();
         for (uint32_t i = 0; i < newArgs.getPointerCount(); i++) {
             newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X,
@@ -293,9 +272,25 @@
         newArgs.xCursorPosition = x;
         newArgs.yCursorPosition = y;
     }
+    if (canUnfadeOnDisplay(displayId)) {
+        pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+    }
     return newArgs;
 }
 
+void PointerChoreographer::processPointerDeviceMotionEventLocked(NotifyMotionArgs& newArgs,
+                                                                 PointerControllerInterface& pc) {
+    const float deltaX = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+    const float deltaY = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+
+    pc.move(deltaX, deltaY);
+    const auto [x, y] = pc.getPosition();
+    newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+    newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+    newArgs.xCursorPosition = x;
+    newArgs.yCursorPosition = y;
+}
+
 void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
     if (args.displayId == ui::LogicalDisplayId::INVALID) {
         return;
@@ -433,7 +428,7 @@
 }
 
 void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) {
-    std::scoped_lock _l(mLock);
+    std::scoped_lock _l(getLock());
     mTouchPointersByDevice.erase(args.deviceId);
     mStylusPointersByDevice.erase(args.deviceId);
     mDrawingTabletPointersByDevice.erase(args.deviceId);
@@ -447,17 +442,22 @@
     bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
             !mDrawingTabletPointersByDevice.empty() || !mStylusPointersByDevice.empty();
 
-    if (requireListener && mWindowInfoListener == nullptr) {
-        mWindowInfoListener = sp<PointerChoreographerDisplayInfoListener>::make(this);
-        mWindowInfoListener->setInitialDisplayInfos(mRegisterListener(mWindowInfoListener));
-        onPrivacySensitiveDisplaysChangedLocked(mWindowInfoListener->getPrivacySensitiveDisplays());
-    } else if (!requireListener && mWindowInfoListener != nullptr) {
+    // PointerChoreographer uses Listener's lock which is already held by caller
+    base::ScopedLockAssertion assumeLocked(mWindowInfoListener->mLock);
+
+    if (requireListener && !mIsWindowInfoListenerRegistered) {
+        mIsWindowInfoListenerRegistered = true;
+        mWindowInfoListener->setInitialDisplayInfosLocked(mRegisterListener(mWindowInfoListener));
+        onPrivacySensitiveDisplaysChangedLocked(
+                mWindowInfoListener->getPrivacySensitiveDisplaysLocked());
+    } else if (!requireListener && mIsWindowInfoListenerRegistered) {
+        mIsWindowInfoListenerRegistered = false;
         mUnregisterListener(mWindowInfoListener);
-        mWindowInfoListener = nullptr;
-    } else if (requireListener && mWindowInfoListener != nullptr) {
+    } else if (requireListener) {
         // controller may have been added to an existing privacy sensitive display, we need to
         // update all controllers again
-        onPrivacySensitiveDisplaysChangedLocked(mWindowInfoListener->getPrivacySensitiveDisplays());
+        onPrivacySensitiveDisplaysChangedLocked(
+                mWindowInfoListener->getPrivacySensitiveDisplaysLocked());
     }
 }
 
@@ -494,7 +494,7 @@
 void PointerChoreographer::notifyPointerCaptureChanged(
         const NotifyPointerCaptureChangedArgs& args) {
     if (args.request.isEnable()) {
-        std::scoped_lock _l(mLock);
+        std::scoped_lock _l(getLock());
         for (const auto& [_, mousePointerController] : mMousePointersByDisplay) {
             mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
         }
@@ -502,14 +502,8 @@
     mNextListener.notify(args);
 }
 
-void PointerChoreographer::onPrivacySensitiveDisplaysChanged(
-        const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays) {
-    std::scoped_lock _l(mLock);
-    onPrivacySensitiveDisplaysChangedLocked(privacySensitiveDisplays);
-}
-
 void PointerChoreographer::dump(std::string& dump) {
-    std::scoped_lock _l(mLock);
+    std::scoped_lock _l(getLock());
 
     dump += "PointerChoreographer:\n";
     dump += StringPrintf(INDENT "Show Touches Enabled: %s\n",
@@ -579,6 +573,10 @@
     return mDisplaysWithPointersHidden.find(displayId) == mDisplaysWithPointersHidden.end();
 }
 
+std::mutex& PointerChoreographer::getLock() const {
+    return mWindowInfoListener->mLock;
+}
+
 PointerChoreographer::PointerDisplayChange PointerChoreographer::updatePointerControllersLocked() {
     std::set<ui::LogicalDisplayId /*displayId*/> mouseDisplaysToKeep;
     std::set<DeviceId> touchDevicesToKeep;
@@ -641,7 +639,7 @@
     std::erase_if(mDrawingTabletPointersByDevice, [&drawingTabletDevicesToKeep](const auto& pair) {
         return drawingTabletDevicesToKeep.find(pair.first) == drawingTabletDevicesToKeep.end();
     });
-    std::erase_if(mMouseDevices, [&](DeviceId id) REQUIRES(mLock) {
+    std::erase_if(mMouseDevices, [&](DeviceId id) REQUIRES(getLock()) {
         return std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
                             [id](const auto& info) { return info.getId() == id; }) ==
                 mInputDeviceInfos.end();
@@ -677,7 +675,7 @@
     PointerDisplayChange pointerDisplayChange;
 
     { // acquire lock
-        std::scoped_lock _l(mLock);
+        std::scoped_lock _l(getLock());
 
         mDefaultMouseDisplayId = displayId;
         pointerDisplayChange = updatePointerControllersLocked();
@@ -690,7 +688,7 @@
     PointerDisplayChange pointerDisplayChange;
 
     { // acquire lock
-        std::scoped_lock _l(mLock);
+        std::scoped_lock _l(getLock());
         for (const auto& viewport : viewports) {
             const ui::LogicalDisplayId displayId = viewport.displayId;
             if (const auto it = mMousePointersByDisplay.find(displayId);
@@ -719,7 +717,7 @@
 
 std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
         ui::LogicalDisplayId associatedDisplayId) {
-    std::scoped_lock _l(mLock);
+    std::scoped_lock _l(getLock());
     const ui::LogicalDisplayId resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId);
     if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) {
         return *viewport;
@@ -728,7 +726,7 @@
 }
 
 FloatPoint PointerChoreographer::getMouseCursorPosition(ui::LogicalDisplayId displayId) {
-    std::scoped_lock _l(mLock);
+    std::scoped_lock _l(getLock());
     const ui::LogicalDisplayId resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
     if (auto it = mMousePointersByDisplay.find(resolvedDisplayId);
         it != mMousePointersByDisplay.end()) {
@@ -741,7 +739,7 @@
     PointerDisplayChange pointerDisplayChange;
 
     { // acquire lock
-        std::scoped_lock _l(mLock);
+        std::scoped_lock _l(getLock());
         if (mShowTouchesEnabled == enabled) {
             return;
         }
@@ -756,7 +754,7 @@
     PointerDisplayChange pointerDisplayChange;
 
     { // acquire lock
-        std::scoped_lock _l(mLock);
+        std::scoped_lock _l(getLock());
         if (mStylusPointerIconEnabled == enabled) {
             return;
         }
@@ -770,7 +768,7 @@
 bool PointerChoreographer::setPointerIcon(
         std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
         ui::LogicalDisplayId displayId, DeviceId deviceId) {
-    std::scoped_lock _l(mLock);
+    std::scoped_lock _l(getLock());
     if (deviceId < 0) {
         LOG(WARNING) << "Invalid device id " << deviceId << ". Cannot set pointer icon.";
         return false;
@@ -821,7 +819,7 @@
 }
 
 void PointerChoreographer::setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     if (visible) {
         mDisplaysWithPointersHidden.erase(displayId);
         // We do not unfade the icons here, because we don't know when the last event happened.
@@ -843,14 +841,14 @@
 }
 
 void PointerChoreographer::setFocusedDisplay(ui::LogicalDisplayId displayId) {
-    std::scoped_lock lock(mLock);
+    std::scoped_lock lock(getLock());
     mCurrentFocusedDisplay = displayId;
 }
 
 PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
         ui::LogicalDisplayId displayId) {
     std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
-            [this, displayId]() REQUIRES(mLock) {
+            [this, displayId]() REQUIRES(getLock()) {
                 auto pc = mPolicy.createPointerController(
                         PointerControllerInterface::ControllerType::MOUSE);
                 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
@@ -864,7 +862,7 @@
 PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusControllerConstructor(
         ui::LogicalDisplayId displayId) {
     std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
-            [this, displayId]() REQUIRES(mLock) {
+            [this, displayId]() REQUIRES(getLock()) {
                 auto pc = mPolicy.createPointerController(
                         PointerControllerInterface::ControllerType::STYLUS);
                 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
@@ -875,9 +873,11 @@
     return ConstructorDelegate(std::move(ctor));
 }
 
+// --- PointerChoreographer::PointerChoreographerDisplayInfoListener ---
+
 void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(
         const gui::WindowInfosUpdate& windowInfosUpdate) {
-    std::scoped_lock _l(mListenerLock);
+    std::scoped_lock _l(mLock);
     if (mPointerChoreographer == nullptr) {
         return;
     }
@@ -885,25 +885,25 @@
             getPrivacySensitiveDisplaysFromWindowInfos(windowInfosUpdate.windowInfos);
     if (newPrivacySensitiveDisplays != mPrivacySensitiveDisplays) {
         mPrivacySensitiveDisplays = std::move(newPrivacySensitiveDisplays);
-        mPointerChoreographer->onPrivacySensitiveDisplaysChanged(mPrivacySensitiveDisplays);
+        // PointerChoreographer uses Listener's lock.
+        base::ScopedLockAssertion assumeLocked(mPointerChoreographer->getLock());
+        mPointerChoreographer->onPrivacySensitiveDisplaysChangedLocked(mPrivacySensitiveDisplays);
     }
 }
 
-void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfos(
+void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfosLocked(
         const std::vector<gui::WindowInfo>& windowInfos) {
-    std::scoped_lock _l(mListenerLock);
     mPrivacySensitiveDisplays = getPrivacySensitiveDisplaysFromWindowInfos(windowInfos);
 }
 
 std::unordered_set<ui::LogicalDisplayId /*displayId*/>
-PointerChoreographer::PointerChoreographerDisplayInfoListener::getPrivacySensitiveDisplays() {
-    std::scoped_lock _l(mListenerLock);
+PointerChoreographer::PointerChoreographerDisplayInfoListener::getPrivacySensitiveDisplaysLocked() {
     return mPrivacySensitiveDisplays;
 }
 
 void PointerChoreographer::PointerChoreographerDisplayInfoListener::
         onPointerChoreographerDestroyed() {
-    std::scoped_lock _l(mListenerLock);
+    std::scoped_lock _l(mLock);
     mPointerChoreographer = nullptr;
 }
 
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 635487b..fba1aef 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -118,31 +118,40 @@
 private:
     using PointerDisplayChange = std::optional<
             std::tuple<ui::LogicalDisplayId /*displayId*/, FloatPoint /*cursorPosition*/>>;
-    [[nodiscard]] PointerDisplayChange updatePointerControllersLocked() REQUIRES(mLock);
-    [[nodiscard]] PointerDisplayChange calculatePointerDisplayChangeToNotify() REQUIRES(mLock);
+
+    // PointerChoreographer's DisplayInfoListener can outlive the PointerChoreographer because when
+    // the listener is registered and called from display thread, a strong pointer to the listener
+    // (which can extend its lifecycle) is given away.
+    // If we use two locks it can also cause deadlocks due to race in acquiring them between the
+    // display and reader thread.
+    // To avoid these problems we use DisplayInfoListener's lock in PointerChoreographer.
+    std::mutex& getLock() const;
+
+    [[nodiscard]] PointerDisplayChange updatePointerControllersLocked() REQUIRES(getLock());
+    [[nodiscard]] PointerDisplayChange calculatePointerDisplayChangeToNotify() REQUIRES(getLock());
     const DisplayViewport* findViewportByIdLocked(ui::LogicalDisplayId displayId) const
-            REQUIRES(mLock);
+            REQUIRES(getLock());
     ui::LogicalDisplayId getTargetMouseDisplayLocked(ui::LogicalDisplayId associatedDisplayId) const
-            REQUIRES(mLock);
+            REQUIRES(getLock());
     std::pair<ui::LogicalDisplayId /*displayId*/, PointerControllerInterface&>
-    ensureMouseControllerLocked(ui::LogicalDisplayId associatedDisplayId) REQUIRES(mLock);
-    InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock);
-    bool canUnfadeOnDisplay(ui::LogicalDisplayId displayId) REQUIRES(mLock);
+    ensureMouseControllerLocked(ui::LogicalDisplayId associatedDisplayId) REQUIRES(getLock());
+    InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(getLock());
+    bool canUnfadeOnDisplay(ui::LogicalDisplayId displayId) REQUIRES(getLock());
 
     void fadeMouseCursorOnKeyPress(const NotifyKeyArgs& args);
     NotifyMotionArgs processMotion(const NotifyMotionArgs& args);
-    NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
-    NotifyMotionArgs processTouchpadEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
-    void processDrawingTabletEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
-    void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
-    void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+    NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(getLock());
+    NotifyMotionArgs processTouchpadEventLocked(const NotifyMotionArgs& args) REQUIRES(getLock());
+    void processDrawingTabletEventLocked(const NotifyMotionArgs& args) REQUIRES(getLock());
+    void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(getLock());
+    void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(getLock());
+    void processPointerDeviceMotionEventLocked(NotifyMotionArgs& newArgs,
+                                               PointerControllerInterface& pc) REQUIRES(getLock());
     void processDeviceReset(const NotifyDeviceResetArgs& args);
-    void onControllerAddedOrRemovedLocked() REQUIRES(mLock);
+    void onControllerAddedOrRemovedLocked() REQUIRES(getLock());
     void onPrivacySensitiveDisplaysChangedLocked(
             const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays)
-            REQUIRES(mLock);
-    void onPrivacySensitiveDisplaysChanged(
-            const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays);
+            REQUIRES(getLock());
 
     /* This listener keeps tracks of visible privacy sensitive displays and updates the
      * choreographer if there are any changes.
@@ -156,49 +165,50 @@
         explicit PointerChoreographerDisplayInfoListener(PointerChoreographer* pc)
               : mPointerChoreographer(pc){};
         void onWindowInfosChanged(const gui::WindowInfosUpdate&) override;
-        void setInitialDisplayInfos(const std::vector<gui::WindowInfo>& windowInfos);
-        std::unordered_set<ui::LogicalDisplayId /*displayId*/> getPrivacySensitiveDisplays();
+        void setInitialDisplayInfosLocked(const std::vector<gui::WindowInfo>& windowInfos)
+                REQUIRES(mLock);
+        std::unordered_set<ui::LogicalDisplayId> getPrivacySensitiveDisplaysLocked()
+                REQUIRES(mLock);
         void onPointerChoreographerDestroyed();
 
+        // This lock is also used by PointerChoreographer. See PointerChoreographer::getLock().
+        std::mutex mLock;
+
     private:
-        std::mutex mListenerLock;
-        PointerChoreographer* mPointerChoreographer GUARDED_BY(mListenerLock);
+        PointerChoreographer* mPointerChoreographer GUARDED_BY(mLock);
         std::unordered_set<ui::LogicalDisplayId /*displayId*/> mPrivacySensitiveDisplays
-                GUARDED_BY(mListenerLock);
+                GUARDED_BY(mLock);
     };
-    sp<PointerChoreographerDisplayInfoListener> mWindowInfoListener GUARDED_BY(mLock);
 
     using ControllerConstructor =
             ConstructorDelegate<std::function<std::shared_ptr<PointerControllerInterface>()>>;
-    ControllerConstructor mTouchControllerConstructor GUARDED_BY(mLock);
+    ControllerConstructor mTouchControllerConstructor GUARDED_BY(getLock());
     ControllerConstructor getMouseControllerConstructor(ui::LogicalDisplayId displayId)
-            REQUIRES(mLock);
+            REQUIRES(getLock());
     ControllerConstructor getStylusControllerConstructor(ui::LogicalDisplayId displayId)
-            REQUIRES(mLock);
-
-    std::mutex mLock;
+            REQUIRES(getLock());
 
     InputListenerInterface& mNextListener;
     PointerChoreographerPolicyInterface& mPolicy;
 
     std::map<ui::LogicalDisplayId, std::shared_ptr<PointerControllerInterface>>
-            mMousePointersByDisplay GUARDED_BY(mLock);
+            mMousePointersByDisplay GUARDED_BY(getLock());
     std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mTouchPointersByDevice
-            GUARDED_BY(mLock);
+            GUARDED_BY(getLock());
     std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mStylusPointersByDevice
-            GUARDED_BY(mLock);
+            GUARDED_BY(getLock());
     std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mDrawingTabletPointersByDevice
-            GUARDED_BY(mLock);
+            GUARDED_BY(getLock());
 
-    ui::LogicalDisplayId mDefaultMouseDisplayId GUARDED_BY(mLock);
-    ui::LogicalDisplayId mNotifiedPointerDisplayId GUARDED_BY(mLock);
-    std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(mLock);
-    std::set<DeviceId> mMouseDevices GUARDED_BY(mLock);
-    std::vector<DisplayViewport> mViewports GUARDED_BY(mLock);
-    bool mShowTouchesEnabled GUARDED_BY(mLock);
-    bool mStylusPointerIconEnabled GUARDED_BY(mLock);
+    ui::LogicalDisplayId mDefaultMouseDisplayId GUARDED_BY(getLock());
+    ui::LogicalDisplayId mNotifiedPointerDisplayId GUARDED_BY(getLock());
+    std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(getLock());
+    std::set<DeviceId> mMouseDevices GUARDED_BY(getLock());
+    std::vector<DisplayViewport> mViewports GUARDED_BY(getLock());
+    bool mShowTouchesEnabled GUARDED_BY(getLock());
+    bool mStylusPointerIconEnabled GUARDED_BY(getLock());
     std::set<ui::LogicalDisplayId /*displayId*/> mDisplaysWithPointersHidden;
-    ui::LogicalDisplayId mCurrentFocusedDisplay GUARDED_BY(mLock);
+    ui::LogicalDisplayId mCurrentFocusedDisplay GUARDED_BY(getLock());
 
 protected:
     using WindowListenerRegisterConsumer = std::function<std::vector<gui::WindowInfo>(
@@ -211,6 +221,10 @@
                                   const WindowListenerUnregisterConsumer& unregisterListener);
 
 private:
+    // WindowInfoListener object should always exist while PointerChoreographer exists, because we
+    // need to use the lock from it. But we don't always need to register the listener.
+    bool mIsWindowInfoListenerRegistered GUARDED_BY(getLock());
+    const sp<PointerChoreographerDisplayInfoListener> mWindowInfoListener;
     const WindowListenerRegisterConsumer mRegisterListener;
     const WindowListenerUnregisterConsumer mUnregisterListener;
 };
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 10fec74..43eaf67 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -35,109 +35,34 @@
       ]
     },
     {
-      "name": "CtsHardwareTestCases",
-      "options": [
-        {
-          "include-filter": "android.hardware.input.cts.tests"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        }
-      ]
+      "name": "CtsHardwareTestCases_cts_tests"
     },
     {
       "name": "CtsInputTestCases"
     },
     {
-      "name": "CtsViewTestCases",
-      "options": [
-        {
-          "include-filter": "android.view.cts.input"
-        }
-      ]
+      "name": "CtsViewTestCases_cts_input"
     },
     {
-      "name": "CtsViewTestCases",
-      "options": [
-        {
-          "include-filter": "android.view.cts.HoverTest"
-        },
-        {
-          "include-filter": "android.view.cts.MotionEventTest"
-        },
-        {
-          "include-filter": "android.view.cts.PointerCaptureTest"
-        },
-        {
-          "include-filter": "android.view.cts.TooltipTest"
-        },
-        {
-          "include-filter": "android.view.cts.TouchDelegateTest"
-        },
-        {
-          "include-filter": "android.view.cts.VerifyInputEventTest"
-        },
-        {
-          "include-filter": "android.view.cts.ViewTest"
-        },
-        {
-          "include-filter": "android.view.cts.ViewUnbufferedTest"
-        }
-      ]
+      "name": "CtsViewTestCases_input_related"
     },
     {
-      "name": "CtsWidgetTestCases",
-      "options": [
-        {
-          "include-filter": "android.widget.cts.NumberPickerTest"
-        },
-        {
-          "include-filter": "android.widget.cts.SeekBarTest"
-        }
-      ]
+      "name": "CtsWidgetTestCases_seekbar_and_numberpicker"
     },
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.hardware.input"
-        }
-      ]
+      "name": "FrameworksCoreTests_hardware_input"
     },
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.view.VerifiedKeyEventTest"
-        },
-        {
-          "include-filter": "android.view.VerifiedMotionEventTest"
-        }
-      ]
+      "name": "FrameworksCoreTests_view_verified"
     },
     {
-      "name": "CtsAppTestCases",
-      "options": [
-        {
-          "include-filter": "android.app.cts.ToolbarActionBarTest"
-        }
-      ]
+      "name": "CtsAppTestCases_cts_toolbaractionbartest"
     },
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.input"
-        }
-      ]
+      "name": "FrameworksServicesTests_android_server_input"
     },
     {
-      "name": "CtsSecurityTestCases",
-      "options": [
-        {
-          "include-filter": "android.security.cts.MotionEventTest"
-        }
-      ]
+      "name": "CtsSecurityTestCases_cts_motioneventtest"
     },
     {
       "name": "CtsSecurityBulletinHostTestCases",
@@ -156,12 +81,7 @@
   ],
   "postsubmit": [
     {
-      "name": "CtsWindowManagerDeviceWindow",
-      "options": [
-        {
-          "include-filter": "android.server.wm.window.WindowInputTests"
-        }
-      ]
+      "name": "CtsWindowManagerDeviceWindow_window_windowinputtests"
     },
     {
       "name": "libinput_tests"
@@ -187,98 +107,31 @@
       ]
     },
     {
-      "name": "CtsHardwareTestCases",
-      "options": [
-        {
-          "include-filter": "android.hardware.input.cts.tests"
-        }
-      ]
+      "name": "CtsHardwareTestCases_cts_tests"
     },
     {
       "name": "CtsInputTestCases"
     },
     {
-      "name": "CtsViewTestCases",
-      "options": [
-        {
-          "include-filter": "android.view.cts.input"
-        }
-      ]
+      "name": "CtsViewTestCases_cts_input"
     },
     {
-      "name": "CtsViewTestCases",
-      "options": [
-        {
-          "include-filter": "android.view.cts.HoverTest"
-        },
-        {
-          "include-filter": "android.view.cts.MotionEventTest"
-        },
-        {
-          "include-filter": "android.view.cts.PointerCaptureTest"
-        },
-        {
-          "include-filter": "android.view.cts.TooltipTest"
-        },
-        {
-          "include-filter": "android.view.cts.TouchDelegateTest"
-        },
-        {
-          "include-filter": "android.view.cts.VerifyInputEventTest"
-        },
-        {
-          "include-filter": "android.view.cts.ViewTest"
-        },
-        {
-          "include-filter": "android.view.cts.ViewUnbufferedTest"
-        }
-      ]
+      "name": "CtsViewTestCases_input_related"
     },
     {
-      "name": "CtsWidgetTestCases",
-      "options": [
-        {
-          "include-filter": "android.widget.cts.NumberPickerTest"
-        },
-        {
-          "include-filter": "android.widget.cts.SeekBarTest"
-        }
-      ]
+      "name": "CtsWidgetTestCases_seekbar_and_numberpicker"
     },
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.view.VerifiedKeyEventTest"
-        },
-        {
-          "include-filter": "android.view.VerifiedMotionEventTest"
-        }
-      ]
+      "name": "FrameworksCoreTests_view_verified"
     },
     {
-      "name": "CtsAppTestCases",
-      "options": [
-        {
-          "include-filter": "android.app.cts.ToolbarActionBarTest"
-        }
-      ]
+      "name": "CtsAppTestCases_cts_toolbaractionbartest"
     },
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.input"
-        }
-      ]
+      "name": "FrameworksServicesTests_server_input"
     },
     {
-      "name": "CtsSecurityTestCases",
-      "options": [
-        {
-          "include-filter": "android.security.cts.MotionEventTest"
-        }
-      ]
+      "name": "CtsSecurityTestCases_cts_motioneventtest"
     },
     {
       "name": "CtsSecurityBulletinHostTestCases",
diff --git a/services/inputflinger/dispatcher/EventLogTags.logtags b/services/inputflinger/dispatcher/EventLogTags.logtags
index 2c5fe21..4dd6073 100644
--- a/services/inputflinger/dispatcher/EventLogTags.logtags
+++ b/services/inputflinger/dispatcher/EventLogTags.logtags
@@ -31,7 +31,7 @@
 # 6: Percent
 # Default value for data of type int/long is 2 (bytes).
 #
-# See system/core/logcat/event.logtags for the master copy of the tags.
+# See system/logging/logcat/event.logtags for the master copy of the tags.
 
 # 62000 - 62199 reserved for inputflinger
 
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 580cde3..4d6b6c7 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -245,6 +245,9 @@
     // True to use three-finger tap as a customizable shortcut; false to use it as a middle-click.
     bool touchpadThreeFingerTapShortcutEnabled;
 
+    // True to enable system gestures (three- and four-finger swipes) on touchpads.
+    bool touchpadSystemGesturesEnabled;
+
     // The set of currently disabled input devices.
     std::set<int32_t> disabledDevices;
 
@@ -297,6 +300,7 @@
             shouldNotifyTouchpadHardwareState(false),
             touchpadRightClickZoneEnabled(false),
             touchpadThreeFingerTapShortcutEnabled(false),
+            touchpadSystemGesturesEnabled(true),
             stylusButtonMotionEventsEnabled(true),
             stylusPointerIconEnabled(false),
             mouseReverseVerticalScrollingEnabled(false),
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 4233f78..1f6600d 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -212,7 +212,7 @@
     // One input device can only have 1 sensor for each sensor Type.
     InputDeviceSensorInfo sensorInfo(identifier.name, std::to_string(identifier.vendor),
                                      identifier.version, sensorType,
-                                     InputDeviceSensorAccuracy::ACCURACY_HIGH,
+                                     InputDeviceSensorAccuracy::HIGH,
                                      /*maxRange=*/axis.max, /*resolution=*/axis.scale,
                                      /*power=*/config.getFloat(prefix + ".power").value_or(0.0f),
                                      /*minDelay=*/config.getInt(prefix + ".minDelay").value_or(0),
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
index 63bc151..7974efe 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -101,7 +101,7 @@
         std::array<int32_t, SENSOR_VEC_LEN> dataVec;
         void resetValue() {
             this->enabled = false;
-            this->accuracy = InputDeviceSensorAccuracy::ACCURACY_NONE;
+            this->accuracy = InputDeviceSensorAccuracy::NONE;
             this->samplingPeriod = std::chrono::nanoseconds(0);
             this->maxBatchReportLatency = std::chrono::nanoseconds(0);
             this->lastSampleTimeNs = std::nullopt;
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index ca8266b..0c094e6 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -351,6 +351,7 @@
 
         bumpGeneration();
     }
+    std::list<NotifyArgs> out;
     if (!changes.any() || changes.test(InputReaderConfiguration::Change::TOUCHPAD_SETTINGS)) {
         mPropertyProvider.getProperty("Use Custom Touchpad Pointer Accel Curve")
                 .setBoolValues({true});
@@ -375,11 +376,11 @@
         mPropertyProvider.getProperty("Button Right Click Zone Enable")
                 .setBoolValues({config.touchpadRightClickZoneEnabled});
         mTouchpadHardwareStateNotificationsEnabled = config.shouldNotifyTouchpadHardwareState;
-
         mGestureConverter.setThreeFingerTapShortcutEnabled(
                 config.touchpadThreeFingerTapShortcutEnabled);
+        out += mGestureConverter.setEnableSystemGestures(when,
+                                                         config.touchpadSystemGesturesEnabled);
     }
-    std::list<NotifyArgs> out;
     if ((!changes.any() && config.pointerCaptureRequest.isEnable()) ||
         changes.test(InputReaderConfiguration::Change::POINTER_CAPTURE)) {
         mPointerCaptured = config.pointerCaptureRequest.isEnable();
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 54270eb..6bd949a 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -132,6 +132,15 @@
     return out;
 }
 
+std::list<NotifyArgs> GestureConverter::setEnableSystemGestures(nsecs_t when, bool enable) {
+    std::list<NotifyArgs> out;
+    if (!enable && mCurrentClassification == MotionClassification::MULTI_FINGER_SWIPE) {
+        out += handleMultiFingerSwipeLift(when, when);
+    }
+    mEnableSystemGestures = enable;
+    return out;
+}
+
 void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const {
     info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0.0f, 1.0f, 0, 0, 0);
 
@@ -461,6 +470,9 @@
                                                                              uint32_t fingerCount,
                                                                              float dx, float dy) {
     std::list<NotifyArgs> out = {};
+    if (!mEnableSystemGestures) {
+        return out;
+    }
 
     if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
         // If the user changes the number of fingers mid-way through a swipe (e.g. they start with
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index ad40721..8d92ead 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -59,6 +59,8 @@
         mThreeFingerTapShortcutEnabled = enabled;
     }
 
+    [[nodiscard]] std::list<NotifyArgs> setEnableSystemGestures(nsecs_t when, bool enable);
+
     void populateMotionRanges(InputDeviceInfo& info) const;
 
     [[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime,
@@ -104,6 +106,7 @@
     InputReaderContext& mReaderContext;
     const bool mEnableFlingStop;
     const bool mEnableNoFocusChange;
+    bool mEnableSystemGestures{true};
 
     bool mThreeFingerTapShortcutEnabled;
 
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index fad8f05..fe40a5e 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -48,6 +48,7 @@
 using testing::AllOf;
 using testing::Each;
 using testing::ElementsAre;
+using testing::IsEmpty;
 using testing::VariantWith;
 
 class GestureConverterTest : public testing::Test {
@@ -849,6 +850,107 @@
                               WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
 }
 
+TEST_F(GestureConverterTest, DisablingSystemGestures_IgnoresMultiFingerSwipe) {
+    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+    GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+    converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
+
+    std::list<NotifyArgs> args = converter.setEnableSystemGestures(ARBITRARY_TIME, false);
+    ASSERT_THAT(args, IsEmpty());
+
+    Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+                         /*dy=*/10);
+    Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+                            /*dy=*/5);
+    Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+
+    args += converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+    args += converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
+    args += converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
+    ASSERT_THAT(args, IsEmpty());
+
+    args = converter.setEnableSystemGestures(ARBITRARY_TIME, true);
+    ASSERT_THAT(args, IsEmpty());
+
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+    ASSERT_THAT(args,
+                ElementsAre(VariantWith<NotifyMotionArgs>(
+                                    WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(
+                                    AMOTION_EVENT_ACTION_POINTER_DOWN |
+                                    1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT)),
+                            VariantWith<NotifyMotionArgs>(WithMotionAction(
+                                    AMOTION_EVENT_ACTION_POINTER_DOWN |
+                                    2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT)),
+                            VariantWith<NotifyMotionArgs>(
+                                    WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
+    ASSERT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(
+                        WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE))));
+}
+
+TEST_F(GestureConverterTest, DisablingSystemGestures_EndsOngoingMultiFingerSwipe) {
+    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+    GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+    converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
+
+    Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+                         /*dy=*/10);
+    std::list<NotifyArgs> args;
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+    ASSERT_FALSE(args.empty());
+
+    // Disabling system gestures should end the swipe early.
+    args = converter.setEnableSystemGestures(ARBITRARY_TIME, false);
+    ASSERT_THAT(args,
+                ElementsAre(VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithMotionAction(
+                                                  AMOTION_EVENT_ACTION_POINTER_UP |
+                                                  2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                                          WithGestureOffset(0, 0, EPSILON),
+                                          WithMotionClassification(
+                                                  MotionClassification::MULTI_FINGER_SWIPE),
+                                          WithPointerCount(3u))),
+                            VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithMotionAction(
+                                                  AMOTION_EVENT_ACTION_POINTER_UP |
+                                                  1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                                          WithGestureOffset(0, 0, EPSILON),
+                                          WithMotionClassification(
+                                                  MotionClassification::MULTI_FINGER_SWIPE),
+                                          WithPointerCount(2u))),
+                            VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+                                          WithGestureOffset(0, 0, EPSILON),
+                                          WithMotionClassification(
+                                                  MotionClassification::MULTI_FINGER_SWIPE),
+                                          WithPointerCount(1u))),
+                            VariantWith<NotifyMotionArgs>(
+                                    AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+                                          WithMotionClassification(MotionClassification::NONE)))));
+    ASSERT_THAT(args,
+                Each(VariantWith<NotifyMotionArgs>(
+                        AllOf(WithToolType(ToolType::FINGER),
+                              WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
+
+    // Further movement in the same swipe should be ignored.
+    Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+                            /*dy=*/5);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
+    ASSERT_THAT(args, IsEmpty());
+    Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
+    ASSERT_THAT(args, IsEmpty());
+
+    // But single-finger pointer motion should be reported.
+    Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
+    ASSERT_THAT(args,
+                ElementsAre(VariantWith<NotifyMotionArgs>(
+                        AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+                              WithRelativeMotion(-5, 10), WithButtonState(0)))));
+}
+
 TEST_F(GestureConverterTest, Pinch_Inwards) {
     input_flags::enable_touchpad_no_focus_change(true);
 
diff --git a/services/inputflinger/tests/SensorInputMapper_test.cpp b/services/inputflinger/tests/SensorInputMapper_test.cpp
index 2c12653..ac2e99e 100644
--- a/services/inputflinger/tests/SensorInputMapper_test.cpp
+++ b/services/inputflinger/tests/SensorInputMapper_test.cpp
@@ -125,7 +125,7 @@
     ASSERT_EQ(arg.source, AINPUT_SOURCE_SENSOR);
     ASSERT_EQ(arg.deviceId, DEVICE_ID);
     ASSERT_EQ(arg.sensorType, InputDeviceSensorType::ACCELEROMETER);
-    ASSERT_EQ(arg.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH);
+    ASSERT_EQ(arg.accuracy, InputDeviceSensorAccuracy::HIGH);
     ASSERT_EQ(arg.hwTimestamp, ARBITRARY_TIME);
     ASSERT_EQ(arg.values, values);
     mMapper->flushSensor(InputDeviceSensorType::ACCELEROMETER);
@@ -170,7 +170,7 @@
     ASSERT_EQ(arg.source, AINPUT_SOURCE_SENSOR);
     ASSERT_EQ(arg.deviceId, DEVICE_ID);
     ASSERT_EQ(arg.sensorType, InputDeviceSensorType::GYROSCOPE);
-    ASSERT_EQ(arg.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH);
+    ASSERT_EQ(arg.accuracy, InputDeviceSensorAccuracy::HIGH);
     ASSERT_EQ(arg.hwTimestamp, ARBITRARY_TIME);
     ASSERT_EQ(arg.values, values);
     mMapper->flushSensor(InputDeviceSensorType::GYROSCOPE);
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index 513b2ef..71d3d1f 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -66,9 +66,9 @@
     MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
     MOCK_METHOD(bool, isRemote, (), (override));
     MOCK_METHOD(ndk::ScopedAStatus, getCpuHeadroom,
-                (const CpuHeadroomParams& params, std::vector<float>* headroom), (override));
+                (const CpuHeadroomParams& params, CpuHeadroomResult* headroom), (override));
     MOCK_METHOD(ndk::ScopedAStatus, getGpuHeadroom,
-                (const GpuHeadroomParams& params, float* headroom), (override));
+                (const GpuHeadroomParams& params, GpuHeadroomResult* headroom), (override));
     MOCK_METHOD(ndk::ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t* interval),
                 (override));
     MOCK_METHOD(ndk::ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* interval),
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
index f8b6c6e..a086aee 100644
--- a/services/surfaceflinger/Display/DisplayModeController.cpp
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -28,6 +28,7 @@
 #include <ftl/concat.h>
 #include <ftl/expected.h>
 #include <log/log.h>
+#include <utils/Errors.h>
 
 namespace android::display {
 
@@ -177,12 +178,13 @@
     }
 }
 
-bool DisplayModeController::initiateModeChange(PhysicalDisplayId displayId,
-                                               DisplayModeRequest&& desiredMode,
-                                               const hal::VsyncPeriodChangeConstraints& constraints,
-                                               hal::VsyncPeriodChangeTimeline& outTimeline) {
+auto DisplayModeController::initiateModeChange(
+        PhysicalDisplayId displayId, DisplayModeRequest&& desiredMode,
+        const hal::VsyncPeriodChangeConstraints& constraints,
+        hal::VsyncPeriodChangeTimeline& outTimeline) -> ModeChangeResult {
     std::lock_guard lock(mDisplayLock);
-    const auto& displayPtr = FTL_EXPECT(mDisplays.get(displayId).ok_or(false)).get();
+    const auto& displayPtr =
+            FTL_EXPECT(mDisplays.get(displayId).ok_or(ModeChangeResult::Aborted)).get();
 
     // TODO: b/255635711 - Flow the DisplayModeRequest through the desired/pending/active states.
     // For now, `desiredMode` and `desiredModeOpt` are one and the same, but the latter is not
@@ -201,13 +203,17 @@
 
     const auto& mode = *displayPtr->pendingModeOpt->mode.modePtr;
 
-    if (mComposerPtr->setActiveModeWithConstraints(displayId, mode.getHwcId(), constraints,
-                                                   &outTimeline) != OK) {
-        return false;
+    const auto error = mComposerPtr->setActiveModeWithConstraints(displayId, mode.getHwcId(),
+                                                                  constraints, &outTimeline);
+    switch (error) {
+        case FAILED_TRANSACTION:
+            return ModeChangeResult::Rejected;
+        case OK:
+            SFTRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
+            return ModeChangeResult::Changed;
+        default:
+            return ModeChangeResult::Aborted;
     }
-
-    SFTRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
-    return true;
 }
 
 void DisplayModeController::finalizeModeChange(PhysicalDisplayId displayId, DisplayModeId modeId,
diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h
index 9ec603d..af3e909 100644
--- a/services/surfaceflinger/Display/DisplayModeController.h
+++ b/services/surfaceflinger/Display/DisplayModeController.h
@@ -70,6 +70,7 @@
     RefreshRateSelectorPtr selectorPtrFor(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
 
     enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch };
+    enum class ModeChangeResult { Changed, Rejected, Aborted };
 
     DesiredModeAction setDesiredMode(PhysicalDisplayId, DisplayModeRequest&&)
             EXCLUDES(mDisplayLock);
@@ -86,9 +87,9 @@
 
     scheduler::FrameRateMode getActiveMode(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
 
-    bool initiateModeChange(PhysicalDisplayId, DisplayModeRequest&&,
-                            const hal::VsyncPeriodChangeConstraints&,
-                            hal::VsyncPeriodChangeTimeline& outTimeline)
+    ModeChangeResult initiateModeChange(PhysicalDisplayId, DisplayModeRequest&&,
+                                        const hal::VsyncPeriodChangeConstraints&,
+                                        hal::VsyncPeriodChangeTimeline& outTimeline)
             REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
 
     void finalizeModeChange(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 4c8ff58..b83f2ab 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -1384,7 +1384,7 @@
     return V2_4::Error::NONE;
 }
 
-V2_4::Error AidlComposer::setActiveConfigWithConstraints(
+Error AidlComposer::setActiveConfigWithConstraints(
         Display display, Config config,
         const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
         VsyncPeriodChangeTimeline* outTimeline) {
@@ -1398,10 +1398,10 @@
                                                      &timeline);
     if (!status.isOk()) {
         ALOGE("setActiveConfigWithConstraints failed %s", status.getDescription().c_str());
-        return static_cast<V2_4::Error>(status.getServiceSpecificError());
+        return static_cast<Error>(status.getServiceSpecificError());
     }
     *outTimeline = translate<VsyncPeriodChangeTimeline>(timeline);
-    return V2_4::Error::NONE;
+    return Error::NONE;
 }
 
 V2_4::Error AidlComposer::setAutoLowLatencyMode(Display display, bool on) {
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 933e8d1..db63d3e 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -206,7 +206,7 @@
     V2_4::Error getDisplayConnectionType(Display display,
                                          IComposerClient::DisplayConnectionType* outType) override;
     V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
-    V2_4::Error setActiveConfigWithConstraints(
+    Error setActiveConfigWithConstraints(
             Display display, Config config,
             const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
             VsyncPeriodChangeTimeline* outTimeline) override;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index c1333c2..ff292fa 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -32,6 +32,8 @@
 #include <ui/PictureProfileHandle.h>
 #include <utils/StrongPointer.h>
 
+#include "DisplayHardware/Hal.h"
+
 #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
 #include <aidl/android/hardware/graphics/common/HdrConversionCapability.h>
 #include <aidl/android/hardware/graphics/common/HdrConversionStrategy.h>
@@ -73,9 +75,9 @@
 using types::V1_2::Dataspace;
 using types::V1_2::PixelFormat;
 
+using hardware::graphics::composer::hal::Error;
 using V2_1::Config;
 using V2_1::Display;
-using V2_1::Error;
 using V2_1::Layer;
 using V2_4::CommandReaderBase;
 using V2_4::CommandWriterBase;
@@ -261,7 +263,7 @@
             Display display, IComposerClient::DisplayConnectionType* outType) = 0;
     virtual V2_4::Error getDisplayVsyncPeriod(Display display,
                                               VsyncPeriodNanos* outVsyncPeriod) = 0;
-    virtual V2_4::Error setActiveConfigWithConstraints(
+    virtual Error setActiveConfigWithConstraints(
             Display display, Config config,
             const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
             VsyncPeriodChangeTimeline* outTimeline) = 0;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index a274995..081f4aa 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -33,6 +33,8 @@
 #include <ui/GraphicBuffer.h>
 #include <ui/PictureProfileHandle.h>
 
+#include "DisplayHardware/Hal.h"
+
 #include <algorithm>
 #include <cinttypes>
 #include <iterator>
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 61d4541..9943856 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -27,6 +27,7 @@
 
 #include "HWComposer.h"
 
+#include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
 #include <android-base/properties.h>
 #include <common/trace.h>
 #include <compositionengine/Output.h>
@@ -733,7 +734,11 @@
     auto error = mDisplayData[displayId].hwcDisplay->setActiveConfigWithConstraints(hwcModeId,
                                                                                     constraints,
                                                                                     outTimeline);
-    RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+    if (error == hal::Error::CONFIG_FAILED) {
+        RETURN_IF_HWC_ERROR_FOR("setActiveConfigWithConstraints", error, displayId,
+                                FAILED_TRANSACTION);
+    }
+    RETURN_IF_HWC_ERROR_FOR("setActiveConfigWithConstraints", error, displayId, UNKNOWN_ERROR);
     return NO_ERROR;
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index e3d9622..568d758 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -17,16 +17,21 @@
 #pragma once
 
 #include <android/hardware/graphics/common/1.1/types.h>
+#include <android/hardware/graphics/composer/2.1/types.h>
 #include <android/hardware/graphics/composer/2.4/IComposer.h>
 #include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+#include <android/hardware/graphics/composer/2.4/types.h>
 
 #include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
 #include <aidl/android/hardware/graphics/common/Hdr.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
+#include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
 #include <aidl/android/hardware/graphics/composer3/VrrConfig.h>
 
+#include <ftl/enum.h>
+
 #define ERROR_HAS_CHANGES 5
 
 namespace android {
@@ -46,7 +51,6 @@
 using types::V1_2::Dataspace;
 using types::V1_2::PixelFormat;
 
-using V2_1::Error;
 using V2_4::IComposer;
 using V2_4::IComposerCallback;
 using V2_4::IComposerClient;
@@ -78,6 +82,22 @@
 using DisplayConfiguration = V3_0::DisplayConfiguration;
 using VrrConfig = V3_0::VrrConfig;
 
+enum class Error : int32_t {
+    NONE = static_cast<int32_t>(V2_1::Error::NONE),
+    BAD_CONFIG = static_cast<int32_t>(V2_1::Error::BAD_CONFIG),
+    BAD_DISPLAY = static_cast<int32_t>(V2_1::Error::BAD_DISPLAY),
+    BAD_LAYER = static_cast<int32_t>(V2_1::Error::BAD_LAYER),
+    BAD_PARAMETER = static_cast<int32_t>(V2_1::Error::BAD_PARAMETER),
+    NO_RESOURCES = static_cast<int32_t>(V2_1::Error::NO_RESOURCES),
+    NOT_VALIDATED = static_cast<int32_t>(V2_1::Error::NOT_VALIDATED),
+    UNSUPPORTED = static_cast<int32_t>(V2_1::Error::UNSUPPORTED),
+    SEAMLESS_NOT_ALLOWED = static_cast<int32_t>(V2_4::Error::SEAMLESS_NOT_ALLOWED),
+    SEAMLESS_NOT_POSSIBLE = static_cast<int32_t>(V2_4::Error::SEAMLESS_NOT_POSSIBLE),
+    CONFIG_FAILED = V3_0::IComposerClient::EX_CONFIG_FAILED,
+    PICTURE_PROFILE_MAX_EXCEEDED = V3_0::IComposerClient::EX_PICTURE_PROFILE_MAX_EXCEEDED,
+    ftl_last = PICTURE_PROFILE_MAX_EXCEEDED
+};
+
 } // namespace hardware::graphics::composer::hal
 
 inline bool hasChangesError(hardware::graphics::composer::hal::Error error) {
@@ -210,7 +230,11 @@
 }
 
 inline std::string to_string(hardware::graphics::composer::hal::Error error) {
-    return to_string(static_cast<hardware::graphics::composer::hal::V2_4::Error>(error));
+    // 5 is reserved for historical reason, during validation 5 means has changes.
+    if (hasChangesError(error)) {
+        return "HAS_CHANGES";
+    }
+    return ftl::enum_string(error);
 }
 
 // For utils::Dumper ADL.
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index e359a26..5703a2d 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -27,6 +27,7 @@
 #include <SurfaceFlingerProperties.h>
 #include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
 #include <android/binder_manager.h>
+#include <android/hardware/graphics/composer/2.1/types.h>
 #include <common/trace.h>
 #include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
 #include <hidl/HidlTransportSupport.h>
@@ -173,7 +174,7 @@
 };
 
 // assume NO_RESOURCES when Status::isOk returns false
-constexpr Error kDefaultError = Error::NO_RESOURCES;
+constexpr V2_1::Error kDefaultError = V2_1::Error::NO_RESOURCES;
 constexpr V2_4::Error kDefaultError_2_4 = static_cast<V2_4::Error>(kDefaultError);
 
 template <typename T, typename U>
@@ -181,7 +182,7 @@
     return (ret.isOk()) ? static_cast<T>(ret) : static_cast<T>(default_val);
 }
 
-Error unwrapRet(Return<Error>& ret) {
+V2_1::Error unwrapRet(Return<V2_1::Error>& ret) {
     return unwrapRet(ret, kDefaultError);
 }
 
@@ -235,7 +236,7 @@
         });
     } else if (sp<V2_3::IComposer> composer_2_3 = V2_3::IComposer::castFrom(mComposer)) {
         composer_2_3->createClient_2_3([&](const auto& tmpError, const auto& tmpClient) {
-            if (tmpError == Error::NONE) {
+            if (tmpError == V2_1::Error::NONE) {
                 mClient = tmpClient;
                 mClient_2_2 = tmpClient;
                 mClient_2_3 = tmpClient;
@@ -243,7 +244,7 @@
         });
     } else {
         mComposer->createClient([&](const auto& tmpError, const auto& tmpClient) {
-            if (tmpError != Error::NONE) {
+            if (tmpError != V2_1::Error::NONE) {
                 return;
             }
 
@@ -325,14 +326,14 @@
 Error HidlComposer::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
                                          Display* outDisplay) {
     const uint32_t bufferSlotCount = 1;
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     if (mClient_2_2) {
         mClient_2_2->createVirtualDisplay_2_2(width, height,
                                               static_cast<types::V1_1::PixelFormat>(*format),
                                               bufferSlotCount,
                                               [&](const auto& tmpError, const auto& tmpDisplay,
                                                   const auto& tmpFormat) {
-                                                  error = tmpError;
+                                                  error = static_cast<Error>(tmpError);
                                                   if (error != Error::NONE) {
                                                       return;
                                                   }
@@ -346,7 +347,7 @@
                                       bufferSlotCount,
                                       [&](const auto& tmpError, const auto& tmpDisplay,
                                           const auto& tmpFormat) {
-                                          error = tmpError;
+                                          error = static_cast<Error>(tmpError);
                                           if (error != Error::NONE) {
                                               return;
                                           }
@@ -361,7 +362,7 @@
 
 Error HidlComposer::destroyVirtualDisplay(Display display) {
     auto ret = mClient->destroyVirtualDisplay(display);
-    return unwrapRet(ret);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::acceptDisplayChanges(Display display) {
@@ -371,10 +372,10 @@
 }
 
 Error HidlComposer::createLayer(Display display, Layer* outLayer) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient->createLayer(display, kMaxLayerBufferCount,
                          [&](const auto& tmpError, const auto& tmpLayer) {
-                             error = tmpError;
+                             error = static_cast<Error>(tmpError);
                              if (error != Error::NONE) {
                                  return;
                              }
@@ -387,13 +388,13 @@
 
 Error HidlComposer::destroyLayer(Display display, Layer layer) {
     auto ret = mClient->destroyLayer(display, layer);
-    return unwrapRet(ret);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::getActiveConfig(Display display, Config* outConfig) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient->getActiveConfig(display, [&](const auto& tmpError, const auto& tmpConfig) {
-        error = tmpError;
+        error = static_cast<Error>(tmpError);
         if (error != Error::NONE) {
             return;
         }
@@ -412,11 +413,11 @@
 }
 
 Error HidlComposer::getColorModes(Display display, std::vector<ColorMode>* outModes) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
 
     if (mClient_2_3) {
         mClient_2_3->getColorModes_2_3(display, [&](const auto& tmpError, const auto& tmpModes) {
-            error = tmpError;
+            error = static_cast<Error>(tmpError);
             if (error != Error::NONE) {
                 return;
             }
@@ -425,7 +426,7 @@
         });
     } else if (mClient_2_2) {
         mClient_2_2->getColorModes_2_2(display, [&](const auto& tmpError, const auto& tmpModes) {
-            error = tmpError;
+            error = static_cast<Error>(tmpError);
             if (error != Error::NONE) {
                 return;
             }
@@ -436,7 +437,7 @@
         });
     } else {
         mClient->getColorModes(display, [&](const auto& tmpError, const auto& tmpModes) {
-            error = tmpError;
+            error = static_cast<Error>(tmpError);
             if (error != Error::NONE) {
                 return;
             }
@@ -451,7 +452,7 @@
 
 Error HidlComposer::getDisplayAttribute(Display display, Config config,
                                         IComposerClient::Attribute attribute, int32_t* outValue) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     if (mClient_2_4) {
         mClient_2_4->getDisplayAttribute_2_4(display, config, attribute,
                                              [&](const auto& tmpError, const auto& tmpValue) {
@@ -466,7 +467,7 @@
         mClient->getDisplayAttribute(display, config,
                                      static_cast<V2_1::IComposerClient::Attribute>(attribute),
                                      [&](const auto& tmpError, const auto& tmpValue) {
-                                         error = tmpError;
+                                         error = static_cast<Error>(tmpError);
                                          if (error != Error::NONE) {
                                              return;
                                          }
@@ -479,9 +480,9 @@
 }
 
 Error HidlComposer::getDisplayConfigs(Display display, std::vector<Config>* outConfigs) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient->getDisplayConfigs(display, [&](const auto& tmpError, const auto& tmpConfigs) {
-        error = tmpError;
+        error = static_cast<Error>(tmpError);
         if (error != Error::NONE) {
             return;
         }
@@ -499,9 +500,9 @@
 }
 
 Error HidlComposer::getDisplayName(Display display, std::string* outName) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient->getDisplayName(display, [&](const auto& tmpError, const auto& tmpName) {
-        error = tmpError;
+        error = static_cast<Error>(tmpError);
         if (error != Error::NONE) {
             return;
         }
@@ -520,9 +521,9 @@
 }
 
 Error HidlComposer::getDozeSupport(Display display, bool* outSupport) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient->getDozeSupport(display, [&](const auto& tmpError, const auto& tmpSupport) {
-        error = tmpError;
+        error = static_cast<Error>(tmpError);
         if (error != Error::NONE) {
             return;
         }
@@ -541,14 +542,14 @@
 Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
                                        float* outMaxLuminance, float* outMaxAverageLuminance,
                                        float* outMinLuminance) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     if (mClient_2_3) {
         mClient_2_3->getHdrCapabilities_2_3(display,
                                             [&](const auto& tmpError, const auto& tmpHdrTypes,
                                                 const auto& tmpMaxLuminance,
                                                 const auto& tmpMaxAverageLuminance,
                                                 const auto& tmpMinLuminance) {
-                                                error = tmpError;
+                                                error = static_cast<Error>(tmpError);
                                                 if (error != Error::NONE) {
                                                     return;
                                                 }
@@ -564,7 +565,7 @@
                                         const auto& tmpMaxLuminance,
                                         const auto& tmpMaxAverageLuminance,
                                         const auto& tmpMinLuminance) {
-                                        error = tmpError;
+                                        error = static_cast<Error>(tmpError);
                                         if (error != Error::NONE) {
                                             return;
                                         }
@@ -606,7 +607,7 @@
 
 Error HidlComposer::setActiveConfig(Display display, Config config) {
     auto ret = mClient->setActiveConfig(display, config);
-    return unwrapRet(ret);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
@@ -625,7 +626,7 @@
 }
 
 Error HidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) {
-    hardware::Return<Error> ret(kDefaultError);
+    hardware::Return<V2_1::Error> ret(kDefaultError);
     if (mClient_2_3) {
         ret = mClient_2_3->setColorMode_2_3(display, mode, renderIntent);
     } else if (mClient_2_2) {
@@ -634,7 +635,7 @@
     } else {
         ret = mClient->setColorMode(display, static_cast<types::V1_0::ColorMode>(mode));
     }
-    return unwrapRet(ret);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::setColorTransform(Display display, const float* matrix) {
@@ -654,25 +655,25 @@
 }
 
 Error HidlComposer::setPowerMode(Display display, IComposerClient::PowerMode mode) {
-    Return<Error> ret(Error::UNSUPPORTED);
+    Return<V2_1::Error> ret(V2_1::Error::UNSUPPORTED);
     if (mClient_2_2) {
         ret = mClient_2_2->setPowerMode_2_2(display, mode);
     } else if (mode != IComposerClient::PowerMode::ON_SUSPEND) {
         ret = mClient->setPowerMode(display, static_cast<V2_1::IComposerClient::PowerMode>(mode));
     }
 
-    return unwrapRet(ret);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
     auto ret = mClient->setVsyncEnabled(display, enabled);
-    return unwrapRet(ret);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::setClientTargetSlotCount(Display display) {
     const uint32_t bufferSlotCount = BufferQueue::NUM_BUFFER_SLOTS;
     auto ret = mClient->setClientTargetSlotCount(display, bufferSlotCount);
-    return unwrapRet(ret);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::validateDisplay(Display display, nsecs_t /*expectedPresentTime*/,
@@ -903,7 +904,7 @@
     // set up new input command queue if necessary
     if (queueChanged) {
         auto ret = mClient->setInputCommandQueue(*mWriter.getMQDescriptor());
-        auto error = unwrapRet(ret);
+        auto error = static_cast<Error>(unwrapRet(ret));
         if (error != Error::NONE) {
             mWriter.reset();
             return error;
@@ -915,17 +916,17 @@
         return Error::NONE;
     }
 
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     hardware::Return<void> ret;
     auto hidl_callback = [&](const auto& tmpError, const auto& tmpOutChanged,
                              const auto& tmpOutLength, const auto& tmpOutHandles) {
-        error = tmpError;
+        error = static_cast<Error>(tmpError);
 
         // set up new output command queue if necessary
         if (error == Error::NONE && tmpOutChanged) {
-            error = kDefaultError;
+            error = static_cast<Error>(kDefaultError);
             mClient->getOutputCommandQueue([&](const auto& tmpError, const auto& tmpDescriptor) {
-                error = tmpError;
+                error = static_cast<Error>(tmpError);
                 if (error != Error::NONE) {
                     return;
                 }
@@ -1000,11 +1001,11 @@
         return keys;
     }
 
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     if (mClient_2_3) {
         mClient_2_3->getPerFrameMetadataKeys_2_3(display,
                                                  [&](const auto& tmpError, const auto& tmpKeys) {
-                                                     error = tmpError;
+                                                     error = static_cast<Error>(tmpError);
                                                      if (error != Error::NONE) {
                                                          ALOGW("getPerFrameMetadataKeys failed "
                                                                "with %d",
@@ -1016,7 +1017,7 @@
     } else {
         mClient_2_2
                 ->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
-                    error = tmpError;
+                    error = static_cast<Error>(tmpError);
                     if (error != Error::NONE) {
                         ALOGW("getPerFrameMetadataKeys failed with %d", tmpError);
                         return;
@@ -1039,10 +1040,10 @@
         return Error::NONE;
     }
 
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
 
     auto getRenderIntentsLambda = [&](const auto& tmpError, const auto& tmpKeys) {
-        error = tmpError;
+        error = static_cast<Error>(tmpError);
         if (error != Error::NONE) {
             return;
         }
@@ -1066,10 +1067,10 @@
         return Error::NONE;
     }
 
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient_2_2->getDataspaceSaturationMatrix(static_cast<types::V1_1::Dataspace>(dataspace),
                                               [&](const auto& tmpError, const auto& tmpMatrix) {
-                                                  error = tmpError;
+                                                  error = static_cast<Error>(tmpError);
                                                   if (error != Error::NONE) {
                                                       return;
                                                   }
@@ -1087,11 +1088,11 @@
         return Error::UNSUPPORTED;
     }
 
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient_2_3->getDisplayIdentificationData(display,
                                               [&](const auto& tmpError, const auto& tmpPort,
                                                   const auto& tmpData) {
-                                                  error = tmpError;
+                                                  error = static_cast<Error>(tmpError);
                                                   if (error != Error::NONE) {
                                                       return;
                                                   }
@@ -1123,13 +1124,13 @@
     if (!mClient_2_3) {
         return Error::UNSUPPORTED;
     }
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient_2_3->getDisplayedContentSamplingAttributes(display,
                                                        [&](const auto tmpError,
                                                            const auto& tmpFormat,
                                                            const auto& tmpDataspace,
                                                            const auto& tmpComponentMask) {
-                                                           error = tmpError;
+                                                           error = static_cast<Error>(tmpError);
                                                            if (error == Error::NONE) {
                                                                *outFormat = tmpFormat;
                                                                *outDataspace = tmpDataspace;
@@ -1149,8 +1150,9 @@
 
     auto enable = enabled ? V2_3::IComposerClient::DisplayedContentSampling::ENABLE
                           : V2_3::IComposerClient::DisplayedContentSampling::DISABLE;
-    return mClient_2_3->setDisplayedContentSamplingEnabled(display, enable, componentMask,
-                                                           maxFrames);
+    auto ret = mClient_2_3->setDisplayedContentSamplingEnabled(display, enable, componentMask,
+                                                               maxFrames);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::getDisplayedContentSample(Display display, uint64_t maxFrames,
@@ -1161,12 +1163,12 @@
     if (!mClient_2_3) {
         return Error::UNSUPPORTED;
     }
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient_2_3->getDisplayedContentSample(display, maxFrames, timestamp,
                                            [&](const auto tmpError, auto tmpNumFrames,
                                                const auto& tmpSamples0, const auto& tmpSamples1,
                                                const auto& tmpSamples2, const auto& tmpSamples3) {
-                                               error = tmpError;
+                                               error = static_cast<Error>(tmpError);
                                                if (error == Error::NONE) {
                                                    outStats->numFrames = tmpNumFrames;
                                                    outStats->component_0_sample = tmpSamples0;
@@ -1196,7 +1198,8 @@
     if (!mClient_2_3) {
         return Error::UNSUPPORTED;
     }
-    return mClient_2_3->setDisplayBrightness(display, brightness);
+    auto ret = mClient_2_3->setDisplayBrightness(display, brightness);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 // Composer HAL 2.4
@@ -1273,19 +1276,18 @@
     return error;
 }
 
-V2_4::Error HidlComposer::setActiveConfigWithConstraints(
+Error HidlComposer::setActiveConfigWithConstraints(
         Display display, Config config,
         const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
         VsyncPeriodChangeTimeline* outTimeline) {
-    using Error = V2_4::Error;
     if (!mClient_2_4) {
         return Error::UNSUPPORTED;
     }
 
-    Error error = kDefaultError_2_4;
+    Error error = static_cast<Error>(kDefaultError_2_4);
     mClient_2_4->setActiveConfigWithConstraints(display, config, vsyncPeriodChangeConstraints,
                                                 [&](const auto& tmpError, const auto& tmpTimeline) {
-                                                    error = tmpError;
+                                                    error = static_cast<Error>(tmpError);
                                                     if (error != Error::NONE) {
                                                         return;
                                                     }
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 9a89dba..42ba9a9 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -60,7 +60,6 @@
 
 using V2_1::Config;
 using V2_1::Display;
-using V2_1::Error;
 using V2_1::Layer;
 using V2_4::CommandReaderBase;
 using V2_4::CommandWriterBase;
@@ -308,7 +307,7 @@
     V2_4::Error getDisplayConnectionType(Display display,
                                          IComposerClient::DisplayConnectionType* outType) override;
     V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
-    V2_4::Error setActiveConfigWithConstraints(
+    Error setActiveConfigWithConstraints(
             Display display, Config config,
             const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
             VsyncPeriodChangeTimeline* outTimeline) override;
diff --git a/services/surfaceflinger/PowerAdvisor/Android.bp b/services/surfaceflinger/PowerAdvisor/Android.bp
new file mode 100644
index 0000000..7352f7a
--- /dev/null
+++ b/services/surfaceflinger/PowerAdvisor/Android.bp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// ADPF uses FMQ which can't build to CPP backend, and is thus not
+// compatible with the rest of SF aidl for this reason
+
+aidl_interface {
+    name: "android.adpf.sessionmanager_aidl",
+    defaults: [
+        "android.hardware.power-aidl",
+    ],
+    srcs: [
+        "aidl/android/adpf/*.aidl",
+    ],
+    local_include_dir: "aidl",
+    unstable: true,
+    backend: {
+        java: {
+            sdk_version: "module_current",
+            enabled: true,
+        },
+        cpp: {
+            enabled: false,
+        },
+        ndk: {
+            enabled: true,
+        },
+    },
+    imports: [
+        "android.hardware.common.fmq-V1",
+        "android.hardware.common-V2",
+    ],
+}
+
+cc_defaults {
+    name: "poweradvisor_deps",
+    shared_libs: [
+        "libpowermanager",
+        "android.adpf.sessionmanager_aidl-ndk",
+    ],
+}
diff --git a/services/surfaceflinger/PowerAdvisor/aidl/android/adpf/ISessionManager.aidl b/services/surfaceflinger/PowerAdvisor/aidl/android/adpf/ISessionManager.aidl
new file mode 100644
index 0000000..c1a6a9e
--- /dev/null
+++ b/services/surfaceflinger/PowerAdvisor/aidl/android/adpf/ISessionManager.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.adpf;
+
+/**
+ * Private service for SessionManager to use. Ideally this will
+ * eventually take the role of HintManagerService.
+ */
+interface ISessionManager {
+    oneway void associateSessionToLayers(in int sessionId, in int ownerUid, in IBinder[] layers);
+    oneway void trackedSessionsDied(in int[] sessionId);
+}
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index e45bdfc..171342d 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -308,11 +308,14 @@
                 const auto setFrameRateVoteType =
                         info->isVisible() ? voteType : LayerVoteType::NoVote;
 
-                const bool hasSetFrameRateOpinion = frameRate.isValid() && !frameRate.isNoVote();
+                const bool hasSetFrameRateOpinion =
+                        frameRate.isValuelessType() || frameRate.vote.rate.isValid();
                 const bool hasCategoryOpinion =
                         frameRate.category != FrameRateCategory::NoPreference &&
                         frameRate.category != FrameRateCategory::Default;
-                const bool hasFrameRateOpinion = hasSetFrameRateOpinion || hasCategoryOpinion;
+                const bool hasFrameRateOpinionAboveGameDefault =
+                        hasSetFrameRateOpinion || hasCategoryOpinion;
+                const bool hasFrameRateOpinionArr = frameRate.isValid() && !frameRate.isNoVote();
 
                 if (gameModeFrameRateOverride.isValid()) {
                     info->setLayerVote({gameFrameRateOverrideVoteType, gameModeFrameRateOverride});
@@ -321,7 +324,8 @@
                         trace(*info, gameFrameRateOverrideVoteType,
                               gameModeFrameRateOverride.getIntValue());
                     }
-                } else if (hasFrameRateOpinion && frameRate.isVoteValidForMrr(isVrrDevice)) {
+                } else if (hasFrameRateOpinionAboveGameDefault &&
+                           frameRate.isVoteValidForMrr(isVrrDevice)) {
                     info->setLayerVote({setFrameRateVoteType,
                                         isValuelessVote ? 0_Hz : frameRate.vote.rate,
                                         frameRate.vote.seamlessness, frameRate.category});
@@ -337,8 +341,18 @@
                         trace(*info, gameFrameRateOverrideVoteType,
                               gameDefaultFrameRateOverride.getIntValue());
                     }
+                } else if (hasFrameRateOpinionArr && frameRate.isVoteValidForMrr(isVrrDevice)) {
+                    // This allows NoPreference votes on ARR devices after considering the
+                    // gameDefaultFrameRateOverride (above).
+                    info->setLayerVote({setFrameRateVoteType,
+                                        isValuelessVote ? 0_Hz : frameRate.vote.rate,
+                                        frameRate.vote.seamlessness, frameRate.category});
+                    if (CC_UNLIKELY(mTraceEnabled)) {
+                        trace(*info, gameFrameRateOverrideVoteType,
+                              frameRate.vote.rate.getIntValue());
+                    }
                 } else {
-                    if (hasFrameRateOpinion && !frameRate.isVoteValidForMrr(isVrrDevice)) {
+                    if (hasFrameRateOpinionArr && !frameRate.isVoteValidForMrr(isVrrDevice)) {
                         SFTRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s "
                                                "%s %s",
                                                info->getName().c_str(),
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0ea26d8..97c8623 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -797,6 +797,12 @@
     }));
 }
 
+bool shouldUseGraphiteIfCompiledAndSupported() {
+    return FlagManager::getInstance().graphite_renderengine() ||
+            (FlagManager::getInstance().graphite_renderengine_preview_rollout() &&
+             base::GetBoolProperty(PROPERTY_DEBUG_RENDERENGINE_GRAPHITE_PREVIEW_OPTIN, false));
+}
+
 void chooseRenderEngineType(renderengine::RenderEngineCreationArgs::Builder& builder) {
     char prop[PROPERTY_VALUE_MAX];
     property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
@@ -825,14 +831,13 @@
 // is used by layertracegenerator (which also needs SurfaceFlinger.cpp). :)
 #if COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_GRAPHITE_RENDERENGINE || \
         COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_FORCE_COMPILE_GRAPHITE_RENDERENGINE
-        const bool useGraphite = FlagManager::getInstance().graphite_renderengine() &&
+        const bool useGraphite = shouldUseGraphiteIfCompiledAndSupported() &&
                 renderengine::RenderEngine::canSupport(kVulkan);
 #else
         const bool useGraphite = false;
-        if (FlagManager::getInstance().graphite_renderengine()) {
-            ALOGE("RenderEngine's Graphite Skia backend was requested with the "
-                  "debug.renderengine.graphite system property, but it is not compiled in this "
-                  "build! Falling back to Ganesh backend selection logic.");
+        if (shouldUseGraphiteIfCompiledAndSupported()) {
+            ALOGE("RenderEngine's Graphite Skia backend was requested, but it is not compiled in "
+                  "this build! Falling back to Ganesh backend selection logic.");
         }
 #endif
         const bool useVulkan = useGraphite ||
@@ -1527,8 +1532,9 @@
         constraints.seamlessRequired = false;
         hal::VsyncPeriodChangeTimeline outTimeline;
 
-        if (!mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
-                                                       constraints, outTimeline)) {
+        if (mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
+                                                      constraints, outTimeline) !=
+            display::DisplayModeController::ModeChangeResult::Changed) {
             continue;
         }
 
@@ -9172,26 +9178,46 @@
 }
 
 binder::Status SurfaceComposerAIDL::enableRefreshRateOverlay(bool active) {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
     mFlinger->sfdo_enableRefreshRateOverlay(active);
     return binder::Status::ok();
 }
 
 binder::Status SurfaceComposerAIDL::setDebugFlash(int delay) {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
     mFlinger->sfdo_setDebugFlash(delay);
     return binder::Status::ok();
 }
 
 binder::Status SurfaceComposerAIDL::scheduleComposite() {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
     mFlinger->sfdo_scheduleComposite();
     return binder::Status::ok();
 }
 
 binder::Status SurfaceComposerAIDL::scheduleCommit() {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
     mFlinger->sfdo_scheduleCommit();
     return binder::Status::ok();
 }
 
 binder::Status SurfaceComposerAIDL::forceClientComposition(bool enabled) {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
     mFlinger->sfdo_forceClientComposition(enabled);
     return binder::Status::ok();
 }
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index bd151be..f257c7c 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -115,6 +115,7 @@
     DUMP_ACONFIG_FLAG(adpf_gpu_sf);
     DUMP_ACONFIG_FLAG(adpf_native_session_manager);
     DUMP_ACONFIG_FLAG(adpf_use_fmq_channel);
+    DUMP_ACONFIG_FLAG(graphite_renderengine_preview_rollout);
 
     /// Trunk stable readonly flags ///
     DUMP_ACONFIG_FLAG(adpf_fmq_sf);
@@ -265,6 +266,7 @@
 FLAG_MANAGER_ACONFIG_FLAG(refresh_rate_overlay_on_external_display, "")
 FLAG_MANAGER_ACONFIG_FLAG(adpf_gpu_sf, "")
 FLAG_MANAGER_ACONFIG_FLAG(adpf_native_session_manager, "");
+FLAG_MANAGER_ACONFIG_FLAG(graphite_renderengine_preview_rollout, "");
 
 /// Trunk stable server (R/W) flags from outside SurfaceFlinger ///
 FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os)
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index fd2306f..a461627 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -53,6 +53,7 @@
     bool adpf_use_fmq_channel() const;
     bool adpf_native_session_manager() const;
     bool adpf_use_fmq_channel_fixed() const;
+    bool graphite_renderengine_preview_rollout() const;
 
     /// Trunk stable readonly flags ///
     bool adpf_fmq_sf() const;
diff --git a/services/surfaceflinger/surfaceflinger.logtags b/services/surfaceflinger/surfaceflinger.logtags
index e68d9f5..6dbe0dd 100644
--- a/services/surfaceflinger/surfaceflinger.logtags
+++ b/services/surfaceflinger/surfaceflinger.logtags
@@ -31,7 +31,7 @@
 # 6: Percent
 # Default value for data of type int/long is 2 (bytes).
 #
-# See system/core/logcat/event.logtags for the master copy of the tags.
+# See system/logging/logcat/event.logtags for the master copy of the tags.
 
 # 60100 - 60199 reserved for surfaceflinger
 
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index 34a935a..2c44e4c 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -173,6 +173,13 @@
 } # frame_rate_category_mrr
 
 flag {
+  name: "graphite_renderengine_preview_rollout"
+  namespace: "core_graphics"
+  description: "R/W flag to enable Skia's Graphite Vulkan backend in RenderEngine, IF it is already compiled with force_compile_graphite_renderengine, AND the debug.renderengine.graphite_preview_optin sysprop is set to true."
+  bug: "293371537"
+} # graphite_renderengine_preview_rollout
+
+flag {
   name: "latch_unsignaled_with_auto_refresh_changed"
   namespace: "core_graphics"
   description: "Ignore eAutoRefreshChanged with latch unsignaled"
diff --git a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
index d971150..29a1fab 100644
--- a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
@@ -20,6 +20,7 @@
 #include "Display/DisplayModeController.h"
 #include "Display/DisplaySnapshot.h"
 #include "DisplayHardware/HWComposer.h"
+#include "DisplayHardware/Hal.h"
 #include "DisplayIdentificationTestHelpers.h"
 #include "FpsOps.h"
 #include "mock/DisplayHardware/MockComposer.h"
@@ -103,7 +104,7 @@
 
         EXPECT_CALL(*mComposerHal,
                     setActiveConfigWithConstraints(kHwcDisplayId, hwcModeId, constraints, _))
-                .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(hal::V2_4::Error::NONE)));
+                .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(hal::Error::NONE)));
 
         return constraints;
     }
@@ -183,7 +184,8 @@
     hal::VsyncPeriodChangeTimeline timeline;
     const auto constraints = expectModeSet(modeRequest, timeline);
 
-    EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+    EXPECT_EQ(DisplayModeController::ModeChangeResult::Changed,
+              mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
     EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getPendingMode(mDisplayId));
 
     mDmc.clearDesiredMode(mDisplayId);
@@ -210,7 +212,8 @@
     hal::VsyncPeriodChangeTimeline timeline;
     auto constraints = expectModeSet(modeRequest, timeline);
 
-    EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+    EXPECT_EQ(DisplayModeController::ModeChangeResult::Changed,
+              mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
     EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getPendingMode(mDisplayId));
 
     // No action since a mode switch has already been initiated.
@@ -223,7 +226,8 @@
     constexpr bool kSubsequent = true;
     constraints = expectModeSet(modeRequest, timeline, kSubsequent);
 
-    EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+    EXPECT_EQ(DisplayModeController::ModeChangeResult::Changed,
+              mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
     EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDmc.getPendingMode(mDisplayId));
 
     mDmc.clearDesiredMode(mDisplayId);
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index e0753a3..30bce2e 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -69,7 +69,7 @@
 using ::testing::StrictMock;
 
 struct HWComposerTest : testing::Test {
-    using HalError = hardware::graphics::composer::V2_1::Error;
+    using HalError = hal::Error;
 
     Hwc2::mock::Composer* const mHal = new StrictMock<Hwc2::mock::Composer>();
     impl::HWComposer mHwc{std::unique_ptr<Hwc2::Composer>(mHal)};
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index de37b63..767000e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -715,6 +715,204 @@
     EXPECT_EQ(0, frequentLayerCount(time));
 }
 
+// Tests MRR NoPreference-only vote, no game default override. Expects vote reset.
+TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreference_mrr) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+    const LayerHistory::LayerVoteType defaultVote = LayerHistory::LayerVoteType::Min;
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    setDefaultLayerVote(layer.get(), defaultVote);
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(defaultVote, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+// Tests VRR NoPreference-only vote, no game default override. Expects NoPreference, *not* vote
+// reset.
+TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreference_vrr) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+    const LayerHistory::LayerVoteType defaultVote = LayerHistory::LayerVoteType::Min;
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    setDefaultLayerVote(layer.get(), defaultVote);
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::NoPreference, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreferenceWithGameDefault_vrr) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+    const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+    const uid_t uid = 456;
+
+    history().updateGameDefaultFrameRateOverride(
+            FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+    auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid));
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(gameDefaultFrameRate, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreferenceWithGameDefault_mrr) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+    const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+    const uid_t uid = 456;
+
+    history().updateGameDefaultFrameRateOverride(
+            FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+    auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid));
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(gameDefaultFrameRate, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerNoVoteWithGameDefault_vrr) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+    const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+    const uid_t uid = 456;
+
+    history().updateGameDefaultFrameRateOverride(
+            FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+    auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid));
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_NO_VOTE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    // Expect NoVote to be skipped in summarize.
+    EXPECT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerNoVoteWithGameDefault_mrr) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+    const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+    const uid_t uid = 456;
+
+    history().updateGameDefaultFrameRateOverride(
+            FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+    auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid));
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_NO_VOTE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    // Expect NoVote to be skipped in summarize.
+    EXPECT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+}
+
 TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategory) {
     SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 8699621..b0dd5c2 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -34,7 +34,7 @@
                                                static_cast<hal::HWConfigId>(        \
                                                        ftl::to_underlying(modeId)), \
                                                _, _))                               \
-            .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)))
+            .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(hal::Error::NONE)))
 
 namespace android {
 namespace {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
index 20a3315..6cc6322 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
@@ -18,12 +18,13 @@
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
 #include <gui/SurfaceComposerClient.h>
+#include "DisplayHardware/Hal.h"
 #include "DisplayTransactionTestHelpers.h"
 
 namespace android {
 
 using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-using android::hardware::graphics::composer::V2_1::Error;
+using android::hardware::graphics::composer::hal::Error;
 
 class NotifyExpectedPresentTest : public DisplayTransactionTest {
 public:
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index c3934e6..3ae5ed9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -21,6 +21,8 @@
 
 #include "CommitAndCompositeTest.h"
 
+#include "DisplayHardware/Hal.h"
+
 using namespace std::chrono_literals;
 using testing::_;
 using testing::Return;
@@ -40,7 +42,7 @@
     EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _)).WillOnce([] {
         constexpr Duration kMockHwcRunTime = 20ms;
         std::this_thread::sleep_for(kMockHwcRunTime);
-        return hardware::graphics::composer::V2_1::Error::NONE;
+        return hardware::graphics::composer::hal::Error::NONE;
     });
     EXPECT_CALL(*mPowerAdvisor, reportActualWorkDuration()).Times(1);
 
@@ -61,7 +63,7 @@
     EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _)).WillOnce([] {
         constexpr Duration kMockHwcRunTime = 20ms;
         std::this_thread::sleep_for(kMockHwcRunTime);
-        return hardware::graphics::composer::V2_1::Error::NONE;
+        return hardware::graphics::composer::hal::Error::NONE;
     });
     EXPECT_CALL(*mPowerAdvisor, reportActualWorkDuration()).Times(0);
 
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 88052db..0d5266e 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -20,6 +20,7 @@
 
 #include "DisplayHardware/ComposerHal.h"
 #include "DisplayHardware/HWC2.h"
+#include "DisplayHardware/Hal.h"
 
 namespace android {
 
@@ -34,9 +35,9 @@
 using android::hardware::graphics::common::V1_2::Dataspace;
 using android::hardware::graphics::common::V1_2::PixelFormat;
 
+using android::hardware::graphics::composer::hal::Error;
 using android::hardware::graphics::composer::V2_1::Config;
 using android::hardware::graphics::composer::V2_1::Display;
-using android::hardware::graphics::composer::V2_1::Error;
 using android::hardware::graphics::composer::V2_1::IComposer;
 using android::hardware::graphics::composer::V2_1::Layer;
 using android::hardware::graphics::composer::V2_4::IComposerCallback;
@@ -142,8 +143,8 @@
                  V2_4::Error(Display, Config, std::vector<VsyncPeriodNanos>*));
     MOCK_METHOD2(getDisplayVsyncPeriod, V2_4::Error(Display, VsyncPeriodNanos*));
     MOCK_METHOD4(setActiveConfigWithConstraints,
-                 V2_4::Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
-                             VsyncPeriodChangeTimeline*));
+                 Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
+                       VsyncPeriodChangeTimeline*));
     MOCK_METHOD2(setAutoLowLatencyMode, V2_4::Error(Display, bool));
     MOCK_METHOD2(setBootDisplayConfig, Error(Display, Config));
     MOCK_METHOD1(clearBootDisplayConfig, Error(Display));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index fa74492..ec065a7 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <gmock/gmock.h>
+#include <cstdint>
 
 #include "DisplayHardware/HWC2.h"
 
@@ -85,8 +86,8 @@
     MOCK_METHOD(ftl::Future<hal::Error>, setDisplayBrightness,
                 (float, float, const Hwc2::Composer::DisplayBrightnessOptions &), (override));
     MOCK_METHOD(hal::Error, setActiveConfigWithConstraints,
-                (hal::HWConfigId, const hal::VsyncPeriodChangeConstraints &,
-                 hal::VsyncPeriodChangeTimeline *),
+                (hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&,
+                 hal::VsyncPeriodChangeTimeline*),
                 (override));
     MOCK_METHOD(hal::Error, setBootDisplayConfig, (hal::HWConfigId), (override));
     MOCK_METHOD(hal::Error, clearBootDisplayConfig, (), (override));
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index c335e2a..8451ad1 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -45,6 +45,9 @@
 #include "driver.h"
 #include "layers_extensions.h"
 
+#include <com_android_graphics_libvulkan_flags.h>
+
+using namespace com::android::graphics::libvulkan;
 
 namespace vulkan {
 namespace api {
@@ -1473,7 +1476,7 @@
     if (!EnsureInitialized())
         return VK_ERROR_OUT_OF_HOST_MEMORY;
 
-    *pApiVersion = VK_API_VERSION_1_3;
+    *pApiVersion = flags::vulkan_1_4_instance_api() ? VK_API_VERSION_1_4 : VK_API_VERSION_1_3;
     return VK_SUCCESS;
 }
 
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 9ff0b46..131c97c 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -266,6 +266,7 @@
     INIT_PROC(true, dev, CreateRenderPass);
     INIT_PROC(true, dev, DestroyRenderPass);
     INIT_PROC(true, dev, GetRenderAreaGranularity);
+    INIT_PROC(false, dev, GetRenderingAreaGranularity);
     INIT_PROC(true, dev, CreateCommandPool);
     INIT_PROC(true, dev, DestroyCommandPool);
     INIT_PROC(true, dev, ResetCommandPool);
@@ -323,6 +324,7 @@
     INIT_PROC_EXT(KHR_swapchain, true, dev, GetSwapchainImagesKHR);
     INIT_PROC_EXT(KHR_swapchain, true, dev, AcquireNextImageKHR);
     INIT_PROC_EXT(KHR_swapchain, true, dev, QueuePresentKHR);
+    INIT_PROC(false, dev, CmdPushDescriptorSet);
     INIT_PROC(false, dev, TrimCommandPool);
     INIT_PROC(false, dev, GetDeviceGroupPeerMemoryFeatures);
     INIT_PROC(false, dev, BindBufferMemory2);
@@ -335,6 +337,7 @@
     INIT_PROC(false, dev, CreateDescriptorUpdateTemplate);
     INIT_PROC(false, dev, DestroyDescriptorUpdateTemplate);
     INIT_PROC(false, dev, UpdateDescriptorSetWithTemplate);
+    INIT_PROC(false, dev, CmdPushDescriptorSetWithTemplate);
     INIT_PROC(false, dev, GetBufferMemoryRequirements2);
     INIT_PROC(false, dev, GetImageMemoryRequirements2);
     INIT_PROC(false, dev, GetImageSparseMemoryRequirements2);
@@ -359,11 +362,13 @@
     INIT_PROC(false, dev, GetBufferOpaqueCaptureAddress);
     INIT_PROC(false, dev, GetBufferDeviceAddress);
     INIT_PROC(false, dev, GetDeviceMemoryOpaqueCaptureAddress);
+    INIT_PROC(false, dev, CmdSetLineStipple);
     INIT_PROC(false, dev, CmdSetCullMode);
     INIT_PROC(false, dev, CmdSetFrontFace);
     INIT_PROC(false, dev, CmdSetPrimitiveTopology);
     INIT_PROC(false, dev, CmdSetViewportWithCount);
     INIT_PROC(false, dev, CmdSetScissorWithCount);
+    INIT_PROC(false, dev, CmdBindIndexBuffer2);
     INIT_PROC(false, dev, CmdBindVertexBuffers2);
     INIT_PROC(false, dev, CmdSetDepthTestEnable);
     INIT_PROC(false, dev, CmdSetDepthWriteEnable);
@@ -390,8 +395,22 @@
     INIT_PROC(false, dev, CmdPipelineBarrier2);
     INIT_PROC(false, dev, QueueSubmit2);
     INIT_PROC(false, dev, CmdWriteTimestamp2);
+    INIT_PROC(false, dev, CopyMemoryToImage);
+    INIT_PROC(false, dev, CopyImageToMemory);
+    INIT_PROC(false, dev, CopyImageToImage);
+    INIT_PROC(false, dev, TransitionImageLayout);
     INIT_PROC(false, dev, CmdBeginRendering);
     INIT_PROC(false, dev, CmdEndRendering);
+    INIT_PROC(false, dev, GetImageSubresourceLayout2);
+    INIT_PROC(false, dev, GetDeviceImageSubresourceLayout);
+    INIT_PROC(false, dev, MapMemory2);
+    INIT_PROC(false, dev, UnmapMemory2);
+    INIT_PROC(false, dev, CmdBindDescriptorSets2);
+    INIT_PROC(false, dev, CmdPushConstants2);
+    INIT_PROC(false, dev, CmdPushDescriptorSet2);
+    INIT_PROC(false, dev, CmdPushDescriptorSetWithTemplate2);
+    INIT_PROC(false, dev, CmdSetRenderingAttachmentLocations);
+    INIT_PROC(false, dev, CmdSetRenderingInputAttachmentIndices);
     // clang-format on
 
     return success;
@@ -480,6 +499,7 @@
 VKAPI_ATTR VkResult CreateRenderPass(VkDevice device, const VkRenderPassCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
 VKAPI_ATTR void DestroyRenderPass(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR void GetRenderAreaGranularity(VkDevice device, VkRenderPass renderPass, VkExtent2D* pGranularity);
+VKAPI_ATTR void GetRenderingAreaGranularity(VkDevice device, const VkRenderingAreaInfo* pRenderingAreaInfo, VkExtent2D* pGranularity);
 VKAPI_ATTR VkResult CreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool);
 VKAPI_ATTR void DestroyCommandPool(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR VkResult ResetCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags);
@@ -550,6 +570,7 @@
 VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties);
 VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties);
 VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties);
+VKAPI_ATTR void CmdPushDescriptorSet(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites);
 VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags);
 VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties);
 VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties);
@@ -567,6 +588,7 @@
 VKAPI_ATTR VkResult CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate);
 VKAPI_ATTR void DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData);
+VKAPI_ATTR void CmdPushDescriptorSetWithTemplate(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplate descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData);
 VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
 VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
 VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements);
@@ -591,12 +613,14 @@
 VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
 VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
 VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo);
+VKAPI_ATTR void CmdSetLineStipple(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor, uint16_t lineStipplePattern);
 VKAPI_ATTR VkResult GetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties);
 VKAPI_ATTR void CmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode);
 VKAPI_ATTR void CmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace);
 VKAPI_ATTR void CmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology);
 VKAPI_ATTR void CmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports);
 VKAPI_ATTR void CmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors);
+VKAPI_ATTR void CmdBindIndexBuffer2(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, VkIndexType indexType);
 VKAPI_ATTR void CmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides);
 VKAPI_ATTR void CmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable);
 VKAPI_ATTR void CmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable);
@@ -623,8 +647,22 @@
 VKAPI_ATTR void CmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo);
 VKAPI_ATTR VkResult QueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence);
 VKAPI_ATTR void CmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query);
+VKAPI_ATTR VkResult CopyMemoryToImage(VkDevice device, const VkCopyMemoryToImageInfo* pCopyMemoryToImageInfo);
+VKAPI_ATTR VkResult CopyImageToMemory(VkDevice device, const VkCopyImageToMemoryInfo* pCopyImageToMemoryInfo);
+VKAPI_ATTR VkResult CopyImageToImage(VkDevice device, const VkCopyImageToImageInfo* pCopyImageToImageInfo);
+VKAPI_ATTR VkResult TransitionImageLayout(VkDevice device, uint32_t transitionCount, const VkHostImageLayoutTransitionInfo* pTransitions);
 VKAPI_ATTR void CmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo);
 VKAPI_ATTR void CmdEndRendering(VkCommandBuffer commandBuffer);
+VKAPI_ATTR void GetImageSubresourceLayout2(VkDevice device, VkImage image, const VkImageSubresource2* pSubresource, VkSubresourceLayout2* pLayout);
+VKAPI_ATTR void GetDeviceImageSubresourceLayout(VkDevice device, const VkDeviceImageSubresourceInfo* pInfo, VkSubresourceLayout2* pLayout);
+VKAPI_ATTR VkResult MapMemory2(VkDevice device, const VkMemoryMapInfo* pMemoryMapInfo, void** ppData);
+VKAPI_ATTR VkResult UnmapMemory2(VkDevice device, const VkMemoryUnmapInfo* pMemoryUnmapInfo);
+VKAPI_ATTR void CmdBindDescriptorSets2(VkCommandBuffer commandBuffer, const VkBindDescriptorSetsInfo* pBindDescriptorSetsInfo);
+VKAPI_ATTR void CmdPushConstants2(VkCommandBuffer commandBuffer, const VkPushConstantsInfo* pPushConstantsInfo);
+VKAPI_ATTR void CmdPushDescriptorSet2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetInfo* pPushDescriptorSetInfo);
+VKAPI_ATTR void CmdPushDescriptorSetWithTemplate2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetWithTemplateInfo* pPushDescriptorSetWithTemplateInfo);
+VKAPI_ATTR void CmdSetRenderingAttachmentLocations(VkCommandBuffer commandBuffer, const VkRenderingAttachmentLocationInfo* pLocationInfo);
+VKAPI_ATTR void CmdSetRenderingInputAttachmentIndices(VkCommandBuffer commandBuffer, const VkRenderingInputAttachmentIndexInfo* pInputAttachmentIndexInfo);
 
 VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) {
     return GetData(instance).dispatch.EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices);
@@ -764,7 +802,9 @@
         { "vkCmdBeginRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRenderPass2) },
         { "vkCmdBeginRendering", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRendering) },
         { "vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(CmdBindDescriptorSets) },
+        { "vkCmdBindDescriptorSets2", reinterpret_cast<PFN_vkVoidFunction>(CmdBindDescriptorSets2) },
         { "vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdBindIndexBuffer) },
+        { "vkCmdBindIndexBuffer2", reinterpret_cast<PFN_vkVoidFunction>(CmdBindIndexBuffer2) },
         { "vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(CmdBindPipeline) },
         { "vkCmdBindVertexBuffers", reinterpret_cast<PFN_vkVoidFunction>(CmdBindVertexBuffers) },
         { "vkCmdBindVertexBuffers2", reinterpret_cast<PFN_vkVoidFunction>(CmdBindVertexBuffers2) },
@@ -802,6 +842,11 @@
         { "vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(CmdPipelineBarrier) },
         { "vkCmdPipelineBarrier2", reinterpret_cast<PFN_vkVoidFunction>(CmdPipelineBarrier2) },
         { "vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(CmdPushConstants) },
+        { "vkCmdPushConstants2", reinterpret_cast<PFN_vkVoidFunction>(CmdPushConstants2) },
+        { "vkCmdPushDescriptorSet", reinterpret_cast<PFN_vkVoidFunction>(CmdPushDescriptorSet) },
+        { "vkCmdPushDescriptorSet2", reinterpret_cast<PFN_vkVoidFunction>(CmdPushDescriptorSet2) },
+        { "vkCmdPushDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(CmdPushDescriptorSetWithTemplate) },
+        { "vkCmdPushDescriptorSetWithTemplate2", reinterpret_cast<PFN_vkVoidFunction>(CmdPushDescriptorSetWithTemplate2) },
         { "vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(CmdResetEvent) },
         { "vkCmdResetEvent2", reinterpret_cast<PFN_vkVoidFunction>(CmdResetEvent2) },
         { "vkCmdResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(CmdResetQueryPool) },
@@ -820,10 +865,13 @@
         { "vkCmdSetEvent", reinterpret_cast<PFN_vkVoidFunction>(CmdSetEvent) },
         { "vkCmdSetEvent2", reinterpret_cast<PFN_vkVoidFunction>(CmdSetEvent2) },
         { "vkCmdSetFrontFace", reinterpret_cast<PFN_vkVoidFunction>(CmdSetFrontFace) },
+        { "vkCmdSetLineStipple", reinterpret_cast<PFN_vkVoidFunction>(CmdSetLineStipple) },
         { "vkCmdSetLineWidth", reinterpret_cast<PFN_vkVoidFunction>(CmdSetLineWidth) },
         { "vkCmdSetPrimitiveRestartEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetPrimitiveRestartEnable) },
         { "vkCmdSetPrimitiveTopology", reinterpret_cast<PFN_vkVoidFunction>(CmdSetPrimitiveTopology) },
         { "vkCmdSetRasterizerDiscardEnable", reinterpret_cast<PFN_vkVoidFunction>(CmdSetRasterizerDiscardEnable) },
+        { "vkCmdSetRenderingAttachmentLocations", reinterpret_cast<PFN_vkVoidFunction>(CmdSetRenderingAttachmentLocations) },
+        { "vkCmdSetRenderingInputAttachmentIndices", reinterpret_cast<PFN_vkVoidFunction>(CmdSetRenderingInputAttachmentIndices) },
         { "vkCmdSetScissor", reinterpret_cast<PFN_vkVoidFunction>(CmdSetScissor) },
         { "vkCmdSetScissorWithCount", reinterpret_cast<PFN_vkVoidFunction>(CmdSetScissorWithCount) },
         { "vkCmdSetStencilCompareMask", reinterpret_cast<PFN_vkVoidFunction>(CmdSetStencilCompareMask) },
@@ -838,6 +886,9 @@
         { "vkCmdWaitEvents2", reinterpret_cast<PFN_vkVoidFunction>(CmdWaitEvents2) },
         { "vkCmdWriteTimestamp", reinterpret_cast<PFN_vkVoidFunction>(CmdWriteTimestamp) },
         { "vkCmdWriteTimestamp2", reinterpret_cast<PFN_vkVoidFunction>(CmdWriteTimestamp2) },
+        { "vkCopyImageToImage", reinterpret_cast<PFN_vkVoidFunction>(CopyImageToImage) },
+        { "vkCopyImageToMemory", reinterpret_cast<PFN_vkVoidFunction>(CopyImageToMemory) },
+        { "vkCopyMemoryToImage", reinterpret_cast<PFN_vkVoidFunction>(CopyMemoryToImage) },
         { "vkCreateBuffer", reinterpret_cast<PFN_vkVoidFunction>(CreateBuffer) },
         { "vkCreateBufferView", reinterpret_cast<PFN_vkVoidFunction>(CreateBufferView) },
         { "vkCreateCommandPool", reinterpret_cast<PFN_vkVoidFunction>(CreateCommandPool) },
@@ -911,6 +962,7 @@
         { "vkGetDeviceGroupSurfacePresentModesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupSurfacePresentModesKHR) },
         { "vkGetDeviceImageMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceImageMemoryRequirements) },
         { "vkGetDeviceImageSparseMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceImageSparseMemoryRequirements) },
+        { "vkGetDeviceImageSubresourceLayout", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceImageSubresourceLayout) },
         { "vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryCommitment) },
         { "vkGetDeviceMemoryOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryOpaqueCaptureAddress) },
         { "vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceProcAddr) },
@@ -923,16 +975,19 @@
         { "vkGetImageSparseMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetImageSparseMemoryRequirements) },
         { "vkGetImageSparseMemoryRequirements2", reinterpret_cast<PFN_vkVoidFunction>(GetImageSparseMemoryRequirements2) },
         { "vkGetImageSubresourceLayout", reinterpret_cast<PFN_vkVoidFunction>(GetImageSubresourceLayout) },
+        { "vkGetImageSubresourceLayout2", reinterpret_cast<PFN_vkVoidFunction>(GetImageSubresourceLayout2) },
         { "vkGetInstanceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr) },
         { "vkGetMemoryAndroidHardwareBufferANDROID", reinterpret_cast<PFN_vkVoidFunction>(GetMemoryAndroidHardwareBufferANDROID) },
         { "vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(GetPipelineCacheData) },
         { "vkGetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(GetPrivateData) },
         { "vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(GetQueryPoolResults) },
         { "vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(GetRenderAreaGranularity) },
+        { "vkGetRenderingAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(GetRenderingAreaGranularity) },
         { "vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(GetSemaphoreCounterValue) },
         { "vkGetSwapchainImagesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetSwapchainImagesKHR) },
         { "vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(InvalidateMappedMemoryRanges) },
         { "vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(MapMemory) },
+        { "vkMapMemory2", reinterpret_cast<PFN_vkVoidFunction>(MapMemory2) },
         { "vkMergePipelineCaches", reinterpret_cast<PFN_vkVoidFunction>(MergePipelineCaches) },
         { "vkQueueBindSparse", reinterpret_cast<PFN_vkVoidFunction>(QueueBindSparse) },
         { "vkQueuePresentKHR", reinterpret_cast<PFN_vkVoidFunction>(QueuePresentKHR) },
@@ -948,8 +1003,10 @@
         { "vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(SetEvent) },
         { "vkSetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(SetPrivateData) },
         { "vkSignalSemaphore", reinterpret_cast<PFN_vkVoidFunction>(SignalSemaphore) },
+        { "vkTransitionImageLayout", reinterpret_cast<PFN_vkVoidFunction>(TransitionImageLayout) },
         { "vkTrimCommandPool", reinterpret_cast<PFN_vkVoidFunction>(TrimCommandPool) },
         { "vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(UnmapMemory) },
+        { "vkUnmapMemory2", reinterpret_cast<PFN_vkVoidFunction>(UnmapMemory2) },
         { "vkUpdateDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(UpdateDescriptorSetWithTemplate) },
         { "vkUpdateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(UpdateDescriptorSets) },
         { "vkWaitForFences", reinterpret_cast<PFN_vkVoidFunction>(WaitForFences) },
@@ -1273,6 +1330,10 @@
     GetData(device).dispatch.GetRenderAreaGranularity(device, renderPass, pGranularity);
 }
 
+VKAPI_ATTR void GetRenderingAreaGranularity(VkDevice device, const VkRenderingAreaInfo* pRenderingAreaInfo, VkExtent2D* pGranularity) {
+    GetData(device).dispatch.GetRenderingAreaGranularity(device, pRenderingAreaInfo, pGranularity);
+}
+
 VKAPI_ATTR VkResult CreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool) {
     return GetData(device).dispatch.CreateCommandPool(device, pCreateInfo, pAllocator, pCommandPool);
 }
@@ -1553,6 +1614,10 @@
     GetData(physicalDevice).dispatch.GetPhysicalDeviceSparseImageFormatProperties2(physicalDevice, pFormatInfo, pPropertyCount, pProperties);
 }
 
+VKAPI_ATTR void CmdPushDescriptorSet(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites) {
+    GetData(commandBuffer).dispatch.CmdPushDescriptorSet(commandBuffer, pipelineBindPoint, layout, set, descriptorWriteCount, pDescriptorWrites);
+}
+
 VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags) {
     GetData(device).dispatch.TrimCommandPool(device, commandPool, flags);
 }
@@ -1621,6 +1686,10 @@
     GetData(device).dispatch.UpdateDescriptorSetWithTemplate(device, descriptorSet, descriptorUpdateTemplate, pData);
 }
 
+VKAPI_ATTR void CmdPushDescriptorSetWithTemplate(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplate descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData) {
+    GetData(commandBuffer).dispatch.CmdPushDescriptorSetWithTemplate(commandBuffer, descriptorUpdateTemplate, layout, set, pData);
+}
+
 VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
     GetData(device).dispatch.GetBufferMemoryRequirements2(device, pInfo, pMemoryRequirements);
 }
@@ -1717,6 +1786,10 @@
     return GetData(device).dispatch.GetDeviceMemoryOpaqueCaptureAddress(device, pInfo);
 }
 
+VKAPI_ATTR void CmdSetLineStipple(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor, uint16_t lineStipplePattern) {
+    GetData(commandBuffer).dispatch.CmdSetLineStipple(commandBuffer, lineStippleFactor, lineStipplePattern);
+}
+
 VKAPI_ATTR VkResult GetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties) {
     return GetData(physicalDevice).dispatch.GetPhysicalDeviceToolProperties(physicalDevice, pToolCount, pToolProperties);
 }
@@ -1741,6 +1814,10 @@
     GetData(commandBuffer).dispatch.CmdSetScissorWithCount(commandBuffer, scissorCount, pScissors);
 }
 
+VKAPI_ATTR void CmdBindIndexBuffer2(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, VkIndexType indexType) {
+    GetData(commandBuffer).dispatch.CmdBindIndexBuffer2(commandBuffer, buffer, offset, size, indexType);
+}
+
 VKAPI_ATTR void CmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides) {
     GetData(commandBuffer).dispatch.CmdBindVertexBuffers2(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets, pSizes, pStrides);
 }
@@ -1845,6 +1922,22 @@
     GetData(commandBuffer).dispatch.CmdWriteTimestamp2(commandBuffer, stage, queryPool, query);
 }
 
+VKAPI_ATTR VkResult CopyMemoryToImage(VkDevice device, const VkCopyMemoryToImageInfo* pCopyMemoryToImageInfo) {
+    return GetData(device).dispatch.CopyMemoryToImage(device, pCopyMemoryToImageInfo);
+}
+
+VKAPI_ATTR VkResult CopyImageToMemory(VkDevice device, const VkCopyImageToMemoryInfo* pCopyImageToMemoryInfo) {
+    return GetData(device).dispatch.CopyImageToMemory(device, pCopyImageToMemoryInfo);
+}
+
+VKAPI_ATTR VkResult CopyImageToImage(VkDevice device, const VkCopyImageToImageInfo* pCopyImageToImageInfo) {
+    return GetData(device).dispatch.CopyImageToImage(device, pCopyImageToImageInfo);
+}
+
+VKAPI_ATTR VkResult TransitionImageLayout(VkDevice device, uint32_t transitionCount, const VkHostImageLayoutTransitionInfo* pTransitions) {
+    return GetData(device).dispatch.TransitionImageLayout(device, transitionCount, pTransitions);
+}
+
 VKAPI_ATTR void CmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo) {
     GetData(commandBuffer).dispatch.CmdBeginRendering(commandBuffer, pRenderingInfo);
 }
@@ -1853,6 +1946,46 @@
     GetData(commandBuffer).dispatch.CmdEndRendering(commandBuffer);
 }
 
+VKAPI_ATTR void GetImageSubresourceLayout2(VkDevice device, VkImage image, const VkImageSubresource2* pSubresource, VkSubresourceLayout2* pLayout) {
+    GetData(device).dispatch.GetImageSubresourceLayout2(device, image, pSubresource, pLayout);
+}
+
+VKAPI_ATTR void GetDeviceImageSubresourceLayout(VkDevice device, const VkDeviceImageSubresourceInfo* pInfo, VkSubresourceLayout2* pLayout) {
+    GetData(device).dispatch.GetDeviceImageSubresourceLayout(device, pInfo, pLayout);
+}
+
+VKAPI_ATTR VkResult MapMemory2(VkDevice device, const VkMemoryMapInfo* pMemoryMapInfo, void** ppData) {
+    return GetData(device).dispatch.MapMemory2(device, pMemoryMapInfo, ppData);
+}
+
+VKAPI_ATTR VkResult UnmapMemory2(VkDevice device, const VkMemoryUnmapInfo* pMemoryUnmapInfo) {
+    return GetData(device).dispatch.UnmapMemory2(device, pMemoryUnmapInfo);
+}
+
+VKAPI_ATTR void CmdBindDescriptorSets2(VkCommandBuffer commandBuffer, const VkBindDescriptorSetsInfo* pBindDescriptorSetsInfo) {
+    GetData(commandBuffer).dispatch.CmdBindDescriptorSets2(commandBuffer, pBindDescriptorSetsInfo);
+}
+
+VKAPI_ATTR void CmdPushConstants2(VkCommandBuffer commandBuffer, const VkPushConstantsInfo* pPushConstantsInfo) {
+    GetData(commandBuffer).dispatch.CmdPushConstants2(commandBuffer, pPushConstantsInfo);
+}
+
+VKAPI_ATTR void CmdPushDescriptorSet2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetInfo* pPushDescriptorSetInfo) {
+    GetData(commandBuffer).dispatch.CmdPushDescriptorSet2(commandBuffer, pPushDescriptorSetInfo);
+}
+
+VKAPI_ATTR void CmdPushDescriptorSetWithTemplate2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetWithTemplateInfo* pPushDescriptorSetWithTemplateInfo) {
+    GetData(commandBuffer).dispatch.CmdPushDescriptorSetWithTemplate2(commandBuffer, pPushDescriptorSetWithTemplateInfo);
+}
+
+VKAPI_ATTR void CmdSetRenderingAttachmentLocations(VkCommandBuffer commandBuffer, const VkRenderingAttachmentLocationInfo* pLocationInfo) {
+    GetData(commandBuffer).dispatch.CmdSetRenderingAttachmentLocations(commandBuffer, pLocationInfo);
+}
+
+VKAPI_ATTR void CmdSetRenderingInputAttachmentIndices(VkCommandBuffer commandBuffer, const VkRenderingInputAttachmentIndexInfo* pInputAttachmentIndexInfo) {
+    GetData(commandBuffer).dispatch.CmdSetRenderingInputAttachmentIndices(commandBuffer, pInputAttachmentIndexInfo);
+}
+
 
 }  // anonymous namespace
 
@@ -2299,6 +2432,11 @@
 }
 
 __attribute__((visibility("default")))
+VKAPI_ATTR void vkGetRenderingAreaGranularity(VkDevice device, const VkRenderingAreaInfo* pRenderingAreaInfo, VkExtent2D* pGranularity) {
+    vulkan::api::GetRenderingAreaGranularity(device, pRenderingAreaInfo, pGranularity);
+}
+
+__attribute__((visibility("default")))
 VKAPI_ATTR VkResult vkCreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool) {
     return vulkan::api::CreateCommandPool(device, pCreateInfo, pAllocator, pCommandPool);
 }
@@ -2649,6 +2787,11 @@
 }
 
 __attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdPushDescriptorSet(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites) {
+    vulkan::api::CmdPushDescriptorSet(commandBuffer, pipelineBindPoint, layout, set, descriptorWriteCount, pDescriptorWrites);
+}
+
+__attribute__((visibility("default")))
 VKAPI_ATTR void vkTrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags) {
     vulkan::api::TrimCommandPool(device, commandPool, flags);
 }
@@ -2734,6 +2877,11 @@
 }
 
 __attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdPushDescriptorSetWithTemplate(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplate descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData) {
+    vulkan::api::CmdPushDescriptorSetWithTemplate(commandBuffer, descriptorUpdateTemplate, layout, set, pData);
+}
+
+__attribute__((visibility("default")))
 VKAPI_ATTR void vkGetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
     vulkan::api::GetBufferMemoryRequirements2(device, pInfo, pMemoryRequirements);
 }
@@ -2854,6 +3002,11 @@
 }
 
 __attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetLineStipple(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor, uint16_t lineStipplePattern) {
+    vulkan::api::CmdSetLineStipple(commandBuffer, lineStippleFactor, lineStipplePattern);
+}
+
+__attribute__((visibility("default")))
 VKAPI_ATTR VkResult vkGetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties) {
     return vulkan::api::GetPhysicalDeviceToolProperties(physicalDevice, pToolCount, pToolProperties);
 }
@@ -2884,6 +3037,11 @@
 }
 
 __attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBindIndexBuffer2(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, VkIndexType indexType) {
+    vulkan::api::CmdBindIndexBuffer2(commandBuffer, buffer, offset, size, indexType);
+}
+
+__attribute__((visibility("default")))
 VKAPI_ATTR void vkCmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides) {
     vulkan::api::CmdBindVertexBuffers2(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets, pSizes, pStrides);
 }
@@ -3014,6 +3172,26 @@
 }
 
 __attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCopyMemoryToImage(VkDevice device, const VkCopyMemoryToImageInfo* pCopyMemoryToImageInfo) {
+    return vulkan::api::CopyMemoryToImage(device, pCopyMemoryToImageInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCopyImageToMemory(VkDevice device, const VkCopyImageToMemoryInfo* pCopyImageToMemoryInfo) {
+    return vulkan::api::CopyImageToMemory(device, pCopyImageToMemoryInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCopyImageToImage(VkDevice device, const VkCopyImageToImageInfo* pCopyImageToImageInfo) {
+    return vulkan::api::CopyImageToImage(device, pCopyImageToImageInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkTransitionImageLayout(VkDevice device, uint32_t transitionCount, const VkHostImageLayoutTransitionInfo* pTransitions) {
+    return vulkan::api::TransitionImageLayout(device, transitionCount, pTransitions);
+}
+
+__attribute__((visibility("default")))
 VKAPI_ATTR void vkCmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo) {
     vulkan::api::CmdBeginRendering(commandBuffer, pRenderingInfo);
 }
@@ -3023,4 +3201,54 @@
     vulkan::api::CmdEndRendering(commandBuffer);
 }
 
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetImageSubresourceLayout2(VkDevice device, VkImage image, const VkImageSubresource2* pSubresource, VkSubresourceLayout2* pLayout) {
+    vulkan::api::GetImageSubresourceLayout2(device, image, pSubresource, pLayout);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetDeviceImageSubresourceLayout(VkDevice device, const VkDeviceImageSubresourceInfo* pInfo, VkSubresourceLayout2* pLayout) {
+    vulkan::api::GetDeviceImageSubresourceLayout(device, pInfo, pLayout);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkMapMemory2(VkDevice device, const VkMemoryMapInfo* pMemoryMapInfo, void** ppData) {
+    return vulkan::api::MapMemory2(device, pMemoryMapInfo, ppData);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkUnmapMemory2(VkDevice device, const VkMemoryUnmapInfo* pMemoryUnmapInfo) {
+    return vulkan::api::UnmapMemory2(device, pMemoryUnmapInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBindDescriptorSets2(VkCommandBuffer commandBuffer, const VkBindDescriptorSetsInfo* pBindDescriptorSetsInfo) {
+    vulkan::api::CmdBindDescriptorSets2(commandBuffer, pBindDescriptorSetsInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdPushConstants2(VkCommandBuffer commandBuffer, const VkPushConstantsInfo* pPushConstantsInfo) {
+    vulkan::api::CmdPushConstants2(commandBuffer, pPushConstantsInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdPushDescriptorSet2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetInfo* pPushDescriptorSetInfo) {
+    vulkan::api::CmdPushDescriptorSet2(commandBuffer, pPushDescriptorSetInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdPushDescriptorSetWithTemplate2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetWithTemplateInfo* pPushDescriptorSetWithTemplateInfo) {
+    vulkan::api::CmdPushDescriptorSetWithTemplate2(commandBuffer, pPushDescriptorSetWithTemplateInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetRenderingAttachmentLocations(VkCommandBuffer commandBuffer, const VkRenderingAttachmentLocationInfo* pLocationInfo) {
+    vulkan::api::CmdSetRenderingAttachmentLocations(commandBuffer, pLocationInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetRenderingInputAttachmentIndices(VkCommandBuffer commandBuffer, const VkRenderingInputAttachmentIndexInfo* pInputAttachmentIndexInfo) {
+    vulkan::api::CmdSetRenderingInputAttachmentIndices(commandBuffer, pInputAttachmentIndexInfo);
+}
+
 // clang-format on
diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h
index b468a89..17dc62f 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -139,6 +139,7 @@
     PFN_vkCreateRenderPass CreateRenderPass;
     PFN_vkDestroyRenderPass DestroyRenderPass;
     PFN_vkGetRenderAreaGranularity GetRenderAreaGranularity;
+    PFN_vkGetRenderingAreaGranularity GetRenderingAreaGranularity;
     PFN_vkCreateCommandPool CreateCommandPool;
     PFN_vkDestroyCommandPool DestroyCommandPool;
     PFN_vkResetCommandPool ResetCommandPool;
@@ -196,6 +197,7 @@
     PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR;
     PFN_vkAcquireNextImageKHR AcquireNextImageKHR;
     PFN_vkQueuePresentKHR QueuePresentKHR;
+    PFN_vkCmdPushDescriptorSet CmdPushDescriptorSet;
     PFN_vkTrimCommandPool TrimCommandPool;
     PFN_vkGetDeviceGroupPeerMemoryFeatures GetDeviceGroupPeerMemoryFeatures;
     PFN_vkBindBufferMemory2 BindBufferMemory2;
@@ -208,6 +210,7 @@
     PFN_vkCreateDescriptorUpdateTemplate CreateDescriptorUpdateTemplate;
     PFN_vkDestroyDescriptorUpdateTemplate DestroyDescriptorUpdateTemplate;
     PFN_vkUpdateDescriptorSetWithTemplate UpdateDescriptorSetWithTemplate;
+    PFN_vkCmdPushDescriptorSetWithTemplate CmdPushDescriptorSetWithTemplate;
     PFN_vkGetBufferMemoryRequirements2 GetBufferMemoryRequirements2;
     PFN_vkGetImageMemoryRequirements2 GetImageMemoryRequirements2;
     PFN_vkGetImageSparseMemoryRequirements2 GetImageSparseMemoryRequirements2;
@@ -232,11 +235,13 @@
     PFN_vkGetBufferOpaqueCaptureAddress GetBufferOpaqueCaptureAddress;
     PFN_vkGetBufferDeviceAddress GetBufferDeviceAddress;
     PFN_vkGetDeviceMemoryOpaqueCaptureAddress GetDeviceMemoryOpaqueCaptureAddress;
+    PFN_vkCmdSetLineStipple CmdSetLineStipple;
     PFN_vkCmdSetCullMode CmdSetCullMode;
     PFN_vkCmdSetFrontFace CmdSetFrontFace;
     PFN_vkCmdSetPrimitiveTopology CmdSetPrimitiveTopology;
     PFN_vkCmdSetViewportWithCount CmdSetViewportWithCount;
     PFN_vkCmdSetScissorWithCount CmdSetScissorWithCount;
+    PFN_vkCmdBindIndexBuffer2 CmdBindIndexBuffer2;
     PFN_vkCmdBindVertexBuffers2 CmdBindVertexBuffers2;
     PFN_vkCmdSetDepthTestEnable CmdSetDepthTestEnable;
     PFN_vkCmdSetDepthWriteEnable CmdSetDepthWriteEnable;
@@ -263,8 +268,22 @@
     PFN_vkCmdPipelineBarrier2 CmdPipelineBarrier2;
     PFN_vkQueueSubmit2 QueueSubmit2;
     PFN_vkCmdWriteTimestamp2 CmdWriteTimestamp2;
+    PFN_vkCopyMemoryToImage CopyMemoryToImage;
+    PFN_vkCopyImageToMemory CopyImageToMemory;
+    PFN_vkCopyImageToImage CopyImageToImage;
+    PFN_vkTransitionImageLayout TransitionImageLayout;
     PFN_vkCmdBeginRendering CmdBeginRendering;
     PFN_vkCmdEndRendering CmdEndRendering;
+    PFN_vkGetImageSubresourceLayout2 GetImageSubresourceLayout2;
+    PFN_vkGetDeviceImageSubresourceLayout GetDeviceImageSubresourceLayout;
+    PFN_vkMapMemory2 MapMemory2;
+    PFN_vkUnmapMemory2 UnmapMemory2;
+    PFN_vkCmdBindDescriptorSets2 CmdBindDescriptorSets2;
+    PFN_vkCmdPushConstants2 CmdPushConstants2;
+    PFN_vkCmdPushDescriptorSet2 CmdPushDescriptorSet2;
+    PFN_vkCmdPushDescriptorSetWithTemplate2 CmdPushDescriptorSetWithTemplate2;
+    PFN_vkCmdSetRenderingAttachmentLocations CmdSetRenderingAttachmentLocations;
+    PFN_vkCmdSetRenderingInputAttachmentIndices CmdSetRenderingInputAttachmentIndices;
     // clang-format on
 };
 
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 01436db..7d0f545 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -398,7 +398,7 @@
                                      const VkAllocationCallbacks& allocator)
     : is_instance_(true),
       allocator_(allocator),
-      loader_api_version_(VK_API_VERSION_1_3),
+      loader_api_version_(flags::vulkan_1_4_instance_api() ? VK_API_VERSION_1_4 : VK_API_VERSION_1_3),
       icd_api_version_(icd_api_version),
       physical_dev_(VK_NULL_HANDLE),
       instance_info_(create_info),
@@ -410,7 +410,7 @@
                                      const VkAllocationCallbacks& allocator)
     : is_instance_(false),
       allocator_(allocator),
-      loader_api_version_(VK_API_VERSION_1_3),
+      loader_api_version_(flags::vulkan_1_4_instance_api() ? VK_API_VERSION_1_4 : VK_API_VERSION_1_3),
       icd_api_version_(icd_api_version),
       physical_dev_(physical_dev),
       dev_info_(create_info),
@@ -552,6 +552,10 @@
         is_instance_ ? loader_api_version_
                      : std::min(icd_api_version_, loader_api_version_);
     switch (api_version) {
+        case VK_API_VERSION_1_4:
+            hook_extensions_.set(ProcHook::EXTENSION_CORE_1_4);
+            hal_extensions_.set(ProcHook::EXTENSION_CORE_1_4);
+            [[clang::fallthrough]];
         case VK_API_VERSION_1_3:
             hook_extensions_.set(ProcHook::EXTENSION_CORE_1_3);
             hal_extensions_.set(ProcHook::EXTENSION_CORE_1_3);
@@ -701,6 +705,7 @@
             case ProcHook::EXTENSION_CORE_1_1:
             case ProcHook::EXTENSION_CORE_1_2:
             case ProcHook::EXTENSION_CORE_1_3:
+            case ProcHook::EXTENSION_CORE_1_4:
             case ProcHook::EXTENSION_COUNT:
                 // Device and meta extensions. If we ever get here it's a bug in
                 // our code. But enumerating them lets us avoid having a default
@@ -766,6 +771,7 @@
             case ProcHook::EXTENSION_CORE_1_1:
             case ProcHook::EXTENSION_CORE_1_2:
             case ProcHook::EXTENSION_CORE_1_3:
+            case ProcHook::EXTENSION_CORE_1_4:
             case ProcHook::EXTENSION_COUNT:
                 // Instance and meta extensions. If we ever get here it's a bug
                 // in our code. But enumerating them lets us avoid having a
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 649c0f1..204b16f 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -68,6 +68,7 @@
         EXTENSION_CORE_1_1,
         EXTENSION_CORE_1_2,
         EXTENSION_CORE_1_3,
+        EXTENSION_CORE_1_4,
         EXTENSION_COUNT,
         EXTENSION_UNKNOWN,
     };
diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt
index b189c68..ffe46f7 100644
--- a/vulkan/libvulkan/libvulkan.map.txt
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -6,32 +6,34 @@
     vkAllocateDescriptorSets;
     vkAllocateMemory;
     vkBeginCommandBuffer;
-    vkBindBufferMemory;
     vkBindBufferMemory2; # introduced=28
-    vkBindImageMemory;
+    vkBindBufferMemory;
     vkBindImageMemory2; # introduced=28
+    vkBindImageMemory;
     vkCmdBeginQuery;
-    vkCmdBeginRendering; # introduced=33
-    vkCmdBeginRenderPass;
     vkCmdBeginRenderPass2; # introduced=31
+    vkCmdBeginRenderPass;
+    vkCmdBeginRendering; # introduced=33
+    vkCmdBindDescriptorSets2; #introduced=36
     vkCmdBindDescriptorSets;
+    vkCmdBindIndexBuffer2; #introduced=36
     vkCmdBindIndexBuffer;
     vkCmdBindPipeline;
-    vkCmdBindVertexBuffers;
     vkCmdBindVertexBuffers2; #introduced=33
-    vkCmdBlitImage;
+    vkCmdBindVertexBuffers;
     vkCmdBlitImage2; #introduced=33
+    vkCmdBlitImage;
     vkCmdClearAttachments;
     vkCmdClearColorImage;
     vkCmdClearDepthStencilImage;
-    vkCmdCopyBuffer;
     vkCmdCopyBuffer2; #introduced=33
-    vkCmdCopyBufferToImage;
+    vkCmdCopyBuffer;
     vkCmdCopyBufferToImage2; #introduced=33
-    vkCmdCopyImage;
+    vkCmdCopyBufferToImage;
     vkCmdCopyImage2; #introduced=33
-    vkCmdCopyImageToBuffer;
+    vkCmdCopyImage;
     vkCmdCopyImageToBuffer2; #introduced=33
+    vkCmdCopyImageToBuffer;
     vkCmdCopyQueryPoolResults;
     vkCmdDispatch;
     vkCmdDispatchBase; # introduced=28
@@ -43,21 +45,26 @@
     vkCmdDrawIndirect;
     vkCmdDrawIndirectCount; # introduced=31
     vkCmdEndQuery;
-    vkCmdEndRendering; #introduced=33
-    vkCmdEndRenderPass;
     vkCmdEndRenderPass2; # introduced=31
+    vkCmdEndRenderPass;
+    vkCmdEndRendering; #introduced=33
     vkCmdExecuteCommands;
     vkCmdFillBuffer;
-    vkCmdNextSubpass;
     vkCmdNextSubpass2; # introduced=31
-    vkCmdPipelineBarrier;
+    vkCmdNextSubpass;
     vkCmdPipelineBarrier2; #introduced=33
+    vkCmdPipelineBarrier;
+    vkCmdPushConstants2; #introduced=36
     vkCmdPushConstants;
-    vkCmdResetEvent;
+    vkCmdPushDescriptorSet2; #introduced=36
+    vkCmdPushDescriptorSet; #introduced=36
+    vkCmdPushDescriptorSetWithTemplate2; #introduced=36
+    vkCmdPushDescriptorSetWithTemplate; #introduced=36
     vkCmdResetEvent2; #introduced=33
+    vkCmdResetEvent;
     vkCmdResetQueryPool;
-    vkCmdResolveImage;
     vkCmdResolveImage2; #introduced=33
+    vkCmdResolveImage;
     vkCmdSetBlendConstants;
     vkCmdSetCullMode; #introduced=33
     vkCmdSetDepthBias;
@@ -68,13 +75,16 @@
     vkCmdSetDepthTestEnable; #introduced=33
     vkCmdSetDepthWriteEnable; #introduced=33
     vkCmdSetDeviceMask; # introduced=28
-    vkCmdSetEvent;
     vkCmdSetEvent2; #introduced=33
+    vkCmdSetEvent;
     vkCmdSetFrontFace; #introduced=33
+    vkCmdSetLineStipple; #introduced=36
     vkCmdSetLineWidth;
     vkCmdSetPrimitiveRestartEnable; #introduced=33
     vkCmdSetPrimitiveTopology; #introduced=33
     vkCmdSetRasterizerDiscardEnable; #introduced=33
+    vkCmdSetRenderingAttachmentLocations; #introduced=36
+    vkCmdSetRenderingInputAttachmentIndices; #introduced=36
     vkCmdSetScissor;
     vkCmdSetScissorWithCount; #introduced=33
     vkCmdSetStencilCompareMask;
@@ -85,10 +95,12 @@
     vkCmdSetViewport;
     vkCmdSetViewportWithCount; #introduced=33
     vkCmdUpdateBuffer;
-    vkCmdWaitEvents;
     vkCmdWaitEvents2; #introduced=33
-    vkCmdWriteTimestamp;
+    vkCmdWaitEvents;
     vkCmdWriteTimestamp2; #introduced=33
+    vkCmdWriteTimestamp;
+    vkCopyImageToMemory; #introduced=36
+    vkCopyMemoryToImage; #introduced=36
     vkCreateAndroidSurfaceKHR;
     vkCreateBuffer;
     vkCreateBufferView;
@@ -109,8 +121,8 @@
     vkCreatePipelineLayout;
     vkCreatePrivateDataSlot; #introduced=33
     vkCreateQueryPool;
-    vkCreateRenderPass;
     vkCreateRenderPass2; # introduced=31
+    vkCreateRenderPass;
     vkCreateSampler;
     vkCreateSamplerYcbcrConversion; # introduced=28
     vkCreateSemaphore;
@@ -156,8 +168,8 @@
     vkFreeMemory;
     vkGetAndroidHardwareBufferPropertiesANDROID; # introduced=28
     vkGetBufferDeviceAddress; # introduced=31
-    vkGetBufferMemoryRequirements;
     vkGetBufferMemoryRequirements2; # introduced=28
+    vkGetBufferMemoryRequirements;
     vkGetBufferOpaqueCaptureAddress; # introduced=31
     vkGetDescriptorSetLayoutSupport; # introduced=28
     vkGetDeviceBufferMemoryRequirements; #introduced=33
@@ -166,39 +178,41 @@
     vkGetDeviceGroupSurfacePresentModesKHR; # introduced=28
     vkGetDeviceImageMemoryRequirements; #introduced=33
     vkGetDeviceImageSparseMemoryRequirements; #introduced=33
+    vkGetDeviceImageSubresourceLayout; #introduced=36
     vkGetDeviceMemoryCommitment;
     vkGetDeviceMemoryOpaqueCaptureAddress; # introduced=31
     vkGetDeviceProcAddr;
-    vkGetDeviceQueue;
     vkGetDeviceQueue2; # introduced=28
+    vkGetDeviceQueue;
     vkGetEventStatus;
     vkGetFenceStatus;
-    vkGetImageMemoryRequirements;
     vkGetImageMemoryRequirements2; # introduced=28
-    vkGetImageSparseMemoryRequirements;
+    vkGetImageMemoryRequirements;
     vkGetImageSparseMemoryRequirements2; # introduced=28
-    vkGetImageSubresourceLayout;
+    vkGetImageSparseMemoryRequirements;
+    vkGetImageSubresourceLayout2; #introduced=36
     vkGetImageSubresourceLayout2EXT; # introduced=UpsideDownCake
+    vkGetImageSubresourceLayout;
     vkGetInstanceProcAddr;
     vkGetMemoryAndroidHardwareBufferANDROID; # introduced=28
     vkGetPhysicalDeviceExternalBufferProperties; # introduced=28
     vkGetPhysicalDeviceExternalFenceProperties; # introduced=28
     vkGetPhysicalDeviceExternalSemaphoreProperties; # introduced=28
-    vkGetPhysicalDeviceFeatures;
     vkGetPhysicalDeviceFeatures2; # introduced=28
-    vkGetPhysicalDeviceFormatProperties;
+    vkGetPhysicalDeviceFeatures;
     vkGetPhysicalDeviceFormatProperties2; # introduced=28
-    vkGetPhysicalDeviceImageFormatProperties;
+    vkGetPhysicalDeviceFormatProperties;
     vkGetPhysicalDeviceImageFormatProperties2; # introduced=28
-    vkGetPhysicalDeviceMemoryProperties;
+    vkGetPhysicalDeviceImageFormatProperties;
     vkGetPhysicalDeviceMemoryProperties2; # introduced=28
+    vkGetPhysicalDeviceMemoryProperties;
     vkGetPhysicalDevicePresentRectanglesKHR; # introduced=28
-    vkGetPhysicalDeviceProperties;
     vkGetPhysicalDeviceProperties2; # introduced=28
-    vkGetPhysicalDeviceQueueFamilyProperties;
+    vkGetPhysicalDeviceProperties;
     vkGetPhysicalDeviceQueueFamilyProperties2; # introduced=28
-    vkGetPhysicalDeviceSparseImageFormatProperties;
+    vkGetPhysicalDeviceQueueFamilyProperties;
     vkGetPhysicalDeviceSparseImageFormatProperties2; # introduced=28
+    vkGetPhysicalDeviceSparseImageFormatProperties;
     vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
     vkGetPhysicalDeviceSurfaceFormatsKHR;
     vkGetPhysicalDeviceSurfacePresentModesKHR;
@@ -208,15 +222,17 @@
     vkGetPrivateData; #introduced=33
     vkGetQueryPoolResults;
     vkGetRenderAreaGranularity;
+    vkGetRenderingAreaGranularity; #introduced=36
     vkGetSemaphoreCounterValue; # introduced=31
     vkGetSwapchainImagesKHR;
     vkInvalidateMappedMemoryRanges;
+    vkMapMemory2; #introduced=36
     vkMapMemory;
     vkMergePipelineCaches;
     vkQueueBindSparse;
     vkQueuePresentKHR;
-    vkQueueSubmit;
     vkQueueSubmit2; #introduced=33
+    vkQueueSubmit;
     vkQueueWaitIdle;
     vkResetCommandBuffer;
     vkResetCommandPool;
@@ -227,10 +243,12 @@
     vkSetEvent;
     vkSetPrivateData; # introduced=33
     vkSignalSemaphore; # introduced=31
+    vkTransitionImageLayout; #introduced=36
     vkTrimCommandPool; # introduced=28
+    vkUnmapMemory2; #introduced=36
     vkUnmapMemory;
-    vkUpdateDescriptorSets;
     vkUpdateDescriptorSetWithTemplate; # introduced=28
+    vkUpdateDescriptorSets;
     vkWaitForFences;
     vkWaitSemaphores; # introduced=31
   local:
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 973e71c..48de3d6 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -1767,6 +1767,69 @@
     return VK_SUCCESS;
 }
 
+void GetRenderingAreaGranularity(VkDevice device, const VkRenderingAreaInfo* pRenderingAreaInfo, VkExtent2D* pGranularity) {
+}
+
+void CmdPushDescriptorSet(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites) {
+}
+
+void CmdPushDescriptorSetWithTemplate(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplate descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData) {
+}
+
+void CmdSetLineStipple(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor, uint16_t lineStipplePattern) {
+}
+
+void CmdBindIndexBuffer2(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, VkIndexType indexType) {
+}
+
+VkResult CopyMemoryToImage(VkDevice device, const VkCopyMemoryToImageInfo* pCopyMemoryToImageInfo) {
+    return VK_SUCCESS;
+}
+
+VkResult CopyImageToMemory(VkDevice device, const VkCopyImageToMemoryInfo* pCopyImageToMemoryInfo) {
+    return VK_SUCCESS;
+}
+
+VkResult CopyImageToImage(VkDevice device, const VkCopyImageToImageInfo* pCopyImageToImageInfo) {
+    return VK_SUCCESS;
+}
+
+VkResult TransitionImageLayout(VkDevice device, uint32_t transitionCount, const VkHostImageLayoutTransitionInfo* pTransitions) {
+    return VK_SUCCESS;
+}
+
+void GetImageSubresourceLayout2(VkDevice device, VkImage image, const VkImageSubresource2* pSubresource, VkSubresourceLayout2* pLayout) {
+}
+
+void GetDeviceImageSubresourceLayout(VkDevice device, const VkDeviceImageSubresourceInfo* pInfo, VkSubresourceLayout2* pLayout) {
+}
+
+VkResult MapMemory2(VkDevice device, const VkMemoryMapInfo* pMemoryMapInfo, void** ppData) {
+    return VK_SUCCESS;
+}
+
+VkResult UnmapMemory2(VkDevice device, const VkMemoryUnmapInfo* pMemoryUnmapInfo) {
+    return VK_SUCCESS;
+}
+
+void CmdBindDescriptorSets2(VkCommandBuffer commandBuffer, const VkBindDescriptorSetsInfo* pBindDescriptorSetsInfo) {
+}
+
+void CmdPushConstants2(VkCommandBuffer commandBuffer, const VkPushConstantsInfo* pPushConstantsInfo) {
+}
+
+void CmdPushDescriptorSet2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetInfo* pPushDescriptorSetInfo) {
+}
+
+void CmdPushDescriptorSetWithTemplate2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetWithTemplateInfo* pPushDescriptorSetWithTemplateInfo) {
+}
+
+void CmdSetRenderingAttachmentLocations(VkCommandBuffer commandBuffer, const VkRenderingAttachmentLocationInfo* pLocationInfo) {
+}
+
+void CmdSetRenderingInputAttachmentIndices(VkCommandBuffer commandBuffer, const VkRenderingInputAttachmentIndexInfo* pInputAttachmentIndexInfo) {
+}
+
 #pragma clang diagnostic pop
 // clang-format on
 
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index 40a45af..30967c2 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -75,7 +75,9 @@
     {"vkCmdBeginRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRenderPass2>(CmdBeginRenderPass2))},
     {"vkCmdBeginRendering", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRendering>(CmdBeginRendering))},
     {"vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindDescriptorSets>(CmdBindDescriptorSets))},
+    {"vkCmdBindDescriptorSets2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindDescriptorSets2>(CmdBindDescriptorSets2))},
     {"vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindIndexBuffer>(CmdBindIndexBuffer))},
+    {"vkCmdBindIndexBuffer2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindIndexBuffer2>(CmdBindIndexBuffer2))},
     {"vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindPipeline>(CmdBindPipeline))},
     {"vkCmdBindVertexBuffers", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindVertexBuffers>(CmdBindVertexBuffers))},
     {"vkCmdBindVertexBuffers2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindVertexBuffers2>(CmdBindVertexBuffers2))},
@@ -113,6 +115,11 @@
     {"vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPipelineBarrier>(CmdPipelineBarrier))},
     {"vkCmdPipelineBarrier2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPipelineBarrier2>(CmdPipelineBarrier2))},
     {"vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushConstants>(CmdPushConstants))},
+    {"vkCmdPushConstants2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushConstants2>(CmdPushConstants2))},
+    {"vkCmdPushDescriptorSet", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushDescriptorSet>(CmdPushDescriptorSet))},
+    {"vkCmdPushDescriptorSet2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushDescriptorSet2>(CmdPushDescriptorSet2))},
+    {"vkCmdPushDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushDescriptorSetWithTemplate>(CmdPushDescriptorSetWithTemplate))},
+    {"vkCmdPushDescriptorSetWithTemplate2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushDescriptorSetWithTemplate2>(CmdPushDescriptorSetWithTemplate2))},
     {"vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetEvent>(CmdResetEvent))},
     {"vkCmdResetEvent2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetEvent2>(CmdResetEvent2))},
     {"vkCmdResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetQueryPool>(CmdResetQueryPool))},
@@ -131,10 +138,13 @@
     {"vkCmdSetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetEvent>(CmdSetEvent))},
     {"vkCmdSetEvent2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetEvent2>(CmdSetEvent2))},
     {"vkCmdSetFrontFace", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetFrontFace>(CmdSetFrontFace))},
+    {"vkCmdSetLineStipple", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetLineStipple>(CmdSetLineStipple))},
     {"vkCmdSetLineWidth", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetLineWidth>(CmdSetLineWidth))},
     {"vkCmdSetPrimitiveRestartEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetPrimitiveRestartEnable>(CmdSetPrimitiveRestartEnable))},
     {"vkCmdSetPrimitiveTopology", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetPrimitiveTopology>(CmdSetPrimitiveTopology))},
     {"vkCmdSetRasterizerDiscardEnable", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetRasterizerDiscardEnable>(CmdSetRasterizerDiscardEnable))},
+    {"vkCmdSetRenderingAttachmentLocations", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetRenderingAttachmentLocations>(CmdSetRenderingAttachmentLocations))},
+    {"vkCmdSetRenderingInputAttachmentIndices", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetRenderingInputAttachmentIndices>(CmdSetRenderingInputAttachmentIndices))},
     {"vkCmdSetScissor", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetScissor>(CmdSetScissor))},
     {"vkCmdSetScissorWithCount", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetScissorWithCount>(CmdSetScissorWithCount))},
     {"vkCmdSetStencilCompareMask", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdSetStencilCompareMask>(CmdSetStencilCompareMask))},
@@ -149,6 +159,9 @@
     {"vkCmdWaitEvents2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdWaitEvents2>(CmdWaitEvents2))},
     {"vkCmdWriteTimestamp", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdWriteTimestamp>(CmdWriteTimestamp))},
     {"vkCmdWriteTimestamp2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdWriteTimestamp2>(CmdWriteTimestamp2))},
+    {"vkCopyImageToImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCopyImageToImage>(CopyImageToImage))},
+    {"vkCopyImageToMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCopyImageToMemory>(CopyImageToMemory))},
+    {"vkCopyMemoryToImage", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCopyMemoryToImage>(CopyMemoryToImage))},
     {"vkCreateBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateBuffer>(CreateBuffer))},
     {"vkCreateBufferView", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateBufferView>(CreateBufferView))},
     {"vkCreateCommandPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateCommandPool>(CreateCommandPool))},
@@ -222,6 +235,7 @@
     {"vkGetDeviceGroupPeerMemoryFeatures", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceGroupPeerMemoryFeatures>(GetDeviceGroupPeerMemoryFeatures))},
     {"vkGetDeviceImageMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceImageMemoryRequirements>(GetDeviceImageMemoryRequirements))},
     {"vkGetDeviceImageSparseMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceImageSparseMemoryRequirements>(GetDeviceImageSparseMemoryRequirements))},
+    {"vkGetDeviceImageSubresourceLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceImageSubresourceLayout>(GetDeviceImageSubresourceLayout))},
     {"vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceMemoryCommitment>(GetDeviceMemoryCommitment))},
     {"vkGetDeviceMemoryOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceMemoryOpaqueCaptureAddress>(GetDeviceMemoryOpaqueCaptureAddress))},
     {"vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceProcAddr>(GetDeviceProcAddr))},
@@ -234,6 +248,7 @@
     {"vkGetImageSparseMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetImageSparseMemoryRequirements>(GetImageSparseMemoryRequirements))},
     {"vkGetImageSparseMemoryRequirements2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetImageSparseMemoryRequirements2>(GetImageSparseMemoryRequirements2))},
     {"vkGetImageSubresourceLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetImageSubresourceLayout>(GetImageSubresourceLayout))},
+    {"vkGetImageSubresourceLayout2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetImageSubresourceLayout2>(GetImageSubresourceLayout2))},
     {"vkGetInstanceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetInstanceProcAddr>(GetInstanceProcAddr))},
     {"vkGetPhysicalDeviceExternalBufferProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceExternalBufferProperties>(GetPhysicalDeviceExternalBufferProperties))},
     {"vkGetPhysicalDeviceExternalFenceProperties", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPhysicalDeviceExternalFenceProperties>(GetPhysicalDeviceExternalFenceProperties))},
@@ -264,6 +279,7 @@
     {"vkGetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPrivateData>(GetPrivateData))},
     {"vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetQueryPoolResults>(GetQueryPoolResults))},
     {"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))},
+    {"vkGetRenderingAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderingAreaGranularity>(GetRenderingAreaGranularity))},
     {"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))},
@@ -271,6 +287,7 @@
     {"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))},
+    {"vkMapMemory2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory2>(MapMemory2))},
     {"vkMergePipelineCaches", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMergePipelineCaches>(MergePipelineCaches))},
     {"vkQueueBindSparse", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueBindSparse>(QueueBindSparse))},
     {"vkQueueSignalReleaseImageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkQueueSignalReleaseImageANDROID>(QueueSignalReleaseImageANDROID))},
@@ -286,8 +303,10 @@
     {"vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSetEvent>(SetEvent))},
     {"vkSetPrivateData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSetPrivateData>(SetPrivateData))},
     {"vkSignalSemaphore", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSignalSemaphore>(SignalSemaphore))},
+    {"vkTransitionImageLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkTransitionImageLayout>(TransitionImageLayout))},
     {"vkTrimCommandPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkTrimCommandPool>(TrimCommandPool))},
     {"vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUnmapMemory>(UnmapMemory))},
+    {"vkUnmapMemory2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUnmapMemory2>(UnmapMemory2))},
     {"vkUpdateDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUpdateDescriptorSetWithTemplate>(UpdateDescriptorSetWithTemplate))},
     {"vkUpdateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUpdateDescriptorSets>(UpdateDescriptorSets))},
     {"vkWaitForFences", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkWaitForFences>(WaitForFences))},
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 0d1e223..f609e7e 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -118,6 +118,7 @@
 VKAPI_ATTR VkResult CreateRenderPass(VkDevice device, const VkRenderPassCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
 VKAPI_ATTR void DestroyRenderPass(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR void GetRenderAreaGranularity(VkDevice device, VkRenderPass renderPass, VkExtent2D* pGranularity);
+VKAPI_ATTR void GetRenderingAreaGranularity(VkDevice device, const VkRenderingAreaInfo* pRenderingAreaInfo, VkExtent2D* pGranularity);
 VKAPI_ATTR VkResult CreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool);
 VKAPI_ATTR void DestroyCommandPool(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR VkResult ResetCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags);
@@ -187,6 +188,7 @@
 VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties);
 VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties);
 VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties);
+VKAPI_ATTR void CmdPushDescriptorSet(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites);
 VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags);
 VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties);
 VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties);
@@ -200,6 +202,7 @@
 VKAPI_ATTR VkResult CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate);
 VKAPI_ATTR void DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData);
+VKAPI_ATTR void CmdPushDescriptorSetWithTemplate(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplate descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData);
 VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
 VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
 VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements);
@@ -228,12 +231,14 @@
 VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
 VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
 VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo);
+VKAPI_ATTR void CmdSetLineStipple(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor, uint16_t lineStipplePattern);
 VKAPI_ATTR VkResult GetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, VkPhysicalDeviceToolProperties* pToolProperties);
 VKAPI_ATTR void CmdSetCullMode(VkCommandBuffer commandBuffer, VkCullModeFlags cullMode);
 VKAPI_ATTR void CmdSetFrontFace(VkCommandBuffer commandBuffer, VkFrontFace frontFace);
 VKAPI_ATTR void CmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, VkPrimitiveTopology primitiveTopology);
 VKAPI_ATTR void CmdSetViewportWithCount(VkCommandBuffer commandBuffer, uint32_t viewportCount, const VkViewport* pViewports);
 VKAPI_ATTR void CmdSetScissorWithCount(VkCommandBuffer commandBuffer, uint32_t scissorCount, const VkRect2D* pScissors);
+VKAPI_ATTR void CmdBindIndexBuffer2(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, VkIndexType indexType);
 VKAPI_ATTR void CmdBindVertexBuffers2(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes, const VkDeviceSize* pStrides);
 VKAPI_ATTR void CmdSetDepthTestEnable(VkCommandBuffer commandBuffer, VkBool32 depthTestEnable);
 VKAPI_ATTR void CmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, VkBool32 depthWriteEnable);
@@ -260,8 +265,22 @@
 VKAPI_ATTR void CmdPipelineBarrier2(VkCommandBuffer commandBuffer, const VkDependencyInfo* pDependencyInfo);
 VKAPI_ATTR VkResult QueueSubmit2(VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence);
 VKAPI_ATTR void CmdWriteTimestamp2(VkCommandBuffer commandBuffer, VkPipelineStageFlags2 stage, VkQueryPool queryPool, uint32_t query);
+VKAPI_ATTR VkResult CopyMemoryToImage(VkDevice device, const VkCopyMemoryToImageInfo* pCopyMemoryToImageInfo);
+VKAPI_ATTR VkResult CopyImageToMemory(VkDevice device, const VkCopyImageToMemoryInfo* pCopyImageToMemoryInfo);
+VKAPI_ATTR VkResult CopyImageToImage(VkDevice device, const VkCopyImageToImageInfo* pCopyImageToImageInfo);
+VKAPI_ATTR VkResult TransitionImageLayout(VkDevice device, uint32_t transitionCount, const VkHostImageLayoutTransitionInfo* pTransitions);
 VKAPI_ATTR void CmdBeginRendering(VkCommandBuffer commandBuffer, const VkRenderingInfo* pRenderingInfo);
 VKAPI_ATTR void CmdEndRendering(VkCommandBuffer commandBuffer);
+VKAPI_ATTR void GetImageSubresourceLayout2(VkDevice device, VkImage image, const VkImageSubresource2* pSubresource, VkSubresourceLayout2* pLayout);
+VKAPI_ATTR void GetDeviceImageSubresourceLayout(VkDevice device, const VkDeviceImageSubresourceInfo* pInfo, VkSubresourceLayout2* pLayout);
+VKAPI_ATTR VkResult MapMemory2(VkDevice device, const VkMemoryMapInfo* pMemoryMapInfo, void** ppData);
+VKAPI_ATTR VkResult UnmapMemory2(VkDevice device, const VkMemoryUnmapInfo* pMemoryUnmapInfo);
+VKAPI_ATTR void CmdBindDescriptorSets2(VkCommandBuffer commandBuffer, const VkBindDescriptorSetsInfo* pBindDescriptorSetsInfo);
+VKAPI_ATTR void CmdPushConstants2(VkCommandBuffer commandBuffer, const VkPushConstantsInfo* pPushConstantsInfo);
+VKAPI_ATTR void CmdPushDescriptorSet2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetInfo* pPushDescriptorSetInfo);
+VKAPI_ATTR void CmdPushDescriptorSetWithTemplate2(VkCommandBuffer commandBuffer, const VkPushDescriptorSetWithTemplateInfo* pPushDescriptorSetWithTemplateInfo);
+VKAPI_ATTR void CmdSetRenderingAttachmentLocations(VkCommandBuffer commandBuffer, const VkRenderingAttachmentLocationInfo* pLocationInfo);
+VKAPI_ATTR void CmdSetRenderingInputAttachmentIndices(VkCommandBuffer commandBuffer, const VkRenderingInputAttachmentIndexInfo* pInputAttachmentIndexInfo);
 // clang-format on
 
 }  // namespace null_driver
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index 6b4cbad..aa1e7f2 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -379,6 +379,9 @@
                 version_dict[cmd_name] = apiversion
 
   for feature in root.iter('feature'):
+    # hack, 'feature' element has multiple meanings.. should be more precise with path match
+    if feature.get('api') is None:
+      continue
     if 'vulkan' not in feature.get('api').split(','):
       continue
 
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index bfb7bd6..9b508aa 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -1050,6 +1050,9 @@
   bool ret = true;
   switch (device->properties.apiVersion ^
           VK_API_VERSION_PATCH(device->properties.apiVersion)) {
+    case VK_API_VERSION_1_4:
+      // TODO: real 1.4 support here
+      FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_3:
       ret &= visitor->Visit("core13", &device->core13);
       FALLTHROUGH_INTENDED;
@@ -1110,6 +1113,8 @@
 inline bool Iterate(Visitor* visitor, VkJsonInstance* instance) {
   bool ret = true;
   switch (instance->api_version ^ VK_API_VERSION_PATCH(instance->api_version)) {
+    case VK_API_VERSION_1_4:
+      FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_3:
       FALLTHROUGH_INTENDED;
     case VK_API_VERSION_1_2: