Merge "Separate vibration log buffers"
diff --git a/api/system-current.txt b/api/system-current.txt
index 0f1ab29..0597dba 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1576,6 +1576,7 @@
     method public boolean getAllocateAggressive();
     method @Deprecated public boolean getAllowDowngrade();
     method public boolean getDontKillApp();
+    method public boolean getEnableRollback();
     method @Nullable public String[] getGrantedRuntimePermissions();
     method public boolean getInstallAsFullApp(boolean);
     method public boolean getInstallAsInstantApp(boolean);
@@ -1587,7 +1588,7 @@
     method @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public void setAllocateAggressive(boolean);
     method @Deprecated public void setAllowDowngrade(boolean);
     method public void setDontKillApp(boolean);
-    method public void setEnableRollback();
+    method public void setEnableRollback(boolean);
     method @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) public void setGrantedRuntimePermissions(String[]);
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex();
     method public void setInstallAsInstantApp(boolean);
@@ -1822,18 +1823,18 @@
 
   public final class PackageRollbackInfo implements android.os.Parcelable {
     method public int describeContents();
-    method public String getPackageName();
-    method public android.content.pm.VersionedPackage getVersionRolledBackFrom();
-    method public android.content.pm.VersionedPackage getVersionRolledBackTo();
+    method @NonNull public String getPackageName();
+    method @NonNull public android.content.pm.VersionedPackage getVersionRolledBackFrom();
+    method @NonNull public android.content.pm.VersionedPackage getVersionRolledBackTo();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.rollback.PackageRollbackInfo> CREATOR;
   }
 
   public final class RollbackInfo implements android.os.Parcelable {
     method public int describeContents();
-    method public java.util.List<android.content.pm.VersionedPackage> getCausePackages();
+    method @NonNull public java.util.List<android.content.pm.VersionedPackage> getCausePackages();
     method public int getCommittedSessionId();
-    method public java.util.List<android.content.rollback.PackageRollbackInfo> getPackages();
+    method @NonNull public java.util.List<android.content.rollback.PackageRollbackInfo> getPackages();
     method public int getRollbackId();
     method public boolean isStaged();
     method public void writeToParcel(android.os.Parcel, int);
@@ -1843,7 +1844,7 @@
   public final class RollbackManager {
     method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void commitRollback(int, @NonNull java.util.List<android.content.pm.VersionedPackage>, @NonNull android.content.IntentSender);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void expireRollbackForPackage(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public java.util.List<android.content.rollback.RollbackInfo> getAvailableRollbacks();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) @NonNull public java.util.List<android.content.rollback.RollbackInfo> getAvailableRollbacks();
     method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) @NonNull public java.util.List<android.content.rollback.RollbackInfo> getRecentlyCommittedRollbacks();
     method @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS) public void reloadPersistedData();
     field public static final String EXTRA_STATUS = "android.content.rollback.extra.STATUS";
@@ -5187,7 +5188,7 @@
 
   public static interface Binder.ProxyTransactListener {
     method public void onTransactEnded(@Nullable Object);
-    method public Object onTransactStarted(android.os.IBinder, int);
+    method @Nullable public Object onTransactStarted(@NonNull android.os.IBinder, int);
   }
 
   public class BugreportManager {
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 7e04469..18d0ec0 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -60,6 +60,14 @@
 
 }
 
+package android.content.pm {
+
+  public static class PackageInstaller.SessionParams implements android.os.Parcelable {
+    method @Deprecated public void setEnableRollback();
+  }
+
+}
+
 package android.location {
 
   public class LocationManager {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d758c4d..7b4dd19 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1448,14 +1448,33 @@
 
         /**
          * Request that rollbacks be enabled for the given upgrade.
+         *
+         * @removed
+         * @deprecated use {@link #setEnableRollback(boolean)} instead.
          * @hide
          */
+        @Deprecated
         @SystemApi
         public void setEnableRollback() {
             installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
         }
 
         /**
+         * Request that rollbacks be enabled or disabled for the given upgrade.
+         *
+         * @param enable set to {@code true} to enable, {@code false} to disable
+         * @hide
+         */
+        @SystemApi
+        public void setEnableRollback(boolean enable) {
+            if (enable) {
+                installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
+            } else {
+                installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
+            }
+        }
+
+        /**
          * @deprecated use {@link #setRequestDowngrade(boolean)}.
          * {@hide}
          */
@@ -2058,6 +2077,16 @@
         }
 
         /**
+         * Return whether rollback is enabled or disabled for the given upgrade.
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean getEnableRollback() {
+            return (installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0;
+        }
+
+        /**
          * Get the value set in {@link SessionParams#setAllocateAggressive(boolean)}.
          *
          * @hide
diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java
index 03810f5..c0414fc 100644
--- a/core/java/android/content/rollback/PackageRollbackInfo.java
+++ b/core/java/android/content/rollback/PackageRollbackInfo.java
@@ -89,6 +89,7 @@
     /**
      * Returns the name of the package to roll back from.
      */
+    @NonNull
     public String getPackageName() {
         return mVersionRolledBackFrom.getPackageName();
     }
@@ -96,6 +97,7 @@
     /**
      * Returns the version of the package rolled back from.
      */
+    @NonNull
     public VersionedPackage getVersionRolledBackFrom() {
         return mVersionRolledBackFrom;
     }
@@ -103,6 +105,7 @@
     /**
      * Returns the version of the package rolled back to.
      */
+    @NonNull
     public VersionedPackage getVersionRolledBackTo() {
         return mVersionRolledBackTo;
     }
diff --git a/core/java/android/content/rollback/RollbackInfo.java b/core/java/android/content/rollback/RollbackInfo.java
index 29b99e0..a363718 100644
--- a/core/java/android/content/rollback/RollbackInfo.java
+++ b/core/java/android/content/rollback/RollbackInfo.java
@@ -16,6 +16,7 @@
 
 package android.content.rollback;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.content.pm.VersionedPackage;
 import android.os.Parcel;
@@ -72,6 +73,7 @@
     /**
      * Returns the list of package that are rolled back.
      */
+    @NonNull
     public List<PackageRollbackInfo> getPackages() {
         return mPackages;
     }
@@ -105,6 +107,7 @@
      * As provided to {@link #commitRollback} when the rollback was committed.
      * This is only applicable for rollbacks that have been committed.
      */
+    @NonNull
     public List<VersionedPackage> getCausePackages() {
         return mCausePackages;
     }
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
index c043491..4e8c254 100644
--- a/core/java/android/content/rollback/RollbackManager.java
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -57,6 +57,7 @@
      *            MANAGE_ROLLBACKS permission.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ROLLBACKS)
+    @NonNull
     public List<RollbackInfo> getAvailableRollbacks() {
         try {
             return mBinder.getAvailableRollbacks().getList();
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index b28c2f4..d5ef249 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -653,7 +653,8 @@
          *
          * @return an object that will be passed back to #onTransactEnded (or null).
          */
-        Object onTransactStarted(IBinder binder, int transactionCode);
+        @Nullable
+        Object onTransactStarted(@NonNull IBinder binder, int transactionCode);
 
         /**
          * Called after onTranact (even when an exception is thrown).
diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml
index a2ad3b9..4e6b712 100644
--- a/core/res/res/values-night/values.xml
+++ b/core/res/res/values-night/values.xml
@@ -24,6 +24,7 @@
         <item name="colorError">@color/error_color_device_default_dark</item>
         <item name="colorControlNormal">?attr/textColorPrimary</item>
         <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
+        <item name="forceDarkAllowed">false</item>
 
         <!-- QS panel background -->
         <item name="colorBackgroundFloating">@color/black</item>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b7f594e..442ad7e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3943,4 +3943,8 @@
     <!-- The default peak refresh rate for a given device. Change this value if you want to allow
          for higher refresh rates to be automatically used out of the box -->
     <integer name="config_defaultPeakRefreshRate">60</integer>
+
+    <!-- The type of the light sensor to be used by the display framework for things like
+         auto-brightness. If unset, then it just gets the default sensor of type TYPE_LIGHT. -->
+    <string name="config_displayLightSensorType" translatable="false" />
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 96a3d96..c7e19c5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3693,5 +3693,9 @@
   <java-symbol type="string" name="mime_type_presentation" />
   <java-symbol type="string" name="mime_type_presentation_ext" />
 
+  <!-- For high refresh rate displays -->
   <java-symbol type="integer" name="config_defaultPeakRefreshRate" />
+
+  <!-- For Auto-Brightness -->
+  <java-symbol type="string" name="config_displayLightSensorType" />
 </resources>
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 793dd8d..4f1b2a4 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -69,6 +69,7 @@
         "libminikin",
         "libandroidfw",
         "libcrypto",
+        "libsync",
     ],
     static_libs: [
         "libEGL_blobCache",
@@ -180,6 +181,7 @@
         "renderthread/EglManager.cpp",
         "renderthread/ReliableSurface.cpp",
         "renderthread/VulkanManager.cpp",
+        "renderthread/VulkanSurface.cpp",
         "renderthread/RenderProxy.cpp",
         "renderthread/RenderTask.cpp",
         "renderthread/RenderThread.cpp",
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 87cffb5..edde6d3 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -55,20 +55,8 @@
 }
 
 Frame SkiaVulkanPipeline::getFrame() {
-    LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr,
-                        "drawRenderNode called on a context with no surface!");
-
-    SkSurface* backBuffer = mVkManager.getBackbufferSurface(&mVkSurface);
-    LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr,
-                        "drawRenderNode called on a context with an invalid surface");
-    if (backBuffer == nullptr) {
-        SkDebugf("failed to get backbuffer");
-        return Frame(-1, -1, 0);
-    }
-
-    Frame frame(mVkSurface->windowWidth(), mVkSurface->windowHeight(),
-                mVkManager.getAge(mVkSurface));
-    return frame;
+    LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr, "getFrame() called on a context with no surface!");
+    return mVkManager.dequeueNextBuffer(mVkSurface);
 }
 
 bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
@@ -77,13 +65,13 @@
                               bool opaque, const LightInfo& lightInfo,
                               const std::vector<sp<RenderNode>>& renderNodes,
                               FrameInfoVisualizer* profiler) {
-    sk_sp<SkSurface> backBuffer = mVkSurface->getBackBufferSurface();
+    sk_sp<SkSurface> backBuffer = mVkSurface->getCurrentSkSurface();
     if (backBuffer.get() == nullptr) {
         return false;
     }
     SkiaPipeline::updateLighting(lightGeometry, lightInfo);
     renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-            backBuffer, mVkSurface->preTransform());
+            backBuffer, mVkSurface->getCurrentPreTransform());
     ShaderCache::get().onVkFrameFlushed(mRenderThread.getGrContext());
     layerUpdateQueue->clear();
 
@@ -113,7 +101,7 @@
     currentFrameInfo->markSwapBuffers();
 
     if (*requireSwap) {
-        mVkManager.swapBuffers(mVkSurface);
+        mVkManager.swapBuffers(mVkSurface, screenDirty);
     }
 
     return *requireSwap;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 2c24edd..77a7ab1 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -18,6 +18,7 @@
 
 #include "SkiaPipeline.h"
 #include "renderthread/VulkanManager.h"
+#include "renderthread/VulkanSurface.h"
 
 #include "renderstate/RenderState.h"
 
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 5af660c..d4c6eae 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -16,6 +16,7 @@
 
 #include "VulkanManager.h"
 
+#include <android/sync.h>
 #include <gui/Surface.h>
 
 #include "Properties.h"
@@ -23,6 +24,7 @@
 #include "renderstate/RenderState.h"
 #include "utils/FatVector.h"
 
+#include <GrBackendSemaphore.h>
 #include <GrBackendSurface.h>
 #include <GrContext.h>
 #include <GrTypes.h>
@@ -142,6 +144,7 @@
     GET_INST_PROC(GetPhysicalDeviceProperties);
     GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
     GET_INST_PROC(GetPhysicalDeviceFeatures2);
+    GET_INST_PROC(GetPhysicalDeviceImageFormatProperties2);
     GET_INST_PROC(CreateDevice);
     GET_INST_PROC(EnumerateDeviceExtensionProperties);
     GET_INST_PROC(CreateAndroidSurfaceKHR);
@@ -318,11 +321,6 @@
     GET_DEV_PROC(GetDeviceQueue);
     GET_DEV_PROC(DeviceWaitIdle);
     GET_DEV_PROC(DestroyDevice);
-    GET_DEV_PROC(CreateSwapchainKHR);
-    GET_DEV_PROC(DestroySwapchainKHR);
-    GET_DEV_PROC(GetSwapchainImagesKHR);
-    GET_DEV_PROC(AcquireNextImageKHR);
-    GET_DEV_PROC(QueuePresentKHR);
     GET_DEV_PROC(CreateCommandPool);
     GET_DEV_PROC(DestroyCommandPool);
     GET_DEV_PROC(AllocateCommandBuffers);
@@ -426,201 +424,102 @@
     };
 }
 
-// Returns the next BackbufferInfo to use for the next draw. The function will make sure all
-// previous uses have finished before returning.
-VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurface* surface) {
-    SkASSERT(surface->mBackbuffers);
+Frame VulkanManager::dequeueNextBuffer(VulkanSurface* surface) {
 
-    ++surface->mCurrentBackbufferIndex;
-    if (surface->mCurrentBackbufferIndex > surface->mImageCount) {
-        surface->mCurrentBackbufferIndex = 0;
+    VulkanSurface::NativeBufferInfo* bufferInfo = surface->dequeueNativeBuffer();
+
+    if (bufferInfo == nullptr) {
+        ALOGE("VulkanSurface::dequeueNativeBuffer called with an invalid surface!");
+        return Frame(-1, -1, 0);
     }
 
-    VulkanSurface::BackbufferInfo* backbuffer =
-            surface->mBackbuffers + surface->mCurrentBackbufferIndex;
+    LOG_ALWAYS_FATAL_IF(!bufferInfo->dequeued);
 
-    // Before we reuse a backbuffer, make sure its fences have all signaled so that we can safely
-    // reuse its commands buffers.
-    VkResult res = mWaitForFences(mDevice, 2, backbuffer->mUsageFences, true, UINT64_MAX);
-    if (res != VK_SUCCESS) {
-        return nullptr;
+    if (bufferInfo->dequeue_fence != -1) {
+        int fence_clone = dup(bufferInfo->dequeue_fence);
+        if (fence_clone == -1) {
+            ALOGE("dup(fence) failed, stalling until signalled: %s (%d)", strerror(errno), errno);
+            sync_wait(bufferInfo->dequeue_fence, -1 /* forever */);
+        } else {
+            VkSemaphoreCreateInfo semaphoreInfo;
+            semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+            semaphoreInfo.pNext = nullptr;
+            semaphoreInfo.flags = 0;
+            VkSemaphore semaphore;
+            VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+            LOG_ALWAYS_FATAL_IF(VK_SUCCESS != err, "Failed to create import semaphore, err: %d",
+                                err);
+
+            VkImportSemaphoreFdInfoKHR importInfo;
+            importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
+            importInfo.pNext = nullptr;
+            importInfo.semaphore = semaphore;
+            importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT;
+            importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+            importInfo.fd = fence_clone;
+
+            err = mImportSemaphoreFdKHR(mDevice, &importInfo);
+            LOG_ALWAYS_FATAL_IF(VK_SUCCESS != err, "Failed to import semaphore, err: %d", err);
+
+            GrBackendSemaphore backendSemaphore;
+            backendSemaphore.initVulkan(semaphore);
+            bufferInfo->skSurface->wait(1, &backendSemaphore);
+        }
     }
 
