Merge "Otapreopt: Fix after shared-library work" into nyc-mr1-dev
diff --git a/docs/Doxyfile b/docs/Doxyfile
index 46d6d84..3ea453f 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -677,7 +677,7 @@
 # directories like "/usr/src/myproject". Separate the files or directories 
 # with spaces.
 
-INPUT                  = ../include/android
+INPUT                  = ../include/android ../../av/include/ndk ../../av/include/camera/ndk
 
 # This tag can be used to specify the character encoding of the source files 
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
diff --git a/docs/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png b/docs/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png
new file mode 100644
index 0000000..7578b48
--- /dev/null
+++ b/docs/images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png b/docs/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png
new file mode 100644
index 0000000..7b10f6b
--- /dev/null
+++ b/docs/images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png b/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png
new file mode 100644
index 0000000..41972cf
--- /dev/null
+++ b/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_e_shading.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png b/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png
new file mode 100644
index 0000000..d26600b
--- /dev/null
+++ b/docs/images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png b/docs/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png
new file mode 100644
index 0000000..1e7208e
--- /dev/null
+++ b/docs/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png b/docs/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png
new file mode 100644
index 0000000..ecef3ae
--- /dev/null
+++ b/docs/images/camera2/metadata/android.statistics.lensShadingMap/red_shading.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png b/docs/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png
new file mode 100644
index 0000000..a02fd89
--- /dev/null
+++ b/docs/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png b/docs/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png
new file mode 100644
index 0000000..c309ac5
--- /dev/null
+++ b/docs/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png b/docs/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png
new file mode 100644
index 0000000..414fad4
--- /dev/null
+++ b/docs/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png b/docs/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png
new file mode 100644
index 0000000..c147a87
--- /dev/null
+++ b/docs/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png b/docs/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png
new file mode 100644
index 0000000..4ce2125
--- /dev/null
+++ b/docs/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png
Binary files differ
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 65f8255..07cfb4f 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -444,14 +444,6 @@
 
         mSlots[found].mBufferState.dequeue();
 
-        // If shared buffer mode has just been enabled, cache the slot of the
-        // first buffer that is dequeued and mark it as the shared buffer.
-        if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot ==
-                BufferQueueCore::INVALID_BUFFER_SLOT) {
-            mCore->mSharedBufferSlot = found;
-            mSlots[found].mBufferState.mShared = true;
-        }
-
         if ((buffer == NULL) ||
                 buffer->needsReallocation(width, height, format, usage))
         {
@@ -483,9 +475,21 @@
 
         eglDisplay = mSlots[found].mEglDisplay;
         eglFence = mSlots[found].mEglFence;
-        *outFence = mSlots[found].mFence;
+        // Don't return a fence in shared buffer mode, except for the first
+        // frame.
+        *outFence = (mCore->mSharedBufferMode &&
+                mCore->mSharedBufferSlot == found) ?
+                Fence::NO_FENCE : mSlots[found].mFence;
         mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
         mSlots[found].mFence = Fence::NO_FENCE;
+
+        // If shared buffer mode has just been enabled, cache the slot of the
+        // first buffer that is dequeued and mark it as the shared buffer.
+        if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot ==
+                BufferQueueCore::INVALID_BUFFER_SLOT) {
+            mCore->mSharedBufferSlot = found;
+            mSlots[found].mBufferState.mShared = true;
+        }
     } // Autolock scope
 
     if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index fb6307e..d654b17 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -44,7 +44,6 @@
 
 LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\"
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
-#LOCAL_CFLAGS += -DENABLE_FENCE_TRACKING
 
 USE_HWC2 := false
 ifeq ($(USE_HWC2),true)
diff --git a/services/surfaceflinger/FenceTracker.cpp b/services/surfaceflinger/FenceTracker.cpp
index 885d712..d415bd5 100644
--- a/services/surfaceflinger/FenceTracker.cpp
+++ b/services/surfaceflinger/FenceTracker.cpp
@@ -184,8 +184,6 @@
 
     mOffset = (mOffset + 1) % MAX_FRAME_HISTORY;
     mFrameCounter++;
-
-    checkFencesForCompletion();
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/FenceTracker.h b/services/surfaceflinger/FenceTracker.h
index de99820..2fcc314 100644
--- a/services/surfaceflinger/FenceTracker.h
+++ b/services/surfaceflinger/FenceTracker.h
@@ -42,7 +42,7 @@
              const Vector<sp<Layer>>& layers, sp<Fence> glDoneFence);
 
 protected:
