Merge "Disconnect from surface when the RenderSurface object is removed." into qt-dev
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index a5cc0df..dbb4f22 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -534,7 +534,7 @@
         }
         AddRuntimeArg(target_sdk_version_arg);
         if (enable_hidden_api_checks) {
-            AddRuntimeArg("-Xhidden-api-checks");
+            AddRuntimeArg("-Xhidden-api-policy:enabled");
         }
 
         if (dex_metadata_fd > -1) {
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index b1c577e..70ed80d 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -66,15 +66,12 @@
     const int LOCATION_VENDOR = 0x2;
     /*  ApplicationInfo.isProduct() == true */
     const int LOCATION_PRODUCT = 0x4;
-    /*  ApplicationInfo.isProductServices() == true */
-    const int LOCATION_PRODUCT_SERVICES = 0x8;
 
     /**
      * Returns a set of bitflags about package location.
      * LOCATION_SYSTEM: getApplicationInfo(packageName).isSystemApp()
      * LOCATION_VENDOR: getApplicationInfo(packageName).isVendor()
      * LOCATION_PRODUCT: getApplicationInfo(packageName).isProduct()
-     * LOCATION_PRODUCT_SERVICES: getApplicationInfo(packageName).isProductService()
      */
     int getLocationFlags(in @utf8InCpp String packageName);
 }
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 7e2b03d..16f6729 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -86,8 +86,9 @@
     mHighRefreshRateOffsets.late = {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs};
 }
 
-PhaseOffsets::Offsets PhaseOffsets::getCurrentOffsets() const {
-    switch (mRefreshRateType) {
+PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(
+        android::scheduler::RefreshRateConfigs::RefreshRateType refreshRateType) const {
+    switch (refreshRateType) {
         case RefreshRateConfigs::RefreshRateType::PERFORMANCE:
             return mHighRefreshRateOffsets;
         default:
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index a85660a..08747a5 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -42,6 +42,8 @@
 
     virtual nsecs_t getCurrentAppOffset() = 0;
     virtual nsecs_t getCurrentSfOffset() = 0;
+    virtual Offsets getOffsetsForRefreshRate(
+            RefreshRateConfigs::RefreshRateType refreshRateType) const = 0;
     virtual Offsets getCurrentOffsets() const = 0;
     virtual void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) = 0;
     virtual void dump(std::string& result) const = 0;
@@ -55,8 +57,14 @@
     nsecs_t getCurrentAppOffset() override;
     nsecs_t getCurrentSfOffset() override;
 
+    // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
+    Offsets getOffsetsForRefreshRate(
+            RefreshRateConfigs::RefreshRateType refreshRateType) const override;
+
     // Returns early, early GL, and late offsets for Apps and SF.
-    Offsets getCurrentOffsets() const override;
+    Offsets getCurrentOffsets() const override {
+        return getOffsetsForRefreshRate(mRefreshRateType);
+    }
 
     // This function should be called when the device is switching between different
     // refresh rates, to properly update the offsets.
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index eb9ae35..d730058 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -47,6 +47,8 @@
         std::string name;
         // Refresh rate in frames per second, rounded to the nearest integer.
         uint32_t fps = 0;
+        // config Id (returned from HWC2::Display::Config::getId())
+        hwc2_config_t id;
     };
 
     // TODO(b/122916473): Get this information from configs prepared by vendors, instead of
@@ -62,13 +64,24 @@
         return nullptr;
     }
 
+    RefreshRateType getRefreshRateType(hwc2_config_t id) const {
+        for (const auto& [type, refreshRate] : mRefreshRates) {
+            if (refreshRate->id == id) {
+                return type;
+            }
+        }
+
+        return RefreshRateType::DEFAULT;
+    }
+
     void populate(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
         mRefreshRates.clear();
 
         // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
         mRefreshRates.emplace(RefreshRateType::POWER_SAVING,
                               std::make_shared<RefreshRate>(
-                                      RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0}));
+                                      RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0,
+                                                  HWC2_SCREEN_OFF_CONFIG_ID}));
 
         if (configs.size() < 1) {
             ALOGE("Device does not have valid configs. Config size is 0.");
@@ -91,11 +104,12 @@
         nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second;
         if (vsyncPeriod != 0) {
             const float fps = 1e9 / vsyncPeriod;
+            const int configId = configIdToVsyncPeriod[0].first;
             mRefreshRates.emplace(RefreshRateType::DEFAULT,
                                   std::make_shared<RefreshRate>(
-                                          RefreshRate{configIdToVsyncPeriod[0].first,
-                                                      base::StringPrintf("%2.ffps", fps),
-                                                      static_cast<uint32_t>(fps)}));
+                                          RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
+                                                      static_cast<uint32_t>(fps),
+                                                      configs.at(configId)->getId()}));
         }
 
         if (configs.size() < 2) {
@@ -107,11 +121,12 @@
         vsyncPeriod = configIdToVsyncPeriod[1].second;
         if (vsyncPeriod != 0) {
             const float fps = 1e9 / vsyncPeriod;
+            const int configId = configIdToVsyncPeriod[1].first;
             mRefreshRates.emplace(RefreshRateType::PERFORMANCE,
                                   std::make_shared<RefreshRate>(
-                                          RefreshRate{configIdToVsyncPeriod[1].first,
-                                                      base::StringPrintf("%2.ffps", fps),
-                                                      static_cast<uint32_t>(fps)}));
+                                          RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
+                                                      static_cast<uint32_t>(fps),
+                                                      configs.at(configId)->getId()}));
         }
     }
 
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index a935cac..3bf3922 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -34,6 +34,7 @@
 // the config is not visible to SF, and is completely maintained by HWC. However, we would
 // still like to keep track of time when the device is in this config.
 static constexpr int SCREEN_OFF_CONFIG_ID = -1;