-    return backbuffer;
+    int bufferAge = (mSwapBehavior == SwapBehavior::Discard) ? 0 : surface->getCurrentBuffersAge();
+    return Frame(surface->logicalWidth(), surface->logicalHeight(), bufferAge);
 }
 
-static SkMatrix getPreTransformMatrix(int width, int height,
-                                      VkSurfaceTransformFlagBitsKHR transform) {
-    switch (transform) {
-        case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
-            return SkMatrix::I();
-        case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
-            return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1);
-        case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
-            return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1);
-        case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
-            return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1);
-        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR:
-            return SkMatrix::MakeAll(-1, 0, width, 0, 1, 0, 0, 0, 1);
-        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR:
-            return SkMatrix::MakeAll(0, -1, height, -1, 0, width, 0, 0, 1);
-        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR:
-            return SkMatrix::MakeAll(1, 0, 0, 0, -1, height, 0, 0, 1);
-        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR:
-            return SkMatrix::MakeAll(0, 1, 0, 1, 0, 0, 0, 0, 1);
-        default:
-            LOG_ALWAYS_FATAL("Unsupported pre transform of swapchain.");
-    }
-    return SkMatrix::I();
-}
-
-
-SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface** surfaceOut) {
-    // Recreate VulkanSurface, if ANativeWindow has been resized.
-    VulkanSurface* surface = *surfaceOut;
-    int windowWidth = 0, windowHeight = 0;
-    ANativeWindow* window = surface->mNativeWindow;
-    window->query(window, NATIVE_WINDOW_WIDTH, &windowWidth);
-    window->query(window, NATIVE_WINDOW_HEIGHT, &windowHeight);
-    if (windowWidth != surface->mWindowWidth || windowHeight != surface->mWindowHeight) {
-        ColorMode colorMode = surface->mColorMode;
-        sk_sp<SkColorSpace> colorSpace = surface->mColorSpace;
-        SkColorType colorType = surface->mColorType;
-        GrContext* grContext = surface->mGrContext;
-        destroySurface(surface);
-        *surfaceOut = createSurface(window, colorMode, colorSpace, colorType, grContext);
-        surface = *surfaceOut;
-        if (!surface) {
-            return nullptr;
-        }
+void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) {
+    if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
+        ATRACE_NAME("Finishing GPU work");
+        mDeviceWaitIdle(mDevice);
     }
 
-    VulkanSurface::BackbufferInfo* backbuffer = getAvailableBackbuffer(surface);
-    SkASSERT(backbuffer);
+    VkExportSemaphoreCreateInfo exportInfo;
+    exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
+    exportInfo.pNext = nullptr;
+    exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
 
-    VkResult res;
+    VkSemaphoreCreateInfo semaphoreInfo;
+    semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+    semaphoreInfo.pNext = &exportInfo;
+    semaphoreInfo.flags = 0;
+    VkSemaphore semaphore;
+    VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+    ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to create semaphore");
 
-    res = mResetFences(mDevice, 2, backbuffer->mUsageFences);
-    SkASSERT(VK_SUCCESS == res);
+    GrBackendSemaphore backendSemaphore;
+    backendSemaphore.initVulkan(semaphore);
 
-    // The acquire will signal the attached mAcquireSemaphore. We use this to know the image has
-    // finished presenting and that it is safe to begin sending new commands to the returned image.
-    res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX,
-                               backbuffer->mAcquireSemaphore, VK_NULL_HANDLE,
-                               &backbuffer->mImageIndex);
+    VulkanSurface::NativeBufferInfo* bufferInfo = surface->getCurrentBufferInfo();
 
-    if (VK_ERROR_SURFACE_LOST_KHR == res) {
-        // need to figure out how to create a new vkSurface without the platformData*
-        // maybe use attach somehow? but need a Window
-        return nullptr;
-    }
-    if (VK_ERROR_OUT_OF_DATE_KHR == res || VK_SUBOPTIMAL_KHR == res) {
-        // tear swapchain down and try again
-        if (!createSwapchain(surface)) {
-            return nullptr;
-        }
-        backbuffer = getAvailableBackbuffer(surface);
-        res = mResetFences(mDevice, 2, backbuffer->mUsageFences);
-        SkASSERT(VK_SUCCESS == res);
+    int fenceFd = -1;
+    GrSemaphoresSubmitted submitted =
+            bufferInfo->skSurface->flush(SkSurface::BackendSurfaceAccess::kPresent,
+                                         SkSurface::kNone_FlushFlags, 1, &backendSemaphore);
+    if (submitted == GrSemaphoresSubmitted::kYes) {
+        VkSemaphoreGetFdInfoKHR getFdInfo;
+        getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
+        getFdInfo.pNext = nullptr;
+        getFdInfo.semaphore = semaphore;
+        getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
 
-        // acquire the image
-        res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX,
-                                   backbuffer->mAcquireSemaphore, VK_NULL_HANDLE,
-                                   &backbuffer->mImageIndex);
-
-        if (VK_SUCCESS != res) {
-            return nullptr;
-        }
+        err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
+        ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to get semaphore Fd");
+    } else {
+        ALOGE("VulkanManager::swapBuffers(): Semaphore submission failed");
+        mQueueWaitIdle(mGraphicsQueue);
     }
 
-    // set up layout transfer from initial to color attachment
-    VkImageLayout layout = surface->mImageInfos[backbuffer->mImageIndex].mImageLayout;
-    SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout || VK_IMAGE_LAYOUT_PRESENT_SRC_KHR == layout);
-    VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-    VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-    VkAccessFlags srcAccessMask = 0;
-    VkAccessFlags dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
-                                  VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+    surface->presentCurrentBuffer(dirtyRect, fenceFd);
 