-     static constexpr size_t MAX_FRAME_HISTORY = 128;
+     static constexpr size_t MAX_FRAME_HISTORY = 8;
 
      struct LayerRecord {
          String8 name; // layer name
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1fa7955..0276d38 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -939,11 +939,7 @@
 void SurfaceFlinger::handleMessageRefresh() {
     ATRACE_CALL();
 
-#ifdef ENABLE_FENCE_TRACKING
     nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#else
-    nsecs_t refreshStartTime = 0;
-#endif
     static nsecs_t previousExpectedPresent = 0;
     nsecs_t expectedPresent = mPrimaryDispSync.computeNextRefresh(0);
     static bool previousFrameMissed = false;
@@ -1033,11 +1029,7 @@
     }
 }
 
-#ifdef ENABLE_FENCE_TRACKING
 void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
-#else
-void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/)
-#endif
 {
     ATRACE_CALL();
     ALOGV("postComposition");
@@ -1069,10 +1061,8 @@
         }
     }
 
-#ifdef ENABLE_FENCE_TRACKING
     mFenceTracker.addFrame(refreshStartTime, presentFence,
             hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-#endif
 
     if (mAnimCompositionPending) {
         mAnimCompositionPending = false;
@@ -2610,14 +2600,12 @@
                 dumpAll = false;
             }
 
-#ifdef ENABLE_FENCE_TRACKING
             if ((index < numArgs) &&
                     (args[index] == String16("--fences"))) {
                 index++;
                 mFenceTracker.dump(&result);
                 dumpAll = false;
             }
-#endif
         }
 
         if (dumpAll) {
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index f094913..71d7cf9 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -943,11 +943,7 @@
 void SurfaceFlinger::handleMessageRefresh() {
     ATRACE_CALL();
 
-#ifdef ENABLE_FENCE_TRACKING
     nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#else
-    nsecs_t refreshStartTime = 0;
-#endif
     static nsecs_t previousExpectedPresent = 0;
     nsecs_t expectedPresent = mPrimaryDispSync.computeNextRefresh(0);
     static bool previousFrameMissed = false;
@@ -1029,11 +1025,7 @@
     }
 }
 
-#ifdef ENABLE_FENCE_TRACKING
 void SurfaceFlinger::postComposition(nsecs_t refreshStartTime)
-#else
-void SurfaceFlinger::postComposition(nsecs_t /*refreshStartTime*/)
-#endif
 {
     const LayerVector& layers(mDrawingState.layersSortedByZ);
     const size_t count = layers.size();
@@ -1063,10 +1055,8 @@
         }
     }
 
-#ifdef ENABLE_FENCE_TRACKING
     mFenceTracker.addFrame(refreshStartTime, presentFence,
             hw->getVisibleLayersSortedByZ(), hw->getClientTargetAcquireFence());
-#endif
 
     if (mAnimCompositionPending) {
         mAnimCompositionPending = false;
@@ -2626,14 +2616,12 @@
                 dumpAll = false;
             }
 
-#ifdef ENABLE_FENCE_TRACKING
             if ((index < numArgs) &&
                     (args[index] == String16("--fences"))) {
                 index++;
                 mFenceTracker.dump(&result);
                 dumpAll = false;
             }
-#endif
         }
 
         if (dumpAll) {
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 320fddb..f8d4d13 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -66,6 +66,8 @@
         sp<ISurfaceComposer> sf(ComposerService::getComposerService());
         sp<IBinder> display(sf->getBuiltInDisplay(
                 ISurfaceComposer::eDisplayIdMain));
+        SurfaceComposerClient::openGlobalTransaction();
+        SurfaceComposerClient::closeGlobalTransaction(true);
         ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer, Rect(), 0, 0,
                 0, INT_MAX, false));
         *sc = new ScreenCapture(cpuConsumer);
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index ff00f32..0a1dda2 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -128,7 +128,6 @@
     INIT_PROC(instance, GetPhysicalDeviceFormatProperties);
     INIT_PROC(instance, GetPhysicalDeviceImageFormatProperties);
     INIT_PROC(instance, CreateDevice);