+static constexpr uint32_t HWC2_SCREEN_OFF_CONFIG_ID = 0xffffffff;
 
 // This number is used when we try to determine how long does a given layer stay relevant.
 // Currently it is set to 100ms, because that would indicate 10Hz rendering.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 53c615f..6063555 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -860,7 +860,9 @@
         info.xdpi = xdpi;
         info.ydpi = ydpi;
         info.fps = 1e9 / hwConfig->getVsyncPeriod();
-        info.appVsyncOffset = mPhaseOffsets->getCurrentAppOffset();
+        const auto refreshRateType = mRefreshRateConfigs.getRefreshRateType(hwConfig->getId());
+        const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(refreshRateType);
+        info.appVsyncOffset = offset.late.app;
 
         // This is how far in advance a buffer must be queued for
         // presentation at a given time.  If you want a buffer to appear
@@ -874,8 +876,7 @@
         //
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
-        info.presentationDeadline =
-                hwConfig->getVsyncPeriod() - mPhaseOffsets->getCurrentSfOffset() + 1000000;
+        info.presentationDeadline = hwConfig->getVsyncPeriod() - offset.late.sf + 1000000;
 
         // All non-virtual displays are currently considered secure.
         info.secure = true;
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 6b2b583..34df606 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -144,6 +144,9 @@
         } else {
             ALOGW("cannot find listener in mPendingTransactions");
         }
+        if (listener->second.size() == 0) {
+            mPendingTransactions.erase(listener);
+        }
 
         status_t err = addCallbackHandle(handle);
         if (err != NO_ERROR) {
@@ -231,7 +234,9 @@
 
                 // If we are still waiting on the callback handles for this transaction, stop
                 // here because all transaction callbacks for the same listener must come in order
-                if (mPendingTransactions[listener].count(transactionStats.callbackIds) != 0) {
+                auto pendingTransactions = mPendingTransactions.find(listener);
+                if (pendingTransactions != mPendingTransactions.end() &&
+                    pendingTransactions->second.count(transactionStats.callbackIds) != 0) {
                     break;
                 }
 
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
index 0739f15..cfa8337 100644
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -33,6 +33,11 @@
     nsecs_t getCurrentAppOffset() override { return FAKE_PHASE_OFFSET_NS; }
     nsecs_t getCurrentSfOffset() override { return FAKE_PHASE_OFFSET_NS; }
 
+    PhaseOffsets::Offsets getOffsetsForRefreshRate(
+            RefreshRateConfigs::RefreshRateType /*refreshRateType*/) const override {
+        return getCurrentOffsets();
+    }
+
     // Returns early, early GL, and late offsets for Apps and SF.
     PhaseOffsets::Offsets getCurrentOffsets() const override {
         return Offsets{{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 8b37c22..5067fe8 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -37,7 +37,9 @@
 class RefreshRateConfigsTest : public testing::Test {
 protected:
     static constexpr int CONFIG_ID_60 = 0;
+    static constexpr hwc2_config_t HWC2_CONFIG_ID_60 = 0;
     static constexpr int CONFIG_ID_90 = 1;
+    static constexpr hwc2_config_t HWC2_CONFIG_ID_90 = 1;
     static constexpr int64_t VSYNC_60 = 16666667;
     static constexpr int64_t VSYNC_90 = 11111111;
 
@@ -81,7 +83,8 @@
     ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
     ASSERT_EQ(rates.end(), rates.find(RefreshRateType::DEFAULT));
 
-    RefreshRate expectedConfig = RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0};
+    RefreshRate expectedConfig =
+            RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
     assertRatesEqual(expectedConfig, *powerSavingRate->second);
 
     ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
@@ -109,9 +112,10 @@
     ASSERT_NE(rates.end(), defaultRate);
     ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
 
-    RefreshRate expectedPowerSavingConfig = RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0};
+    RefreshRate expectedPowerSavingConfig =
+            RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
     assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
-    RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60};
+    RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60};
     assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
 
     ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
@@ -145,11 +149,13 @@
     ASSERT_NE(rates.end(), defaultRate);
     ASSERT_NE(rates.end(), performanceRate);
 
-    RefreshRate expectedPowerSavingConfig = RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0};
+    RefreshRate expectedPowerSavingConfig =
+            RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
     assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
-    RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60};
+    RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60};
     assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