-    VkImageMemoryBarrier imageMemoryBarrier = {
-            VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,     // sType
-            NULL,                                       // pNext
-            srcAccessMask,                              // outputMask
-            dstAccessMask,                              // inputMask
-            layout,                                     // oldLayout
-            VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,   // newLayout
-            mPresentQueueIndex,                         // srcQueueFamilyIndex
-            mGraphicsQueueIndex,       // dstQueueFamilyIndex
-            surface->mImages[backbuffer->mImageIndex],  // image
-            {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}     // subresourceRange
-    };
-    mResetCommandBuffer(backbuffer->mTransitionCmdBuffers[0], 0);
-
-    VkCommandBufferBeginInfo info;
-    memset(&info, 0, sizeof(VkCommandBufferBeginInfo));
-    info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
-    info.flags = 0;
-    mBeginCommandBuffer(backbuffer->mTransitionCmdBuffers[0], &info);
-
-    mCmdPipelineBarrier(backbuffer->mTransitionCmdBuffers[0], srcStageMask, dstStageMask, 0, 0,
-                        nullptr, 0, nullptr, 1, &imageMemoryBarrier);
-
-    mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[0]);
-
-    VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-    // insert the layout transfer into the queue and wait on the acquire
-    VkSubmitInfo submitInfo;
-    memset(&submitInfo, 0, sizeof(VkSubmitInfo));
-    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
-    submitInfo.waitSemaphoreCount = 1;
-    // Wait to make sure aquire semaphore set above has signaled.
-    submitInfo.pWaitSemaphores = &backbuffer->mAcquireSemaphore;
-    submitInfo.pWaitDstStageMask = &waitDstStageFlags;
-    submitInfo.commandBufferCount = 1;
-    submitInfo.pCommandBuffers = &backbuffer->mTransitionCmdBuffers[0];
-    submitInfo.signalSemaphoreCount = 0;
-
-    // Attach first fence to submission here so we can track when the command buffer finishes.
-    mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[0]);
-
-    // We need to notify Skia that we changed the layout of the wrapped VkImage
-    sk_sp<SkSurface> skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface;
-    GrBackendRenderTarget backendRT = skSurface->getBackendRenderTarget(
-            SkSurface::kFlushRead_BackendHandleAccess);
-    if (!backendRT.isValid()) {
-        SkASSERT(backendRT.isValid());
-        return nullptr;
-    }
-    backendRT.setVkImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
-
-    surface->mPreTransform = getPreTransformMatrix(surface->windowWidth(),
-                                                   surface->windowHeight(),
-                                                   surface->mTransform);
-
-    surface->mBackbuffer = std::move(skSurface);
-    return surface->mBackbuffer.get();
-}
-
-void VulkanManager::destroyBuffers(VulkanSurface* surface) {
-    if (surface->mBackbuffers) {
-        for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
-            mWaitForFences(mDevice, 2, surface->mBackbuffers[i].mUsageFences, true, UINT64_MAX);
-            surface->mBackbuffers[i].mImageIndex = -1;
-            mDestroySemaphore(mDevice, surface->mBackbuffers[i].mAcquireSemaphore, nullptr);
-            mDestroySemaphore(mDevice, surface->mBackbuffers[i].mRenderSemaphore, nullptr);
-            mFreeCommandBuffers(mDevice, mCommandPool, 2,
-                    surface->mBackbuffers[i].mTransitionCmdBuffers);
-            mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[0], 0);
-            mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[1], 0);
-        }
-    }
-
-    delete[] surface->mBackbuffers;
-    surface->mBackbuffers = nullptr;
-    delete[] surface->mImageInfos;
-    surface->mImageInfos = nullptr;
-    delete[] surface->mImages;
-    surface->mImages = nullptr;
+    // Exporting a semaphore with copy transference via vkGetSemaphoreFdKHR, has the same effect of
+    // destroying the semaphore and creating a new one with the same handle, and the payloads
+    // ownership is move to the Fd we created. Thus the semaphore is in a state that we can delete
+    // it and we don't need to wait on the command buffer we submitted to finish.
+    mDestroySemaphore(mDevice, semaphore, nullptr);
 }
 
 void VulkanManager::destroySurface(VulkanSurface* surface) {
@@ -630,271 +529,9 @@
     }
     mDeviceWaitIdle(mDevice);
 
-    destroyBuffers(surface);
-
-    if (VK_NULL_HANDLE != surface->mSwapchain) {
-        mDestroySwapchainKHR(mDevice, surface->mSwapchain, nullptr);
-        surface->mSwapchain = VK_NULL_HANDLE;
-    }
-
-    if (VK_NULL_HANDLE != surface->mVkSurface) {
-        mDestroySurfaceKHR(mInstance, surface->mVkSurface, nullptr);
-        surface->mVkSurface = VK_NULL_HANDLE;
-    }
     delete surface;
 }
 
-void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent) {
-    mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, nullptr);
-    SkASSERT(surface->mImageCount);
-    surface->mImages = new VkImage[surface->mImageCount];
-    mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, surface->mImages);
-
-    SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
-
-    // set up initial image layouts and create surfaces
-    surface->mImageInfos = new VulkanSurface::ImageInfo[surface->mImageCount];
-    for (uint32_t i = 0; i < surface->mImageCount; ++i) {
-        GrVkImageInfo info;
-        info.fImage = surface->mImages[i];
-        info.fAlloc = GrVkAlloc();
-        info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-        info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
-        info.fFormat = format;
-        info.fLevelCount = 1;
-
-        GrBackendRenderTarget backendRT(extent.width, extent.height, 0, 0, info);
-
-        VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i];
-        imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(
-                surface->mGrContext, backendRT, kTopLeft_GrSurfaceOrigin,
-                surface->mColorType, surface->mColorSpace, &props);
-    }
-
-    SkASSERT(mCommandPool != VK_NULL_HANDLE);
-
-    // set up the backbuffers
-    VkSemaphoreCreateInfo semaphoreInfo;
-    memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo));
-    semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
-    semaphoreInfo.pNext = nullptr;
-    semaphoreInfo.flags = 0;
-    VkCommandBufferAllocateInfo commandBuffersInfo;
-    memset(&commandBuffersInfo, 0, sizeof(VkCommandBufferAllocateInfo));
-    commandBuffersInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
-    commandBuffersInfo.pNext = nullptr;
-    commandBuffersInfo.commandPool = mCommandPool;
-    commandBuffersInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
-    commandBuffersInfo.commandBufferCount = 2;
-    VkFenceCreateInfo fenceInfo;
-    memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo));
-    fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
-    fenceInfo.pNext = nullptr;
-    fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
-
-    // we create one additional backbuffer structure here, because we want to
-    // give the command buffers they contain a chance to finish before we cycle back
-    surface->mBackbuffers = new VulkanSurface::BackbufferInfo[surface->mImageCount + 1];
-    for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
-        SkDEBUGCODE(VkResult res);
-        surface->mBackbuffers[i].mImageIndex = -1;
-        SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr,
-                                            &surface->mBackbuffers[i].mAcquireSemaphore);
-        SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr,
-                                            &surface->mBackbuffers[i].mRenderSemaphore);
-        SkDEBUGCODE(res =) mAllocateCommandBuffers(mDevice, &commandBuffersInfo,
-                                                   surface->mBackbuffers[i].mTransitionCmdBuffers);
-        SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr,
-                                        &surface->mBackbuffers[i].mUsageFences[0]);
-        SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr,
-                                        &surface->mBackbuffers[i].mUsageFences[1]);
-        SkASSERT(VK_SUCCESS == res);
-    }
-    surface->mCurrentBackbufferIndex = surface->mImageCount;
-}
-
-bool VulkanManager::createSwapchain(VulkanSurface* surface) {
-    // check for capabilities
-    VkSurfaceCapabilitiesKHR caps;
-    VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDevice,
-                                                            surface->mVkSurface, &caps);
-    if (VK_SUCCESS != res) {
-        return false;
-    }
-
-    uint32_t surfaceFormatCount;
-    res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface,
-                                              &surfaceFormatCount, nullptr);
-    if (VK_SUCCESS != res) {
-        return false;
-    }
-
-    FatVector<VkSurfaceFormatKHR, 4> surfaceFormats(surfaceFormatCount);
-    res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface,
-                                              &surfaceFormatCount, surfaceFormats.data());
-    if (VK_SUCCESS != res) {
-        return false;
-    }
-
-    uint32_t presentModeCount;
-    res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice,
-                                                   surface->mVkSurface, &presentModeCount, nullptr);
-    if (VK_SUCCESS != res) {
-        return false;
-    }
-
-    FatVector<VkPresentModeKHR, VK_PRESENT_MODE_RANGE_SIZE_KHR> presentModes(presentModeCount);
-    res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice,
-                                                   surface->mVkSurface, &presentModeCount,
-                                                   presentModes.data());
-    if (VK_SUCCESS != res) {
-        return false;
-    }
-
-    if (!SkToBool(caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)) {
-        return false;
-    }
-    VkSurfaceTransformFlagBitsKHR transform;
-    if (SkToBool(caps.supportedTransforms & caps.currentTransform) &&
-        !SkToBool(caps.currentTransform & VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR)) {
-        transform = caps.currentTransform;
-    } else {
-        transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
-    }
-
-    VkExtent2D extent = caps.currentExtent;
-    // clamp width; to handle currentExtent of -1 and  protect us from broken hints
-    if (extent.width < caps.minImageExtent.width) {
-        extent.width = caps.minImageExtent.width;
-    }
-    SkASSERT(extent.width <= caps.maxImageExtent.width);
-    // clamp height
-    if (extent.height < caps.minImageExtent.height) {
-        extent.height = caps.minImageExtent.height;
-    }
-    SkASSERT(extent.height <= caps.maxImageExtent.height);
-
-    VkExtent2D swapExtent = extent;
-    if (transform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
-        transform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR ||
-        transform == VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR ||
-        transform == VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR) {
-        swapExtent.width = extent.height;
-        swapExtent.height = extent.width;
-    }
-
-    surface->mWindowWidth = extent.width;
-    surface->mWindowHeight = extent.height;
-
-    uint32_t imageCount = std::max<uint32_t>(3, caps.minImageCount);
-    if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) {
-        // Application must settle for fewer images than desired:
-        imageCount = caps.maxImageCount;
-    }
-
-    // Currently Skia requires the images to be color attchments and support all transfer
-    // operations.
-    VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
-                                   VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
-                                   VK_IMAGE_USAGE_TRANSFER_DST_BIT;
-    SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags);
-
-    SkASSERT(caps.supportedCompositeAlpha &
-             (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR | VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR));
-    VkCompositeAlphaFlagBitsKHR composite_alpha =
-            (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
-                    ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
-                    : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
-
-    VkFormat surfaceFormat = VK_FORMAT_R8G8B8A8_UNORM;
-    VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
-    if (surface->mColorType == SkColorType::kRGBA_F16_SkColorType) {
-        surfaceFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
-    }
-
-    if (surface->mColorMode == ColorMode::WideColorGamut) {
-        skcms_Matrix3x3 surfaceGamut;
-        LOG_ALWAYS_FATAL_IF(!surface->mColorSpace->toXYZD50(&surfaceGamut),
-                            "Could not get gamut matrix from color space");
-        if (memcmp(&surfaceGamut, &SkNamedGamut::kSRGB, sizeof(surfaceGamut)) == 0) {
-            colorSpace = VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT;
-        } else if (memcmp(&surfaceGamut, &SkNamedGamut::kDCIP3, sizeof(surfaceGamut)) == 0) {
-            colorSpace = VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT;
-        } else {
-            LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
-        }
-    }
-
-    bool foundSurfaceFormat = false;
-    for (uint32_t i = 0; i < surfaceFormatCount; ++i) {
-        if (surfaceFormat == surfaceFormats[i].format
-                && colorSpace == surfaceFormats[i].colorSpace) {
-            foundSurfaceFormat = true;
-            break;
-        }
-    }
-
-    if (!foundSurfaceFormat) {
-        return false;
-    }
-
-    // FIFO is always available and will match what we do on GL so just pick that here.
-    VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR;
-
-    VkSwapchainCreateInfoKHR swapchainCreateInfo;
-    memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR));
-    swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
-    swapchainCreateInfo.surface = surface->mVkSurface;
-    swapchainCreateInfo.minImageCount = imageCount;
-    swapchainCreateInfo.imageFormat = surfaceFormat;
-    swapchainCreateInfo.imageColorSpace = colorSpace;
-    swapchainCreateInfo.imageExtent = swapExtent;
-    swapchainCreateInfo.imageArrayLayers = 1;
-    swapchainCreateInfo.imageUsage = usageFlags;
-
-    uint32_t queueFamilies[] = {mGraphicsQueueIndex, mPresentQueueIndex};
-    if (mGraphicsQueueIndex != mPresentQueueIndex) {
-        swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
-        swapchainCreateInfo.queueFamilyIndexCount = 2;
-        swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
-    } else {
-        swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
-        swapchainCreateInfo.queueFamilyIndexCount = 0;
-        swapchainCreateInfo.pQueueFamilyIndices = nullptr;
-    }
-
-    swapchainCreateInfo.preTransform = transform;
-    swapchainCreateInfo.compositeAlpha = composite_alpha;
-    swapchainCreateInfo.presentMode = mode;
-    swapchainCreateInfo.clipped = true;
-    swapchainCreateInfo.oldSwapchain = surface->mSwapchain;
-
-    res = mCreateSwapchainKHR(mDevice, &swapchainCreateInfo, nullptr, &surface->mSwapchain);
-    if (VK_SUCCESS != res) {
-        return false;
-    }
-
-    surface->mTransform = transform;
-
-    // destroy the old swapchain
-    if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
-        mDeviceWaitIdle(mDevice);
-
-        destroyBuffers(surface);
-
-        mDestroySwapchainKHR(mDevice, swapchainCreateInfo.oldSwapchain, nullptr);
-    }
-
-    createBuffers(surface, surfaceFormat, swapExtent);
-
-    // The window content is not updated (frozen) until a buffer of the window size is received.
-    // This prevents temporary stretching of the window after it is resized, but before the first
-    // buffer with new size is enqueued.
-    native_window_set_scaling_mode(surface->mNativeWindow, NATIVE_WINDOW_SCALING_MODE_FREEZE);
-
-    return true;
-}
-
 VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode,
                                             sk_sp<SkColorSpace> surfaceColorSpace,
                                             SkColorType surfaceColorType,