-    INIT_PROC(instance, EnumerateDeviceLayerProperties);
     INIT_PROC(instance, EnumerateDeviceExtensionProperties);
     INIT_PROC(instance, GetPhysicalDeviceSparseImageFormatProperties);
     INIT_PROC_EXT(KHR_surface, instance, DestroySurfaceKHR);
diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h
index 779b654..7f8d274 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -38,7 +38,6 @@
     PFN_vkGetPhysicalDeviceFormatProperties GetPhysicalDeviceFormatProperties;
     PFN_vkGetPhysicalDeviceImageFormatProperties GetPhysicalDeviceImageFormatProperties;
     PFN_vkCreateDevice CreateDevice;
-    PFN_vkEnumerateDeviceLayerProperties EnumerateDeviceLayerProperties;
     PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
     PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties;
     PFN_vkDestroySurfaceKHR DestroySurfaceKHR;
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
index 307f0e4..f9a4670 100644
--- a/vulkan/libvulkan/code-generator.tmpl
+++ b/vulkan/libvulkan/code-generator.tmpl
@@ -434,7 +434,10 @@
   {{AssertType $ "Function"}}
 
   {{if and (Macro "IsFunctionExported" $) (Macro "IsInstanceDispatched" $)}}
-    true
+    {{/* deprecated and unused internally */}}
+    {{if not (eq $.Name "vkEnumerateDeviceLayerProperties")}}
+      true
+    {{end}}
   {{end}}
 {{end}}
 
@@ -938,8 +941,6 @@
     {{else if eq $.Name "vkDestroyInstance"}}true
     {{else if eq $.Name "vkDestroyDevice"}}true
 
-    {{else if eq $.Name "vkEnumerateDeviceLayerProperties"}}true
-
     {{/* Enumeration of extensions */}}
     {{else if eq $.Name "vkEnumerateDeviceExtensionProperties"}}true
 
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index eabbf1f..2555272 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -124,7 +124,7 @@
 Hal Hal::hal_;
 
 bool Hal::Open() {
-    ALOG_ASSERT(!dev_, "OpenHAL called more than once");
+    ALOG_ASSERT(!hal_.dev_, "OpenHAL called more than once");
 
     // Use a stub device unless we successfully open a real HAL device.
     hal_.dev_ = &stubhal::kDevice;
@@ -797,6 +797,7 @@
 
         return VK_ERROR_INCOMPATIBLE_DRIVER;
     }
+    data->driver_device = dev;
 
     *pDevice = dev;
 
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 210c3c7..a02ebd7 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -98,6 +98,7 @@
 
     std::bitset<ProcHook::EXTENSION_COUNT> hook_extensions;
 
+    VkDevice driver_device;
     DeviceDriverTable driver;
 };
 
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index 29351a1..d979a34 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -328,7 +328,6 @@
     INIT_PROC(instance, EnumeratePhysicalDevices);
     INIT_PROC(instance, GetInstanceProcAddr);
     INIT_PROC(instance, CreateDevice);
-    INIT_PROC(instance, EnumerateDeviceLayerProperties);
     INIT_PROC(instance, EnumerateDeviceExtensionProperties);
     INIT_PROC_EXT(EXT_debug_report, instance, CreateDebugReportCallbackEXT);
     INIT_PROC_EXT(EXT_debug_report, instance, DestroyDebugReportCallbackEXT);
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index ca17d57..a60b2fe 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -58,7 +58,6 @@
     PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices;
     PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
     PFN_vkCreateDevice CreateDevice;
-    PFN_vkEnumerateDeviceLayerProperties EnumerateDeviceLayerProperties;
     PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
     PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT;
     PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT;
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index c3d71d5..207c318 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -109,6 +109,7 @@
 
 struct Surface {
     android::sp<ANativeWindow> window;
+    VkSwapchainKHR swapchain_handle;
 };
 
 VkSurfaceKHR HandleFromSurface(Surface* surface) {
@@ -147,6 +148,66 @@
     return reinterpret_cast<Swapchain*>(handle);
 }
 