-    RefreshRate expectedPerformanceConfig = RefreshRate{CONFIG_ID_90, "90fps", 90};
+    RefreshRate expectedPerformanceConfig =
+            RefreshRate{CONFIG_ID_90, "90fps", 90, HWC2_CONFIG_ID_90};
     assertRatesEqual(expectedPerformanceConfig, *performanceRate->second);
 
     ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index f04eb03..bdd3573 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -703,6 +703,7 @@
 {{Macro "driver.InterceptedExtensions"}}
 VK_KHR_get_physical_device_properties2
 VK_ANDROID_external_memory_android_hardware_buffer
+VK_KHR_bind_memory2
 {{end}}
 
 
@@ -750,6 +751,9 @@
     {{else if eq $.Name "vkGetInstanceProcAddr"}}true
     {{else if eq $.Name "vkGetDeviceProcAddr"}}true
 
+    {{/* VK_KHR_swapchain v69 requirement */}}
+    {{else if eq $.Name "vkBindImageMemory2"}}true
+    {{else if eq $.Name "vkBindImageMemory2KHR"}}true
     {{end}}
 
     {{$ext := GetAnnotation $ "extension"}}
@@ -985,6 +989,10 @@
     {{else if eq $.Name "vkGetPhysicalDeviceProperties"}}true
     {{else if eq $.Name "vkGetPhysicalDeviceProperties2"}}true
     {{else if eq $.Name "vkGetPhysicalDeviceProperties2KHR"}}true
+
+    {{/* VK_KHR_swapchain v69 requirement */}}
+    {{else if eq $.Name "vkBindImageMemory2"}}true
+    {{else if eq $.Name "vkBindImageMemory2KHR"}}true
     {{end}}
 
     {{$ext := GetAnnotation $ "extension"}}
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index b3259de..491d4d1 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -537,6 +537,7 @@
                 // Extensions we don't need to do anything about at this level
                 break;
 
+            case ProcHook::KHR_bind_memory2:
             case ProcHook::KHR_incremental_present:
             case ProcHook::KHR_shared_presentable_image:
             case ProcHook::KHR_swapchain:
@@ -577,6 +578,7 @@
                 // return now as these extensions do not require HAL support
                 return;
             case ProcHook::EXT_hdr_metadata:
+            case ProcHook::KHR_bind_memory2:
                 hook_extensions_.set(ext_bit);
                 break;
             case ProcHook::ANDROID_external_memory_android_hardware_buffer:
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index ec98b9f..574c327 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -137,6 +137,15 @@
     }
 }
 