@@ -904,185 +541,8 @@
         return nullptr;
     }
 
-    VulkanSurface* surface = new VulkanSurface(colorMode, window, surfaceColorSpace,
-                                               surfaceColorType, grContext);
-
-    VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
-    memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
-    surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
-    surfaceCreateInfo.pNext = nullptr;
-    surfaceCreateInfo.flags = 0;
-    surfaceCreateInfo.window = window;
-
-    VkResult res = mCreateAndroidSurfaceKHR(mInstance, &surfaceCreateInfo, nullptr,
-            &surface->mVkSurface);
-    if (VK_SUCCESS != res) {
-        delete surface;
-        return nullptr;
-    }
-
-    SkDEBUGCODE(VkBool32 supported; res = mGetPhysicalDeviceSurfaceSupportKHR(
-            mPhysicalDevice, mPresentQueueIndex, surface->mVkSurface, &supported);
-    // All physical devices and queue families on Android must be capable of
-    // presentation with any native window.
-    SkASSERT(VK_SUCCESS == res && supported););
-
-    if (!createSwapchain(surface)) {
-        destroySurface(surface);
-        return nullptr;
-    }
-
-    return surface;
-}
-
-// Helper to know which src stage flags we need to set when transitioning to the present layout
-static VkPipelineStageFlags layoutToPipelineSrcStageFlags(const VkImageLayout layout) {
-    if (VK_IMAGE_LAYOUT_GENERAL == layout) {
-        return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
-    } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout ||
-               VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) {
-        return VK_PIPELINE_STAGE_TRANSFER_BIT;
-    } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout) {
-        return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-    } else if (VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout ||
-               VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL == layout) {
-        return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
-    } else if (VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) {
-        return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
-    } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) {
-        return VK_PIPELINE_STAGE_HOST_BIT;
-    }
-
-    SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout);
-    return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
-}
-
-// Helper to know which src access mask we need to set when transitioning to the present layout
-static VkAccessFlags layoutToSrcAccessMask(const VkImageLayout layout) {
-    VkAccessFlags flags = 0;
-    if (VK_IMAGE_LAYOUT_GENERAL == layout) {
-        flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
-                VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT |
-                VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_HOST_WRITE_BIT |
-                VK_ACCESS_HOST_READ_BIT;
-    } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) {
-        flags = VK_ACCESS_HOST_WRITE_BIT;
-    } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout) {
-        flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
-    } else if (VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout) {
-        flags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
-    } else if (VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) {
-        flags = VK_ACCESS_TRANSFER_WRITE_BIT;
-    } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout) {
-        flags = VK_ACCESS_TRANSFER_READ_BIT;
-    } else if (VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) {
-        flags = VK_ACCESS_SHADER_READ_BIT;
-    }
-    return flags;
-}
-
-void VulkanManager::swapBuffers(VulkanSurface* surface) {
-    if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
-        ATRACE_NAME("Finishing GPU work");
-        mDeviceWaitIdle(mDevice);
-    }
-
-    SkASSERT(surface->mBackbuffers);
-    VulkanSurface::BackbufferInfo* backbuffer =
-            surface->mBackbuffers + surface->mCurrentBackbufferIndex;
-
-    SkSurface* skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface.get();
-    GrBackendRenderTarget backendRT = skSurface->getBackendRenderTarget(
-            SkSurface::kFlushRead_BackendHandleAccess);
-    SkASSERT(backendRT.isValid());
-
-    GrVkImageInfo imageInfo;
-    SkAssertResult(backendRT.getVkImageInfo(&imageInfo));
-
-    // Check to make sure we never change the actually wrapped image
-    SkASSERT(imageInfo.fImage == surface->mImages[backbuffer->mImageIndex]);
-
-    // We need to transition the image to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR and make sure that all
-    // previous work is complete for before presenting. So we first add the necessary barrier here.
-    VkImageLayout layout = imageInfo.fImageLayout;
-    VkPipelineStageFlags srcStageMask = layoutToPipelineSrcStageFlags(layout);
-    VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
-    VkAccessFlags srcAccessMask = layoutToSrcAccessMask(layout);
-    VkAccessFlags dstAccessMask = 0;
-
-    VkImageMemoryBarrier imageMemoryBarrier = {
-            VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,     // sType
-            NULL,                                       // pNext
-            srcAccessMask,                              // outputMask
-            dstAccessMask,                              // inputMask
-            layout,                                     // oldLayout
-            VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,            // newLayout
-            mGraphicsQueueIndex,                        // srcQueueFamilyIndex
-            mPresentQueueIndex,                         // dstQueueFamilyIndex
-            surface->mImages[backbuffer->mImageIndex],  // image
-            {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}     // subresourceRange
-    };
-
-    mResetCommandBuffer(backbuffer->mTransitionCmdBuffers[1], 0);
-    VkCommandBufferBeginInfo info;
-    memset(&info, 0, sizeof(VkCommandBufferBeginInfo));
-    info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
-    info.flags = 0;
-    mBeginCommandBuffer(backbuffer->mTransitionCmdBuffers[1], &info);
-    mCmdPipelineBarrier(backbuffer->mTransitionCmdBuffers[1], srcStageMask, dstStageMask, 0, 0,
-                        nullptr, 0, nullptr, 1, &imageMemoryBarrier);
-    mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[1]);
-
-    surface->mImageInfos[backbuffer->mImageIndex].mImageLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
-
-    // insert the layout transfer into the queue and wait on the acquire
-    VkSubmitInfo submitInfo;
-    memset(&submitInfo, 0, sizeof(VkSubmitInfo));
-    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
-    submitInfo.waitSemaphoreCount = 0;
-    submitInfo.pWaitDstStageMask = 0;
-    submitInfo.commandBufferCount = 1;
-    submitInfo.pCommandBuffers = &backbuffer->mTransitionCmdBuffers[1];
-    submitInfo.signalSemaphoreCount = 1;
-    // When this command buffer finishes we will signal this semaphore so that we know it is now
-    // safe to present the image to the screen.
-    submitInfo.pSignalSemaphores = &backbuffer->mRenderSemaphore;
-
-    // Attach second fence to submission here so we can track when the command buffer finishes.
-    mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[1]);
-
-    // Submit present operation to present queue. We use a semaphore here to make sure all rendering
-    // to the image is complete and that the layout has been change to present on the graphics
-    // queue.
-    const VkPresentInfoKHR presentInfo = {
-            VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,  // sType
-            NULL,                                // pNext
-            1,                                   // waitSemaphoreCount
-            &backbuffer->mRenderSemaphore,       // pWaitSemaphores
-            1,                                   // swapchainCount
-            &surface->mSwapchain,                // pSwapchains
-            &backbuffer->mImageIndex,            // pImageIndices
-            NULL                                 // pResults
-    };
-
-    mQueuePresentKHR(mPresentQueue, &presentInfo);
-
-    surface->mBackbuffer.reset();
-    surface->mImageInfos[backbuffer->mImageIndex].mLastUsed = surface->mCurrentTime;
-    surface->mImageInfos[backbuffer->mImageIndex].mInvalid = false;
-    surface->mCurrentTime++;
-}
-
-int VulkanManager::getAge(VulkanSurface* surface) {
-    SkASSERT(surface->mBackbuffers);
-    VulkanSurface::BackbufferInfo* backbuffer =
-            surface->mBackbuffers + surface->mCurrentBackbufferIndex;
-    if (mSwapBehavior == SwapBehavior::Discard ||
-        surface->mImageInfos[backbuffer->mImageIndex].mInvalid) {
-        return 0;
-    }
-    uint16_t lastUsed = surface->mImageInfos[backbuffer->mImageIndex].mLastUsed;
-    return surface->mCurrentTime - lastUsed;
+    return VulkanSurface::Create(window, colorMode, surfaceColorType, surfaceColorSpace, grContext,
+                                  *this);
 }
 
 bool VulkanManager::setupDummyCommandBuffer() {
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 95c9630..c3d2891 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -28,7 +28,9 @@
 #include <ui/Fence.h>
 #include <utils/StrongPointer.h>
 #include <vk/GrVkBackendContext.h>
+#include "Frame.h"
 #include "IRenderPipeline.h"
+#include "VulkanSurface.h"
 
 class GrVkExtensions;
 
@@ -38,66 +40,6 @@
 
 class RenderThread;
 
-class VulkanSurface {
-public:
-    VulkanSurface(ColorMode colorMode, ANativeWindow* window, sk_sp<SkColorSpace> colorSpace,
-                  SkColorType colorType, GrContext* grContext)
-            : mColorMode(colorMode), mNativeWindow(window), mColorSpace(colorSpace),
-              mColorType(colorType), mGrContext(grContext) {}
-
-    sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
-
-    // The width and height are are the logical width and height for when submitting draws to the
-    // surface. In reality if the window is rotated the underlying VkImage may have the width and
-    // height swapped.
-    int windowWidth() const { return mWindowWidth; }
-    int windowHeight() const { return mWindowHeight; }
-
-    SkMatrix& preTransform() { return mPreTransform; }
-
-private:
-    friend class VulkanManager;
-    struct BackbufferInfo {
-        uint32_t mImageIndex;           // image this is associated with
-        VkSemaphore mAcquireSemaphore;  // we signal on this for acquisition of image
-        VkSemaphore mRenderSemaphore;   // we wait on this for rendering to be done
-        VkCommandBuffer
-                mTransitionCmdBuffers[2];  // to transition layout between present and render
-        // We use these fences to make sure the above Command buffers have finished their work
-        // before attempting to reuse them or destroy them.
-        VkFence mUsageFences[2];
-    };
-
-    struct ImageInfo {
-        VkImageLayout mImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-        sk_sp<SkSurface> mSurface;
-        uint16_t mLastUsed = 0;
-        bool mInvalid = true;
-    };
-
-    sk_sp<SkSurface> mBackbuffer;
-
-    VkSurfaceKHR mVkSurface = VK_NULL_HANDLE;
-    VkSwapchainKHR mSwapchain = VK_NULL_HANDLE;
-
-    BackbufferInfo* mBackbuffers = nullptr;
-    uint32_t mCurrentBackbufferIndex;
-
-    uint32_t mImageCount;
-    VkImage* mImages = nullptr;
-    ImageInfo* mImageInfos;
-    uint16_t mCurrentTime = 0;
-    ColorMode mColorMode;
-    ANativeWindow* mNativeWindow;
-    int mWindowWidth = 0;
-    int mWindowHeight = 0;
-    sk_sp<SkColorSpace> mColorSpace;
-    SkColorType mColorType;
-    VkSurfaceTransformFlagBitsKHR mTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
-    SkMatrix mPreTransform;
-    GrContext* mGrContext;
-};
-
 // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
 // which are re-used by CanvasContext. This class is created once and should be used by all vulkan
 // windowing contexts. The VulkanManager must be initialized before use.
@@ -114,33 +56,19 @@
     // Quick check to see if the VulkanManager has been initialized.
     bool hasVkContext() { return mDevice != VK_NULL_HANDLE; }
 
-    // Given a window this creates a new VkSurfaceKHR and VkSwapchain and stores them inside a new
-    // VulkanSurface object which is returned.
+    // Create and destroy functions for wrapping an ANativeWindow in a VulkanSurface
     VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode,
                                  sk_sp<SkColorSpace> surfaceColorSpace,
                                  SkColorType surfaceColorType,
                                  GrContext* grContext);