+void ReleaseSwapchainImage(VkDevice device,
+                           ANativeWindow* window,
+                           int release_fence,
+                           Swapchain::Image& image) {
+    ALOG_ASSERT(release_fence == -1 || image.dequeued,
+                "ReleaseSwapchainImage: can't provide a release fence for "
+                "non-dequeued images");
+
+    if (image.dequeued) {
+        if (release_fence >= 0) {
+            // We get here from vkQueuePresentKHR. The application is
+            // responsible for creating an execution dependency chain from
+            // vkAcquireNextImage (dequeue_fence) to vkQueuePresentKHR
+            // (release_fence), so we can drop the dequeue_fence here.
+            if (image.dequeue_fence >= 0)
+                close(image.dequeue_fence);
+        } else {
+            // We get here during swapchain destruction, or various serious
+            // error cases e.g. when we can't create the release_fence during
+            // vkQueuePresentKHR. In non-error cases, the dequeue_fence should
+            // have already signalled, since the swapchain images are supposed
+            // to be idle before the swapchain is destroyed. In error cases,
+            // there may be rendering in flight to the image, but since we
+            // weren't able to create a release_fence, waiting for the
+            // dequeue_fence is about the best we can do.
+            release_fence = image.dequeue_fence;
+        }
+        image.dequeue_fence = -1;
+
+        if (window) {
+            window->cancelBuffer(window, image.buffer.get(), release_fence);
+        } else {
+            if (release_fence >= 0) {
+                sync_wait(release_fence, -1 /* forever */);
+                close(release_fence);
+            }
+        }
+
+        image.dequeued = false;
+    }
+
+    if (image.image) {
+        GetData(device).driver.DestroyImage(device, image.image, nullptr);
+        image.image = VK_NULL_HANDLE;
+    }
+
+    image.buffer.clear();
+}
+
+void OrphanSwapchain(VkDevice device, Swapchain* swapchain) {
+    if (swapchain->surface.swapchain_handle != HandleFromSwapchain(swapchain))
+        return;
+    const auto& dispatch = GetData(device).driver;
+    for (uint32_t i = 0; i < swapchain->num_images; i++) {
+        if (!swapchain->images[i].dequeued)
+            ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i]);
+    }
+    swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
+}
+
 }  // anonymous namespace
 
 VKAPI_ATTR
@@ -165,6 +226,7 @@
     Surface* surface = new (mem) Surface;
 
     surface->window = pCreateInfo->window;
+    surface->swapchain_handle = VK_NULL_HANDLE;
 
     // TODO(jessehall): Create and use NATIVE_WINDOW_API_VULKAN.
     int err =
@@ -191,6 +253,11 @@
     if (!surface)
         return;
     native_window_api_disconnect(surface->window.get(), NATIVE_WINDOW_API_EGL);
+    ALOGE_IF(surface->swapchain_handle != VK_NULL_HANDLE,
+             "destroyed VkSurfaceKHR 0x%" PRIx64
+             " has active VkSwapchainKHR 0x%" PRIx64,
+             reinterpret_cast<uint64_t>(surface_handle),
+             reinterpret_cast<uint64_t>(surface->swapchain_handle));
     surface->~Surface();
     if (!allocator)
         allocator = &GetData(instance).allocator;
@@ -343,30 +410,54 @@
     if (!allocator)
         allocator = &GetData(device).allocator;
 
-    ALOGV_IF(create_info->imageArrayLayers != 1,
-             "Swapchain imageArrayLayers (%u) != 1 not supported",
+    ALOGE_IF(create_info->imageArrayLayers != 1,
+             "swapchain imageArrayLayers=%u not supported",
              create_info->imageArrayLayers);
-
     ALOGE_IF(create_info->imageColorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
-             "color spaces other than SRGB_NONLINEAR not yet implemented");
-    ALOGE_IF(create_info->oldSwapchain,
-             "swapchain re-creation not yet implemented");
+             "swapchain imageColorSpace=%u not supported",
+             create_info->imageColorSpace);
     ALOGE_IF((create_info->preTransform & ~kSupportedTransforms) != 0,
-             "swapchain preTransform %d not supported",
+             "swapchain preTransform=%#x not supported",
              create_info->preTransform);