+VKAPI_ATTR VkResult checkedBindImageMemory2KHR(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfoKHR* pBindInfos) {
+    if (GetData(device).hook_extensions[ProcHook::KHR_bind_memory2]) {
+        return BindImageMemory2KHR(device, bindInfoCount, pBindInfos);
+    } else {
+        Logger(device).Err(device, "VK_KHR_bind_memory2 not enabled. vkBindImageMemory2KHR not executed.");
+        return VK_SUCCESS;
+    }
+}
+
 // clang-format on
 
 const ProcHook g_proc_hooks[] = {
@@ -170,6 +179,20 @@
         nullptr,
     },
     {
+        "vkBindImageMemory2",
+        ProcHook::DEVICE,
+        ProcHook::EXTENSION_CORE,
+        reinterpret_cast<PFN_vkVoidFunction>(BindImageMemory2),
+        nullptr,
+    },
+    {
+        "vkBindImageMemory2KHR",
+        ProcHook::DEVICE,
+        ProcHook::KHR_bind_memory2,
+        reinterpret_cast<PFN_vkVoidFunction>(BindImageMemory2KHR),
+        reinterpret_cast<PFN_vkVoidFunction>(checkedBindImageMemory2KHR),
+    },
+    {
         "vkCreateAndroidSurfaceKHR",
         ProcHook::INSTANCE,
         ProcHook::KHR_android_surface,
@@ -458,6 +481,7 @@
     if (strcmp(name, "VK_KHR_get_surface_capabilities2") == 0) return ProcHook::KHR_get_surface_capabilities2;
     if (strcmp(name, "VK_KHR_get_physical_device_properties2") == 0) return ProcHook::KHR_get_physical_device_properties2;
     if (strcmp(name, "VK_ANDROID_external_memory_android_hardware_buffer") == 0) return ProcHook::ANDROID_external_memory_android_hardware_buffer;
+    if (strcmp(name, "VK_KHR_bind_memory2") == 0) return ProcHook::KHR_bind_memory2;
     // clang-format on
     return ProcHook::EXTENSION_UNKNOWN;
 }
@@ -517,11 +541,13 @@
     INIT_PROC(true, dev, CreateImage);
     INIT_PROC(true, dev, DestroyImage);
     INIT_PROC(true, dev, AllocateCommandBuffers);
+    INIT_PROC(false, dev, BindImageMemory2);
     INIT_PROC(false, dev, GetDeviceQueue2);
     INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID);
     INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
     INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID);
     INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID);
+    INIT_PROC_EXT(KHR_bind_memory2, true, dev, BindImageMemory2KHR);
     // clang-format on
 
     return success;
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 14c3aba..3faf6c0 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -46,6 +46,7 @@
         KHR_get_surface_capabilities2,
         KHR_get_physical_device_properties2,
         ANDROID_external_memory_android_hardware_buffer,
+        KHR_bind_memory2,
 
         EXTENSION_CORE,  // valid bit
         EXTENSION_COUNT,
@@ -85,11 +86,13 @@
     PFN_vkCreateImage CreateImage;
     PFN_vkDestroyImage DestroyImage;
     PFN_vkAllocateCommandBuffers AllocateCommandBuffers;
+    PFN_vkBindImageMemory2 BindImageMemory2;
     PFN_vkGetDeviceQueue2 GetDeviceQueue2;
     PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
     PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID;
     PFN_vkAcquireImageANDROID AcquireImageANDROID;
     PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
+    PFN_vkBindImageMemory2KHR BindImageMemory2KHR;
     // clang-format on
 };
 
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 73fc7b2..bc00190 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -16,17 +16,19 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <algorithm>
-
+#include <android/hardware/graphics/common/1.0/types.h>
 #include <grallocusage/GrallocUsageConversion.h>
 #include <log/log.h>
-#include <ui/BufferQueueDefs.h>
 #include <sync/sync.h>
+#include <system/window.h>
+#include <ui/BufferQueueDefs.h>
 #include <utils/StrongPointer.h>
 #include <utils/Trace.h>
 #include <utils/Vector.h>
-#include <system/window.h>
-#include <android/hardware/graphics/common/1.0/types.h>
+
+#include <algorithm>
+#include <unordered_set>
+#include <vector>
 
 #include "driver.h"
 
@@ -1818,5 +1820,106 @@
     return;
 }
 