-
-    // Destroy the VulkanSurface and all associated vulkan objects.
     void destroySurface(VulkanSurface* surface);
 
+    Frame dequeueNextBuffer(VulkanSurface* surface);
+    void swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect);
+
     // Cleans up all the global state in the VulkanManger.
     void destroy();
 
-    // No work is needed to make a VulkanSurface current, and all functions require that a
-    // VulkanSurface is passed into them so we just return true here.
-    bool isCurrent(VulkanSurface* surface) { return true; }
-
-    int getAge(VulkanSurface* surface);
-
-    // Returns an SkSurface which wraps the next image returned from vkAcquireNextImageKHR. It also
-    // will transition the VkImage from a present layout to color attachment so that it can be used
-    // by the client for drawing.
-    SkSurface* getBackbufferSurface(VulkanSurface** surface);
-
-    // Presents the current VkImage.
-    void swapBuffers(VulkanSurface* surface);
-
     // Inserts a wait on fence command into the Vulkan command buffer.
     status_t fenceWait(sp<Fence>& fence);
 
@@ -153,17 +81,10 @@
     sk_sp<GrContext> createContext(const GrContextOptions& options);
 
 private:
+    friend class VulkanSurface;
     // Sets up the VkInstance and VkDevice objects. Also fills out the passed in
     // VkPhysicalDeviceFeatures struct.
     void setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&);
-
-    void destroyBuffers(VulkanSurface* surface);
-
-    bool createSwapchain(VulkanSurface* surface);
-    void createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent);
-
-    VulkanSurface::BackbufferInfo* getAvailableBackbuffer(VulkanSurface* surface);
-
     bool setupDummyCommandBuffer();
 
     // simple wrapper class that exists only to initialize a pointer to NULL
@@ -190,13 +111,6 @@
     VkPtr<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR> mGetPhysicalDeviceSurfaceFormatsKHR;
     VkPtr<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR> mGetPhysicalDeviceSurfacePresentModesKHR;
 
-    VkPtr<PFN_vkCreateSwapchainKHR> mCreateSwapchainKHR;
-    VkPtr<PFN_vkDestroySwapchainKHR> mDestroySwapchainKHR;
-    VkPtr<PFN_vkGetSwapchainImagesKHR> mGetSwapchainImagesKHR;
-    VkPtr<PFN_vkAcquireNextImageKHR> mAcquireNextImageKHR;
-    VkPtr<PFN_vkQueuePresentKHR> mQueuePresentKHR;
-    VkPtr<PFN_vkCreateSharedSwapchainsKHR> mCreateSharedSwapchainsKHR;
-
     // Instance Functions
     VkPtr<PFN_vkEnumerateInstanceVersion> mEnumerateInstanceVersion;
     VkPtr<PFN_vkEnumerateInstanceExtensionProperties> mEnumerateInstanceExtensionProperties;
@@ -207,6 +121,7 @@
     VkPtr<PFN_vkGetPhysicalDeviceProperties> mGetPhysicalDeviceProperties;
     VkPtr<PFN_vkGetPhysicalDeviceQueueFamilyProperties> mGetPhysicalDeviceQueueFamilyProperties;
     VkPtr<PFN_vkGetPhysicalDeviceFeatures2> mGetPhysicalDeviceFeatures2;