-    ALOGW_IF(!(create_info->presentMode == VK_PRESENT_MODE_FIFO_KHR ||
+    ALOGE_IF(!(create_info->presentMode == VK_PRESENT_MODE_FIFO_KHR ||
                create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR),
-             "swapchain present mode %d not supported",
+             "swapchain presentMode=%u not supported",
              create_info->presentMode);
 
     Surface& surface = *SurfaceFromHandle(create_info->surface);
 
+    if (surface.swapchain_handle != create_info->oldSwapchain) {
+        ALOGE("Can't create a swapchain for VkSurfaceKHR 0x%" PRIx64
+              " because it already has active swapchain 0x%" PRIx64
+              " but VkSwapchainCreateInfo::oldSwapchain=0x%" PRIx64,
+              reinterpret_cast<uint64_t>(create_info->surface),
+              reinterpret_cast<uint64_t>(surface.swapchain_handle),
+              reinterpret_cast<uint64_t>(create_info->oldSwapchain));
+        return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;
+    }
+    if (create_info->oldSwapchain != VK_NULL_HANDLE)
+        OrphanSwapchain(device, SwapchainFromHandle(create_info->oldSwapchain));
+
     // -- Reset the native window --
     // The native window might have been used previously, and had its properties
     // changed from defaults. That will affect the answer we get for queries
     // like MIN_UNDEQUED_BUFFERS. Reset to a known/default state before we
     // attempt such queries.
 
+    // The native window only allows dequeueing all buffers before any have
+    // been queued, since after that point at least one is assumed to be in
+    // non-FREE state at any given time. Disconnecting and re-connecting
+    // orphans the previous buffers, getting us back to the state where we can
+    // dequeue all buffers.
+    err = native_window_api_disconnect(surface.window.get(),
+                                       NATIVE_WINDOW_API_EGL);
+    ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)",
+             strerror(-err), err);
+    err =
+        native_window_api_connect(surface.window.get(), NATIVE_WINDOW_API_EGL);
+    ALOGW_IF(err != 0, "native_window_api_connect failed: %s (%d)",
+             strerror(-err), err);
+
     err = native_window_set_buffer_count(surface.window.get(), 0);
     if (err != 0) {
         ALOGE("native_window_set_buffer_count(0) failed: %s (%d)",
@@ -618,7 +709,8 @@
         return result;
     }
 
-    *swapchain_handle = HandleFromSwapchain(swapchain);
+    surface.swapchain_handle = HandleFromSwapchain(swapchain);
+    *swapchain_handle = surface.swapchain_handle;
     return VK_SUCCESS;
 }
 
@@ -628,21 +720,15 @@
                          const VkAllocationCallbacks* allocator) {
     const auto& dispatch = GetData(device).driver;
     Swapchain* swapchain = SwapchainFromHandle(swapchain_handle);
-    const android::sp<ANativeWindow>& window = swapchain->surface.window;
+    ANativeWindow* window =
+        (swapchain->surface.swapchain_handle == swapchain_handle)
+            ? swapchain->surface.window.get()
+            : nullptr;
 
-    for (uint32_t i = 0; i < swapchain->num_images; i++) {
-        Swapchain::Image& img = swapchain->images[i];
-        if (img.dequeued) {
-            window->cancelBuffer(window.get(), img.buffer.get(),
-                                 img.dequeue_fence);
-            img.dequeue_fence = -1;
-            img.dequeued = false;
-        }
-        if (img.image) {
-            dispatch.DestroyImage(device, img.image, nullptr);
-        }
-    }
-
+    for (uint32_t i = 0; i < swapchain->num_images; i++)
+        ReleaseSwapchainImage(device, window, -1, swapchain->images[i]);
+    if (swapchain->surface.swapchain_handle == swapchain_handle)
+        swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
     if (!allocator)
         allocator = &GetData(device).allocator;
     swapchain->~Swapchain();
@@ -655,6 +741,10 @@
                                uint32_t* count,
                                VkImage* images) {
     Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
+    ALOGW_IF(swapchain.surface.swapchain_handle != swapchain_handle,
+             "getting images for non-active swapchain 0x%" PRIx64
+             "; only dequeued image handles are valid",
+             reinterpret_cast<uint64_t>(swapchain_handle));
     VkResult result = VK_SUCCESS;
     if (images) {
         uint32_t n = swapchain.num_images;
@@ -681,6 +771,9 @@
     VkResult result;
     int err;
 
+    if (swapchain.surface.swapchain_handle != swapchain_handle)
+        return VK_ERROR_OUT_OF_DATE_KHR;
+
     ALOGW_IF(
         timeout != UINT64_MAX,
         "vkAcquireNextImageKHR: non-infinite timeouts not yet implemented");
@@ -739,6 +832,26 @@
     return VK_SUCCESS;
 }
 
+static VkResult WorstPresentResult(VkResult a, VkResult b) {
+    // See the error ranking for vkQueuePresentKHR at the end of section 29.6
+    // (in spec version 1.0.14).
+    static const VkResult kWorstToBest[] = {
+        VK_ERROR_DEVICE_LOST,
+        VK_ERROR_SURFACE_LOST_KHR,
+        VK_ERROR_OUT_OF_DATE_KHR,
+        VK_ERROR_OUT_OF_DEVICE_MEMORY,
+        VK_ERROR_OUT_OF_HOST_MEMORY,
+        VK_SUBOPTIMAL_KHR,
+    };
+    for (auto result : kWorstToBest) {
+        if (a == result || b == result)
+            return result;
+    }
+    ALOG_ASSERT(a == VK_SUCCESS, "invalid vkQueuePresentKHR result %d", a);
+    ALOG_ASSERT(b == VK_SUCCESS, "invalid vkQueuePresentKHR result %d", b);
+    return a != VK_SUCCESS ? a : b;
+}
+
 VKAPI_ATTR
 VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) {
     ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
@@ -746,14 +859,16 @@
              present_info->sType);
     ALOGV_IF(present_info->pNext, "VkPresentInfo::pNext != NULL");
 