+static void InterceptBindImageMemory2(
+    uint32_t bind_info_count,
+    const VkBindImageMemoryInfo* bind_infos,
+    std::vector<VkNativeBufferANDROID>* out_native_buffers,
+    std::vector<VkBindImageMemoryInfo>* out_bind_infos) {
+    out_native_buffers->clear();
+    out_bind_infos->clear();
+
+    if (!bind_info_count)
+        return;
+
+    std::unordered_set<uint32_t> intercepted_indexes;
+
+    for (uint32_t idx = 0; idx < bind_info_count; idx++) {
+        auto info = reinterpret_cast<const VkBindImageMemorySwapchainInfoKHR*>(
+            bind_infos[idx].pNext);
+        while (info &&
+               info->sType !=
+                   VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR) {
+            info = reinterpret_cast<const VkBindImageMemorySwapchainInfoKHR*>(
+                info->pNext);
+        }
+
+        if (!info)
+            continue;
+
+        ALOG_ASSERT(info->swapchain != VK_NULL_HANDLE,
+                    "swapchain handle must not be NULL");
+        const Swapchain* swapchain = SwapchainFromHandle(info->swapchain);
+        ALOG_ASSERT(
+            info->imageIndex < swapchain->num_images,
+            "imageIndex must be less than the number of images in swapchain");
+
+        ANativeWindowBuffer* buffer =
+            swapchain->images[info->imageIndex].buffer.get();
+        VkNativeBufferANDROID native_buffer = {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wold-style-cast"
+            .sType = VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID,
+#pragma clang diagnostic pop
+            .pNext = bind_infos[idx].pNext,
+            .handle = buffer->handle,
+            .stride = buffer->stride,
+            .format = buffer->format,
+            .usage = int(buffer->usage),
+        };
+        // Reserve enough space to avoid letting re-allocation invalidate the
+        // addresses of the elements inside.
+        out_native_buffers->reserve(bind_info_count);
+        out_native_buffers->emplace_back(native_buffer);
+
+        // Reserve the space now since we know how much is needed now.
+        out_bind_infos->reserve(bind_info_count);
+        out_bind_infos->emplace_back(bind_infos[idx]);
+        out_bind_infos->back().pNext = &out_native_buffers->back();
+
+        intercepted_indexes.insert(idx);
+    }
+
+    if (intercepted_indexes.empty())
+        return;
+
+    for (uint32_t idx = 0; idx < bind_info_count; idx++) {
+        if (intercepted_indexes.count(idx))
+            continue;
+        out_bind_infos->emplace_back(bind_infos[idx]);
+    }
+}
+
+VKAPI_ATTR
+VkResult BindImageMemory2(VkDevice device,
+                          uint32_t bindInfoCount,
+                          const VkBindImageMemoryInfo* pBindInfos) {
+    ATRACE_CALL();
+
+    // out_native_buffers is for maintaining the lifecycle of the constructed
+    // VkNativeBufferANDROID objects inside InterceptBindImageMemory2.
+    std::vector<VkNativeBufferANDROID> out_native_buffers;
+    std::vector<VkBindImageMemoryInfo> out_bind_infos;
+    InterceptBindImageMemory2(bindInfoCount, pBindInfos, &out_native_buffers,
+                              &out_bind_infos);
+    return GetData(device).driver.BindImageMemory2(
+        device, bindInfoCount,
+        out_bind_infos.empty() ? pBindInfos : out_bind_infos.data());
+}
+
+VKAPI_ATTR
+VkResult BindImageMemory2KHR(VkDevice device,
+                             uint32_t bindInfoCount,
+                             const VkBindImageMemoryInfo* pBindInfos) {
+    ATRACE_CALL();
+
+    std::vector<VkNativeBufferANDROID> out_native_buffers;
+    std::vector<VkBindImageMemoryInfo> out_bind_infos;
+    InterceptBindImageMemory2(bindInfoCount, pBindInfos, &out_native_buffers,
+                              &out_bind_infos);
+    return GetData(device).driver.BindImageMemory2KHR(
+        device, bindInfoCount,
+        out_bind_infos.empty() ? pBindInfos : out_bind_infos.data());
+}
+
 }  // namespace driver
 }  // namespace vulkan
diff --git a/vulkan/libvulkan/swapchain.h b/vulkan/libvulkan/swapchain.h
index ed5718c..4912ef1 100644
--- a/vulkan/libvulkan/swapchain.h
+++ b/vulkan/libvulkan/swapchain.h
@@ -44,6 +44,8 @@
 VKAPI_ATTR void SetHdrMetadataEXT(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pHdrMetadataEXTs);
 VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilities2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, VkSurfaceCapabilities2KHR* pSurfaceCapabilities);
 VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceFormats2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, uint32_t* pSurfaceFormatCount, VkSurfaceFormat2KHR* pSurfaceFormats);
+VKAPI_ATTR VkResult BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos);
+VKAPI_ATTR VkResult BindImageMemory2KHR(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos);
 // clang-format on
 
 }  // namespace driver