+    VkPtr<PFN_vkGetPhysicalDeviceImageFormatProperties2> mGetPhysicalDeviceImageFormatProperties2;
     VkPtr<PFN_vkCreateDevice> mCreateDevice;
     VkPtr<PFN_vkEnumerateDeviceExtensionProperties> mEnumerateDeviceExtensionProperties;
 
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
new file mode 100644
index 0000000..c03c3a8
--- /dev/null
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2019 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 "VulkanSurface.h"
+
+#include <algorithm>
+#include <SkSurface.h>
+
+#include "VulkanManager.h"
+#include "utils/TraceUtils.h"
+#include "utils/Color.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+static bool IsTransformSupported(int transform) {
+    // For now, only support pure rotations, not flip or flip-and-rotate, until we have
+    // more time to test them and build sample code. As far as I know we never actually
+    // use anything besides pure rotations anyway.
+    return transform == 0
+        || transform == NATIVE_WINDOW_TRANSFORM_ROT_90
+        || transform == NATIVE_WINDOW_TRANSFORM_ROT_180
+        || transform == NATIVE_WINDOW_TRANSFORM_ROT_270;
+}
+
+static int InvertTransform(int transform) {
+    switch (transform) {
+        case NATIVE_WINDOW_TRANSFORM_ROT_90:
+            return NATIVE_WINDOW_TRANSFORM_ROT_270;
+        case NATIVE_WINDOW_TRANSFORM_ROT_180:
+            return NATIVE_WINDOW_TRANSFORM_ROT_180;
+        case NATIVE_WINDOW_TRANSFORM_ROT_270:
+            return NATIVE_WINDOW_TRANSFORM_ROT_90;
+        default:
+            return 0;
+    }
+}
+
+static int ConvertVkTransformToNative(VkSurfaceTransformFlagsKHR transform) {
+    switch (transform) {
+        case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_ROT_270;
+        case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_ROT_180;
+        case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_ROT_90;
+        case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
+        case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR:
+        default:
+            return 0;
+    }
+}
+
+static SkMatrix GetPreTransformMatrix(SkISize windowSize, int transform) {
+    const int width = windowSize.width();
+    const int height = windowSize.height();
+
+    switch (transform) {
+        case 0:
+            return SkMatrix::I();
+        case NATIVE_WINDOW_TRANSFORM_ROT_90:
+            return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1);
+        case NATIVE_WINDOW_TRANSFORM_ROT_180:
+            return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1);
+        case NATIVE_WINDOW_TRANSFORM_ROT_270:
+            return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1);
+        default:
+            LOG_ALWAYS_FATAL("Unsupported Window Transform (%d)", transform);
+    }
+    return SkMatrix::I();
+}
+
+void VulkanSurface::ComputeWindowSizeAndTransform(WindowInfo* windowInfo, const SkISize& minSize,
+                                                   const SkISize& maxSize) {
+    SkISize& windowSize = windowInfo->size;
+
+    // clamp width & height to handle currentExtent of -1 and  protect us from broken hints
+    if (windowSize.width() < minSize.width() || windowSize.width() > maxSize.width()
+        || windowSize.height() < minSize.height() || windowSize.height() > maxSize.height()) {
+        int width = std::min(maxSize.width(), std::max(minSize.width(), windowSize.width()));
+        int height = std::min(maxSize.height(), std::max(minSize.height(), windowSize.height()));
+        ALOGE("Invalid Window Dimensions [%d, %d]; clamping to [%d, %d]",
+              windowSize.width(), windowSize.height(), width, height);
+        windowSize.set(width, height);
+    }
+
+    windowInfo->actualSize = windowSize;
+    if (windowInfo->transform & HAL_TRANSFORM_ROT_90) {
+        windowInfo->actualSize.set(windowSize.height(), windowSize.width());
+    }
+
+    windowInfo->preTransform = GetPreTransformMatrix(windowInfo->size, windowInfo->transform);
+}
+
+static bool ResetNativeWindow(ANativeWindow* window) {
+    // -- 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_UNDEQUEUED_BUFFERS. Reset to a known/default state before we
+    // attempt such queries.
+
+    int err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+    if (err != 0) {
+        ALOGW("native_window_api_connect failed: %s (%d)", strerror(-err), err);
+        return false;
+    }
+
+    // this will match what we do on GL so pick that here.
+    err = window->setSwapInterval(window, 1);
+    if (err != 0) {
+        ALOGW("native_window->setSwapInterval(1) failed: %s (%d)", strerror(-err), err);
+        return false;
+    }
+
+    err = native_window_set_shared_buffer_mode(window, false);
+    if (err != 0) {
+        ALOGW("native_window_set_shared_buffer_mode(false) failed: %s (%d)", strerror(-err), err);
+        return false;
+    }
+
+    err = native_window_set_auto_refresh(window, false);
+    if (err != 0) {
+        ALOGW("native_window_set_auto_refresh(false) failed: %s (%d)", strerror(-err), err);
+        return false;
+    }
+
+    return true;
+}
+
+class VkSurfaceAutoDeleter {
+public:
+    VkSurfaceAutoDeleter(VkInstance instance, VkSurfaceKHR surface,
+                         PFN_vkDestroySurfaceKHR destroySurfaceKHR)
+            : mInstance(instance)
+            , mSurface(surface)
+            , mDestroySurfaceKHR(destroySurfaceKHR) {}
+    ~VkSurfaceAutoDeleter() {
+        destroy();
+    }
+
+    void destroy() {
+        if (mSurface != VK_NULL_HANDLE) {
+            mDestroySurfaceKHR(mInstance, mSurface, nullptr);
+            mSurface = VK_NULL_HANDLE;
+        }
+    }
+
+private:
+    VkInstance mInstance;
+    VkSurfaceKHR mSurface;
+    PFN_vkDestroySurfaceKHR mDestroySurfaceKHR;
+};
+
+VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode,
+                                       SkColorType colorType, sk_sp<SkColorSpace> colorSpace,
+                                       GrContext* grContext, const VulkanManager& vkManager) {
+
+    VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
+    memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
+    surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
+    surfaceCreateInfo.pNext = nullptr;
+    surfaceCreateInfo.flags = 0;
+    surfaceCreateInfo.window = window;
+
+    VkSurfaceKHR vkSurface = VK_NULL_HANDLE;
+    VkResult res = vkManager.mCreateAndroidSurfaceKHR(vkManager.mInstance, &surfaceCreateInfo,
+                                                      nullptr, &vkSurface);
+    if (VK_SUCCESS != res) {
+        ALOGE("VulkanSurface::Create() vkCreateAndroidSurfaceKHR failed (%d)", res);
+        return nullptr;
+    }
+
+    VkSurfaceAutoDeleter vkSurfaceDeleter(vkManager.mInstance, vkSurface,
+                                          vkManager.mDestroySurfaceKHR);
+
+    SkDEBUGCODE(VkBool32 supported; res = vkManager.mGetPhysicalDeviceSurfaceSupportKHR(
+            vkManager.mPhysicalDevice, vkManager.mPresentQueueIndex, vkSurface, &supported);
+    // All physical devices and queue families on Android must be capable of
+    // presentation with any native window.
+    SkASSERT(VK_SUCCESS == res && supported););
+
+    // check for capabilities
+    VkSurfaceCapabilitiesKHR caps;
+    res = vkManager.mGetPhysicalDeviceSurfaceCapabilitiesKHR(vkManager.mPhysicalDevice, vkSurface,
+                                                             &caps);
+    if (VK_SUCCESS != res) {
+        ALOGE("VulkanSurface::Create() vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed (%d)", res);
+        return nullptr;
+    }
+
+    LOG_ALWAYS_FATAL_IF(0 == (caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR));
+
+    /*
+     * We must destroy the VK Surface before attempting to update the window as doing so after
+     * will cause the native window to be modified in unexpected ways.
+     */
+    vkSurfaceDeleter.destroy();
+
+    /*
+     * Populate Window Info struct
+     */
+    WindowInfo windowInfo;
+
+    windowInfo.transform = ConvertVkTransformToNative(caps.supportedTransforms);
+    windowInfo.size = SkISize::Make(caps.currentExtent.width, caps.currentExtent.height);
+
+    const SkISize minSize = SkISize::Make(caps.minImageExtent.width, caps.minImageExtent.height);
+    const SkISize maxSize = SkISize::Make(caps.maxImageExtent.width, caps.maxImageExtent.height);
+    ComputeWindowSizeAndTransform(&windowInfo, minSize, maxSize);
+
+    windowInfo.bufferCount = std::max<uint32_t>(VulkanSurface::sMaxBufferCount, caps.minImageCount);
+    if (caps.maxImageCount > 0 && windowInfo.bufferCount > caps.maxImageCount) {
+        // Application must settle for fewer images than desired:
+        windowInfo.bufferCount = caps.maxImageCount;
+    }
+
+    // Currently Skia requires the images to be color attachments and support all transfer
+    // operations.
+    VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
+                                   VK_IMAGE_USAGE_SAMPLED_BIT |
+                                   VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+                                   VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+    LOG_ALWAYS_FATAL_IF((caps.supportedUsageFlags & usageFlags) != usageFlags);
+
+    windowInfo.dataspace = HAL_DATASPACE_V0_SRGB;
+    if (colorMode == ColorMode::WideColorGamut) {
+        skcms_Matrix3x3 surfaceGamut;
+        LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&surfaceGamut),
+                            "Could not get gamut matrix from color space");
+        if (memcmp(&surfaceGamut, &SkNamedGamut::kSRGB, sizeof(surfaceGamut)) == 0) {
+            windowInfo.dataspace = HAL_DATASPACE_V0_SCRGB;
+        } else if (memcmp(&surfaceGamut, &SkNamedGamut::kDCIP3, sizeof(surfaceGamut)) == 0) {
+            windowInfo.dataspace = HAL_DATASPACE_DISPLAY_P3;
+        } else {
+            LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
+        }
+    }
+
+    windowInfo.pixelFormat = ColorTypeToPixelFormat(colorType);
+    VkFormat vkPixelFormat = VK_FORMAT_R8G8B8A8_UNORM;
+    if (windowInfo.pixelFormat == PIXEL_FORMAT_RGBA_FP16) {
+        vkPixelFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
+    }
+
+    uint64_t producerUsage =
+            AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
+    uint64_t consumerUsage;
+    native_window_get_consumer_usage(window, &consumerUsage);
+    windowInfo.windowUsageFlags = consumerUsage | producerUsage;
+
+    /*
+     * Now we attempt to modify the window!
+     */
+    if (!UpdateWindow(window, windowInfo)) {
+        return nullptr;
+    }
+
+    return new VulkanSurface(window, windowInfo, minSize, maxSize, grContext);
+}
+
+bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo) {
+    ATRACE_CALL();
+
+    if (!ResetNativeWindow(window)) {
+        return false;
+    }
+
+    // -- Configure the native window --
+    int err = native_window_set_buffers_format(window, windowInfo.pixelFormat);
+    if (err != 0) {
+        ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_format(%d) failed: %s (%d)",
+              windowInfo.pixelFormat, strerror(-err), err);
+        return false;
+    }
+
+    err = native_window_set_buffers_data_space(window, windowInfo.dataspace);
+    if (err != 0) {
+        ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_data_space(%d) "
+              "failed: %s (%d)", windowInfo.dataspace, strerror(-err), err);
+        return false;
+    }
+
+    const SkISize& size = windowInfo.actualSize;
+    err = native_window_set_buffers_dimensions(window, size.width(), size.height());
+    if (err != 0) {
+        ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_dimensions(%d,%d) "
+              "failed: %s (%d)", size.width(), size.height(), strerror(-err), err);
+        return false;
+    }
+
+    // native_window_set_buffers_transform() expects the transform the app is requesting that
+    // the compositor perform during composition. With native windows, pre-transform works by
+    // rendering with the same transform the compositor is applying (as in Vulkan), but
+    // then requesting the inverse transform, so that when the compositor does
+    // it's job the two transforms cancel each other out and the compositor ends
+    // up applying an identity transform to the app's buffer.
+    err = native_window_set_buffers_transform(window, InvertTransform(windowInfo.transform));
+    if (err != 0) {
+        ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_transform(%d) "
+              "failed: %s (%d)", windowInfo.transform, strerror(-err), err);
+        return false;
+    }
+
+    // Vulkan defaults to NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, but this is different than
+    // HWUI's expectation
+    err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE);
+    if (err != 0) {
+        ALOGE("VulkanSurface::UpdateWindow() native_window_set_scaling_mode(SCALE_TO_WINDOW) "
+              "failed: %s (%d)", strerror(-err), err);
+        return false;
+    }
+
+    // Lower layer insists that we have at least two buffers.
+    err = native_window_set_buffer_count(window, std::max(2, windowInfo.bufferCount));
+    if (err != 0) {
+        ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%d) failed: %s (%d)",
+              windowInfo.bufferCount, strerror(-err), err);
+        return false;
+    }
+
+    err = native_window_set_usage(window, windowInfo.windowUsageFlags);
+    if (err != 0) {
+        ALOGE("VulkanSurface::UpdateWindow() native_window_set_usage failed: %s (%d)",
+              strerror(-err), err);
+        return false;
+    }
+
+    return err == 0;
+}
+
+VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo,
+                               SkISize minWindowSize, SkISize maxWindowSize, GrContext* grContext)
+        : mNativeWindow(window)
+        , mWindowInfo(windowInfo)
+        , mGrContext(grContext)
+        , mMinWindowSize(minWindowSize)
+        , mMaxWindowSize(maxWindowSize) { }
+
+VulkanSurface::~VulkanSurface() {
+    releaseBuffers();
+
+    // release the native window to be available for use by other clients
+    int err = native_window_api_disconnect(mNativeWindow.get(), NATIVE_WINDOW_API_EGL);
+    ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)", strerror(-err), err);
+}
+
+void VulkanSurface::releaseBuffers() {
+    for (uint32_t i = 0; i < VulkanSurface::sMaxBufferCount; i++) {
+        VulkanSurface::NativeBufferInfo& bufferInfo = mNativeBuffers[i];
+
+        if (bufferInfo.buffer.get() != nullptr && bufferInfo.dequeued) {
+            int err = mNativeWindow->cancelBuffer(mNativeWindow.get(), bufferInfo.buffer.get(),
+                                                  bufferInfo.dequeue_fence);
+            if (err != 0) {
+                ALOGE("cancelBuffer[%u] failed during destroy: %s (%d)", i, strerror(-err), err);
+            }
+            bufferInfo.dequeued = false;
+
+            if (bufferInfo.dequeue_fence >= 0) {
+                close(bufferInfo.dequeue_fence);
+                bufferInfo.dequeue_fence = -1;
+            }
+        }
+
+        LOG_ALWAYS_FATAL_IF(bufferInfo.dequeued);
+        LOG_ALWAYS_FATAL_IF(bufferInfo.dequeue_fence != -1);
+
+        bufferInfo.skSurface.reset();
+        bufferInfo.buffer.clear();
+        bufferInfo.hasValidContents = false;
+        bufferInfo.lastPresentedCount = 0;
+    }
+}
+
+VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() {
+    // Set the dequeue index to invalid in case of error and only reset it to the correct
+    // value at the end of the function if everything dequeued correctly.
+    mDequeuedIndex = -1;
+
+    //check if the native window has been resized or rotated and update accordingly
+    SkISize newSize = SkISize::MakeEmpty();
+    int transformHint = 0;
+    mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_WIDTH, &newSize.fWidth);
+    mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_HEIGHT, &newSize.fHeight);
+    mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
+    if (newSize != mWindowInfo.actualSize || transformHint != mWindowInfo.transform) {
+        WindowInfo newWindowInfo = mWindowInfo;
+        newWindowInfo.size = newSize;
+        newWindowInfo.transform = IsTransformSupported(transformHint) ? transformHint : 0;
+        ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize);
+
+        int err = 0;
+        if (newWindowInfo.actualSize != mWindowInfo.actualSize) {
+            // reset the native buffers and update the window
+            err = native_window_set_buffers_dimensions(mNativeWindow.get(),
+                                                       newWindowInfo.actualSize.width(),
+                                                       newWindowInfo.actualSize.height());
+            if (err != 0) {
+                ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
+                      newWindowInfo.actualSize.width(),
+                      newWindowInfo.actualSize.height(), strerror(-err), err);
+                return nullptr;
+            }
+            // reset the NativeBufferInfo (including SkSurface) associated with the old buffers. The
+            // new NativeBufferInfo storage will be populated lazily as we dequeue each new buffer.
+            releaseBuffers();
+            // TODO should we ask the nativewindow to allocate buffers?
+        }
+
+        if (newWindowInfo.transform != mWindowInfo.transform) {
+            err = native_window_set_buffers_transform(mNativeWindow.get(),
+                    InvertTransform(newWindowInfo.transform));
+            if (err != 0) {
+                ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
+                      newWindowInfo.transform, strerror(-err), err);
+                newWindowInfo.transform = mWindowInfo.transform;
+                ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize);
+            }
+        }
+
+        mWindowInfo = newWindowInfo;
+    }
+
+    ANativeWindowBuffer* buffer;
+    int fence_fd;
+    int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fence_fd);
+    if (err != 0) {
+        ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
+        return nullptr;
+    }
+
+    uint32_t idx;
+    for (idx = 0; idx < mWindowInfo.bufferCount; idx++) {
+        if (mNativeBuffers[idx].buffer.get() == buffer) {
+            mNativeBuffers[idx].dequeued = true;
+            mNativeBuffers[idx].dequeue_fence = fence_fd;
+            break;
+        } else if (mNativeBuffers[idx].buffer.get() == nullptr) {
+            // increasing the number of buffers we have allocated
+            mNativeBuffers[idx].buffer = buffer;
+            mNativeBuffers[idx].dequeued = true;
+            mNativeBuffers[idx].dequeue_fence = fence_fd;
+            break;
+        }
+    }
+    if (idx == mWindowInfo.bufferCount) {
+        ALOGE("dequeueBuffer returned unrecognized buffer");
+        mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
+        return nullptr;
+    }
+
+    VulkanSurface::NativeBufferInfo* bufferInfo = &mNativeBuffers[idx];
+
+    if (bufferInfo->skSurface.get() == nullptr) {
+        bufferInfo->skSurface =
+                SkSurface::MakeFromAHardwareBuffer(mGrContext,
+                        ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()),
+                        kTopLeft_GrSurfaceOrigin, DataSpaceToColorSpace(mWindowInfo.dataspace),
+                        nullptr);
+        if (bufferInfo->skSurface.get() == nullptr) {
+            ALOGE("SkSurface::MakeFromAHardwareBuffer failed");
+            mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
+            return nullptr;
+        }
+    }
+
+    mDequeuedIndex = idx;
+    return bufferInfo;
+}
+
+bool VulkanSurface::presentCurrentBuffer(const SkRect& dirtyRect, int semaphoreFd) {
+    if (!dirtyRect.isEmpty()) {
+        SkRect transformedRect;
+        mWindowInfo.preTransform.mapRect(&transformedRect, dirtyRect);
+
+        SkIRect transformedIRect;
+        transformedRect.roundOut(&transformedIRect);
+        transformedIRect.intersect(0, 0, mWindowInfo.size.fWidth, mWindowInfo.size.fHeight);
+
+        // map to bottom-left coordinate system
+        android_native_rect_t aRect;
+        aRect.left = transformedIRect.x();
+        aRect.top = mWindowInfo.size.fHeight - (transformedIRect.y() + transformedIRect.height());
+        aRect.right = aRect.left + transformedIRect.width();
+        aRect.bottom = aRect.top - transformedIRect.height();
+
+        int err = native_window_set_surface_damage(mNativeWindow.get(), &aRect, 1);
+        ALOGE_IF(err != 0, "native_window_set_surface_damage failed: %s (%d)", strerror(-err), err);
+    }
+
+    VulkanSurface::NativeBufferInfo& currentBuffer = mNativeBuffers[mDequeuedIndex];
+    int queuedFd = (semaphoreFd != -1) ? semaphoreFd : currentBuffer.dequeue_fence;
+    int err = mNativeWindow->queueBuffer(mNativeWindow.get(), currentBuffer.buffer.get(), queuedFd);
+
+    currentBuffer.dequeued = false;
+    // queueBuffer always closes fence, even on error
+    if (err != 0) {
+        ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
+        mNativeWindow->cancelBuffer(mNativeWindow.get(), currentBuffer.buffer.get(),
+                                    currentBuffer.dequeue_fence);
+    } else {
+        currentBuffer.hasValidContents = true;
+        currentBuffer.lastPresentedCount = mPresentCount;
+        mPresentCount++;
+    }
+
+    if (currentBuffer.dequeue_fence >= 0) {
+        close(currentBuffer.dequeue_fence);
+        currentBuffer.dequeue_fence = -1;
+    }
+
+    return err == 0;
+}
+
+int VulkanSurface::getCurrentBuffersAge() {
+    VulkanSurface::NativeBufferInfo& currentBuffer = mNativeBuffers[mDequeuedIndex];
+    return currentBuffer.hasValidContents ? (mPresentCount - currentBuffer.lastPresentedCount) : 0;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h
new file mode 100644
index 0000000..4fd9cd2
--- /dev/null
+++ b/libs/hwui/renderthread/VulkanSurface.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2019 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 <system/graphics.h>
+#include <system/window.h>
+#include <vulkan/vulkan.h>
+
+#include <SkSize.h>
+#include <SkRefCnt.h>
+
+#include "IRenderPipeline.h"
+
+class SkSurface;
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class VulkanManager;
+
+class VulkanSurface {
+public:
+    static VulkanSurface* Create(ANativeWindow* window,
+                                  ColorMode colorMode,
+                                  SkColorType colorType,
+                                  sk_sp<SkColorSpace> colorSpace,
+                                  GrContext* grContext,
+                                  const VulkanManager& vkManager);
+    ~VulkanSurface();
+
+    sk_sp<SkSurface> getCurrentSkSurface() { return mNativeBuffers[mDequeuedIndex].skSurface; }
+    const SkMatrix& getCurrentPreTransform() { return mWindowInfo.preTransform; }
+
+private:
+    /*
+     * All structs/methods in this private section are specifically for use by the VulkanManager
+     *
+     */
+    friend VulkanManager;
+    struct NativeBufferInfo {
+        sk_sp<SkSurface> skSurface;
+        sp<ANativeWindowBuffer> buffer;
+        // The fence is only valid when the buffer is dequeued, and should be
+        // -1 any other time. When valid, we own the fd, and must ensure it is
+        // closed: either by closing it explicitly when queueing the buffer,
+        // or by passing ownership e.g. to ANativeWindow::cancelBuffer().
+        int dequeue_fence = -1;
+        bool dequeued = false;
+        uint32_t lastPresentedCount = 0;
+        bool hasValidContents = false;
+    };
+
+    NativeBufferInfo* dequeueNativeBuffer();
+    NativeBufferInfo* getCurrentBufferInfo() { return &mNativeBuffers[mDequeuedIndex]; }
+    bool presentCurrentBuffer(const SkRect& dirtyRect, int semaphoreFd);
+
+    // The width and height are are the logical width and height for when submitting draws to the
+    // surface. In reality if the window is rotated the underlying window may have the width and
+    // height swapped.
+    int logicalWidth() const { return mWindowInfo.size.width(); }
+    int logicalHeight() const { return mWindowInfo.size.height(); }
+    int getCurrentBuffersAge();
+
+private:
+    /*
+     * All code below this line while logically available to VulkanManager should not be treated
+     * as private to this class.
+     *
+     */
+    static constexpr int sMaxBufferCount = 3;
+
+    struct WindowInfo {
+        SkISize size;
+        PixelFormat pixelFormat;
+        android_dataspace dataspace;
+        int transform;
+        int bufferCount;
+        uint64_t windowUsageFlags;
+
+        // size of the ANativeWindow if the inverse of transform requires us to swap width/height
+        SkISize actualSize;
+        // transform to be applied to the SkSurface to map the coordinates to the provided transform
+        SkMatrix preTransform;
+    };
+
+    VulkanSurface(ANativeWindow* window,
+                  const WindowInfo& windowInfo,
+                  SkISize minWindowSize,
+                  SkISize maxWindowSize,
+                  GrContext* grContext);
+    static bool UpdateWindow(ANativeWindow* window,
+                             const WindowInfo& windowInfo);
+    static void ComputeWindowSizeAndTransform(WindowInfo* windowInfo,
+                                              const SkISize& minSize,
+                                              const SkISize& maxSize);
+    void releaseBuffers();
+
+    NativeBufferInfo mNativeBuffers[VulkanSurface::sMaxBufferCount];
+
+    sp<ANativeWindow> mNativeWindow;
+    WindowInfo mWindowInfo;
+    GrContext* mGrContext;
+
+    int mDequeuedIndex = -1;
+    uint32_t mPresentCount = 0;
+
+    const SkISize mMinWindowSize;
+    const SkISize mMaxWindowSize;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
\ No newline at end of file
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index f0a7eaa..4cd8971 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -73,6 +73,12 @@
  * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW, RAF and HEIF.
  * <p>
  * Attribute mutation is supported for JPEG image files.
+ * <p>
+ * Note: It is recommended to use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/exifinterface/media/ExifInterface.html">ExifInterface
+ * Library</a> since it is a superset of this class. In addition to the functionalities of this
+ * class, it supports parsing extra metadata such as exposure and data compression information
+ * as well as setting extra metadata such as GPS and datetime information.
  */
 public class ExifInterface {
     private static final String TAG = "ExifInterface";
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index b985b6b..4adece9 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -287,7 +287,6 @@
         <item name="*android:lockPatternStyle">@style/LockPatternStyle</item>
         <item name="passwordStyle">@style/PasswordTheme</item>
         <item name="backgroundProtectedStyle">@style/BackgroundProtectedStyle</item>
-        <item name="android:forceDarkAllowed">false</item>
 
         <!-- Needed for MediaRoute chooser dialog -->
         <item name="*android:isLightTheme">false</item>
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index 7295008..9df6ba5 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -29,6 +29,7 @@
 import android.opengl.GLSurfaceView;
 import android.os.Build;
 import android.util.Log;
+import android.util.MathUtils;
 
 import com.android.systemui.ImageWallpaper;
 import com.android.systemui.ImageWallpaper.ImageGLView;
@@ -43,6 +44,8 @@
 public class ImageWallpaperRenderer implements GLSurfaceView.Renderer,
         ImageWallpaper.WallpaperStatusListener, ImageRevealHelper.RevealStateListener {
     private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
+    private static final float SCALE_VIEWPORT_MIN = 0.98f;
+    private static final float SCALE_VIEWPORT_MAX = 1f;
 
     private final WallpaperManager mWallpaperManager;
     private final ImageGLProgram mProgram;
@@ -52,6 +55,8 @@
     private final ImageGLView mGLView;
     private float mXOffset = 0f;
     private float mYOffset = 0f;
+    private int mWidth = 0;
+    private int mHeight = 0;
 
     public ImageWallpaperRenderer(Context context, ImageGLView glView) {
         mWallpaperManager = context.getSystemService(WallpaperManager.class);
@@ -87,6 +92,8 @@
             Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height
                     + ", xOffset=" + mXOffset + ", yOffset=" + mYOffset);
         }
+        mWidth = width;
+        mHeight = height;
         mWallpaper.adjustTextureCoordinates(mWallpaperManager.getBitmap(),
                 width, height, mXOffset, mYOffset);
     }
@@ -102,10 +109,21 @@
         glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), per85);
         glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal);
 