+    VkDevice device = GetData(queue).driver_device;
     const auto& dispatch = GetData(queue).driver;
     VkResult final_result = VK_SUCCESS;
+
     for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
         Swapchain& swapchain =
             *SwapchainFromHandle(present_info->pSwapchains[sc]);
-        ANativeWindow* window = swapchain.surface.window.get();
         uint32_t image_idx = present_info->pImageIndices[sc];
         Swapchain::Image& img = swapchain.images[image_idx];
+        VkResult swapchain_result = VK_SUCCESS;
         VkResult result;
         int err;
 
@@ -763,37 +878,42 @@
             present_info->pWaitSemaphores, img.image, &fence);
         if (result != VK_SUCCESS) {
             ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
-            if (present_info->pResults)
-                present_info->pResults[sc] = result;
-            if (final_result == VK_SUCCESS)
-                final_result = result;
-            // TODO(jessehall): What happens to the buffer here? Does the app
-            // still own it or not, i.e. should we cancel the buffer? Hard to
-            // do correctly without synchronizing, though I guess we could wait
-            // for the queue to idle.
-            continue;
+            swapchain_result = result;
         }
 
-        err = window->queueBuffer(window, img.buffer.get(), fence);
-        if (err != 0) {
-            // TODO(jessehall): What now? We should probably cancel the buffer,
-            // I guess?
-            ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
-            if (present_info->pResults)
-                present_info->pResults[sc] = result;
-            if (final_result == VK_SUCCESS)
-                final_result = VK_ERROR_INITIALIZATION_FAILED;
-            continue;
+        if (swapchain.surface.swapchain_handle ==
+            present_info->pSwapchains[sc]) {
+            ANativeWindow* window = swapchain.surface.window.get();
+            if (swapchain_result == VK_SUCCESS) {
+                err = window->queueBuffer(window, img.buffer.get(), fence);
+                // queueBuffer always closes fence, even on error
+                if (err != 0) {
+                    // TODO(jessehall): What now? We should probably cancel the
+                    // buffer, I guess?
+                    ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
+                    swapchain_result = WorstPresentResult(
+                        swapchain_result, VK_ERROR_OUT_OF_DATE_KHR);
+                }
+                if (img.dequeue_fence >= 0) {
+                    close(img.dequeue_fence);
+                    img.dequeue_fence = -1;
+                }
+                img.dequeued = false;
+            }
+            if (swapchain_result != VK_SUCCESS) {
+                ReleaseSwapchainImage(device, window, fence, img);
+                OrphanSwapchain(device, &swapchain);
+            }
+        } else {
+            ReleaseSwapchainImage(device, nullptr, fence, img);
+            swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
         }
 
-        if (img.dequeue_fence != -1) {
-            close(img.dequeue_fence);
-            img.dequeue_fence = -1;
-        }
-        img.dequeued = false;
-
         if (present_info->pResults)
-            present_info->pResults[sc] = VK_SUCCESS;
+            present_info->pResults[sc] = swapchain_result;
+
+        if (swapchain_result != final_result)
+            final_result = WorstPresentResult(final_result, swapchain_result);
     }
 
     return final_result;