+        scaleViewport(reveal);
         mWallpaper.useTexture();
         mWallpaper.draw();
     }
 
+    private void scaleViewport(float reveal) {
+        // Interpolation between SCALE_VIEWPORT_MAX and SCALE_VIEWPORT_MIN by reveal.
+        float vpScaled = MathUtils.lerp(SCALE_VIEWPORT_MAX, SCALE_VIEWPORT_MIN, reveal);
+        // Calculate the offset amount from the lower left corner.
+        float offset = (SCALE_VIEWPORT_MAX - vpScaled) / 2;
+        // Change the viewport.
+        glViewport((int) (mWidth * offset), (int) (mHeight * offset),
+                (int) (mWidth * vpScaled), (int) (mHeight * vpScaled));
+    }
+
     @Override
     public void onAmbientModeChanged(boolean inAmbientMode, long duration) {
         mImageRevealHelper.updateAwake(!inAmbientMode, duration);
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 9789688..1ac09ad 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -70,9 +70,8 @@
     private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
 
     // Return values for #setCurrentProxyScriptUrl
-    enum ToSendOrNotToSendBroadcast {
-        DONT_SEND_BROADCAST, DO_SEND_BROADCAST
-    }
+    public static final boolean DONT_SEND_BROADCAST = false;
+    public static final boolean DO_SEND_BROADCAST = true;
 
     private String mCurrentPac;
     @GuardedBy("mProxyLock")
@@ -176,11 +175,11 @@
      * @param proxy Proxy information that is about to be broadcast.
      * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
      */
-    synchronized ToSendOrNotToSendBroadcast setCurrentProxyScriptUrl(ProxyInfo proxy) {
+    synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
         if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
             if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
                 // Allow to send broadcast, nothing to do.
-                return ToSendOrNotToSendBroadcast.DO_SEND_BROADCAST;
+                return DO_SEND_BROADCAST;
             }
             mPacUrl = proxy.getPacFileUrl();
             mCurrentDelay = DELAY_1;
@@ -188,7 +187,7 @@
             mHasDownloaded = false;
             getAlarmManager().cancel(mPacRefreshIntent);
             bind();
-            return ToSendOrNotToSendBroadcast.DONT_SEND_BROADCAST;
+            return DONT_SEND_BROADCAST;
         } else {
             getAlarmManager().cancel(mPacRefreshIntent);
             synchronized (mProxyLock) {
@@ -204,7 +203,7 @@
                     }
                 }
             }
-            return ToSendOrNotToSendBroadcast.DO_SEND_BROADCAST;
+            return DO_SEND_BROADCAST;
         }
     }
 
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index a671287..e715890 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -208,8 +208,7 @@
     public void sendProxyBroadcast() {
         final ProxyInfo defaultProxy = getDefaultProxy();
         final ProxyInfo proxyInfo = null != defaultProxy ? defaultProxy : new ProxyInfo("", 0, "");
-        if (mPacManager.setCurrentProxyScriptUrl(proxyInfo)
-                == PacManager.ToSendOrNotToSendBroadcast.DONT_SEND_BROADCAST) {
+        if (mPacManager.setCurrentProxyScriptUrl(proxyInfo) == PacManager.DONT_SEND_BROADCAST) {
             return;
         }
         if (DBG) Slog.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 1f28a6c..6d7dff5 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -216,7 +216,7 @@
     private PackageManager mPackageManager;
 
     public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
-            SensorManager sensorManager, BrightnessMappingStrategy mapper,
+            SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper,
             int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
             int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
             long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
@@ -249,7 +249,7 @@
             new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
 
         if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
-            mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+            mLightSensor = lightSensor;
         }
 
         mActivityTaskManager = ActivityTaskManager.getService();
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 74cda9d..b79ead0 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -44,6 +44,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.MathUtils;
 import android.util.Slog;
 import android.util.TimeUtils;
@@ -58,6 +59,7 @@
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.io.PrintWriter;
+import java.util.List;
 
 /**
  * Controls the power state of the display.
@@ -474,10 +476,14 @@
             int shortTermModelTimeout = resources.getInteger(
                     com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout);
 
+            String lightSensorType = resources.getString(
+                    com.android.internal.R.string.config_displayLightSensorType);
+            Sensor lightSensor = findDisplayLightSensor(lightSensorType);
+
             mBrightnessMapper = BrightnessMappingStrategy.create(resources);
             if (mBrightnessMapper != null) {
                 mAutomaticBrightnessController = new AutomaticBrightnessController(this,
-                        handler.getLooper(), sensorManager, mBrightnessMapper,
+                        handler.getLooper(), sensorManager, lightSensor, mBrightnessMapper,
                         lightSensorWarmUpTimeConfig, mScreenBrightnessRangeMinimum,
                         mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
                         initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
@@ -530,6 +536,19 @@
         mDisplayWhiteBalanceController = displayWhiteBalanceController;
     }
 
+    private Sensor findDisplayLightSensor(String sensorType) {
+        if (!TextUtils.isEmpty(sensorType)) {
+            List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+            for (int i = 0; i < sensors.size(); i++) {
+                Sensor sensor = sensors.get(i);
+                if (sensorType.equals(sensor.getStringType())) {
+                    return sensor;
+                }
+            }
+        }
+        return mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+    }
+
     /**
      * Returns true if the proximity sensor screen-off function is available.
      */
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 92198fa..0ef2d15 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -18,6 +18,7 @@
         "services.usage",
         "guava",
         "androidx.test.rules", "hamcrest-library",
+        "compatibility-device-util-axt",
         "mockito-target-inline-minus-junit4",
         "platform-test-annotations",
         "hamcrest-library",
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 7df52b2..49d8ad0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -42,6 +42,8 @@
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
 
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
@@ -4178,7 +4180,7 @@
         mService.setNotificationAssistantAccessGrantedCallback(
                 mNotificationAssistantAccessGrantedCallback);
 
-        mService.setDefaultAssistantForUser(0);
+        runWithShellPermissionIdentity(() -> mService.setDefaultAssistantForUser(0));
 
         verify(mNotificationAssistantAccessGrantedCallback)
                 .onGranted(eq(xmlConfig), eq(0), eq(true));
@@ -4198,7 +4200,7 @@
         mService.setNotificationAssistantAccessGrantedCallback(
                 mNotificationAssistantAccessGrantedCallback);
 
-        mService.setDefaultAssistantForUser(0);
+        runWithShellPermissionIdentity(() -> mService.setDefaultAssistantForUser(0));
 
         verify(mNotificationAssistantAccessGrantedCallback)
                 .onGranted(eq(deviceConfig), eq(0), eq(true));
@@ -4219,22 +4221,22 @@
         mService.setNotificationAssistantAccessGrantedCallback(
                 mNotificationAssistantAccessGrantedCallback);
 
-        mService.setDefaultAssistantForUser(0);
+        runWithShellPermissionIdentity(() -> mService.setDefaultAssistantForUser(0));
 
         verify(mNotificationAssistantAccessGrantedCallback)
                 .onGranted(eq(xmlConfig), eq(0), eq(true));
     }
 
     private void clearDeviceConfig() {
-        DeviceConfig.resetToDefaults(
-                Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI);
+        runWithShellPermissionIdentity(() -> DeviceConfig.resetToDefaults(
+                Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI));
     }
 
     private void setDefaultAssistantInDeviceConfig(String componentName) {
-        DeviceConfig.setProperty(
+        runWithShellPermissionIdentity(() -> DeviceConfig.setProperty(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE,
                 componentName,
-                false);
+                false));
     }
 }
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
index ed8a533..a03fae0 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java
@@ -165,9 +165,7 @@
         PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
         PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                 PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-        if (enableRollback) {
-            params.setEnableRollback();
-        }
+        params.setEnableRollback(enableRollback);
         int sessionId = packageInstaller.createSession(params);
         session = packageInstaller.openSession(sessionId);
 
@@ -224,11 +222,9 @@
         if (staged) {
             multiPackageParams.setStaged();
         }
-        if (enableRollback) {
-            // TODO: Do we set this on the parent params, the child params, or
-            // both?
-            multiPackageParams.setEnableRollback();
-        }
+        // TODO: Do we set this on the parent params, the child params, or
+        // both?
+        multiPackageParams.setEnableRollback(enableRollback);
         int multiPackageId = packageInstaller.createSession(multiPackageParams);
         PackageInstaller.Session multiPackage = packageInstaller.openSession(multiPackageId);
 
@@ -242,9 +238,7 @@
             if (resourceName.endsWith(".apex")) {
                 params.setInstallAsApex();
             }
-            if (enableRollback) {
-                params.setEnableRollback();
-            }
+            params.setEnableRollback(enableRollback);
             int sessionId = packageInstaller.createSession(params);
             session = packageInstaller.openSession(sessionId);