Merge "Add glReadPixels with buffer-offset argument" into nyc-dev
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 128681f..3abd8f0 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -485,7 +485,8 @@
 /* adds a new entry to the existing zip file. */
 static bool add_zip_entry_from_fd(const std::string& entry_name, int fd) {
     if (!zip_writer) {
-        MYLOGD("Not adding zip entry %s from fd because zip_writer is not set\n", entry_name.c_str());
+        MYLOGD("Not adding zip entry %s from fd because zip_writer is not set\n",
+                entry_name.c_str());
         return false;
     }
     // Logging statement  below is useful to time how long each entry takes, but it's too verbose.
@@ -545,6 +546,7 @@
         MYLOGD("Not adding dir %s because zip_writer is not set\n", dir);
         return;
     }
+    MYLOGD("Adding dir %s (recursive: %d)\n", dir, recursive);
     DurationReporter duration_reporter(dir, NULL);
     dump_files(NULL, dir, recursive ? skip_none : is_dir, _add_file_from_fd);
 }
@@ -598,7 +600,6 @@
     dump_file("BUDDYINFO", "/proc/buddyinfo");
     dump_file("FRAGMENTATION INFO", "/d/extfrag/unusable_index");
 
-    dump_file("KERNEL WAKELOCKS", "/proc/wakelocks");
     dump_file("KERNEL WAKE SOURCES", "/d/wakeup_sources");
     dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
     dump_file("KERNEL SYNC", "/d/sync");
@@ -1065,6 +1066,7 @@
     }
 
     /* parse arguments */
+    log_args("Dumpstate command line", argc, const_cast<const char **>(argv));
     int c;
     while ((c = getopt(argc, argv, "dho:svqzpPBRV:")) != -1) {
         switch (c) {
@@ -1326,8 +1328,9 @@
 
         bool do_text_file = true;
         if (do_zip_file) {
-            MYLOGD("Adding text entry to .zip bugreport\n");
-            if (!finish_zip_file(base_name + "-" + suffix + ".txt", tmp_path, now)) {
+            std::string entry_name = base_name + "-" + suffix + ".txt";
+            MYLOGD("Adding main entry (%s) to .zip bugreport\n", entry_name.c_str());
+            if (!finish_zip_file(entry_name, tmp_path, now)) {
                 MYLOGE("Failed to finish zip file; sending text bugreport instead\n");
                 do_text_file = true;
             } else {
@@ -1335,8 +1338,8 @@
             }
         }
         if (do_text_file) {
-            MYLOGD("Generating .txt bugreport\n");
             path = bugreport_dir + "/" + base_name + "-" + suffix + ".txt";
+            MYLOGD("Generating .txt bugreport at %s from %s\n", path.c_str(), tmp_path.c_str());
             if (rename(tmp_path.c_str(), path.c_str())) {
                 MYLOGE("rename(%s, %s): %s\n", tmp_path.c_str(), path.c_str(), strerror(errno));
                 path.clear();
@@ -1352,6 +1355,7 @@
                  "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
                  "--ei", "android.intent.extra.ID", std::to_string(id),
                  "--ei", "android.intent.extra.PID", std::to_string(getpid()),
+                 "--ei", "android.intent.extra.MAX", std::to_string(weight_total),
                  "--es", "android.intent.extra.BUGREPORT", path,
                  "--es", "android.intent.extra.DUMPSTATE_LOG", log_path
             };
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index a8aea42..9c975d2 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -171,6 +171,9 @@
 /* dump eMMC Extended CSD data */
 void dump_emmc_ecsd(const char *ext_csd_path);
 
+/** logs command-line arguments */
+void log_args(const std::string& message, int argc, const char *argv[]);
+
 /*
  * Helper class used to report how long it takes for a section to finish.
  *
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 282a772..f0ae325 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -713,6 +713,7 @@
     }
     // Always terminate with NULL.
     am_args[am_index + 1] = NULL;
+    log_args("send_broadcast arguments", am_index, am_args);
     run_command_always(NULL, 5, am_args);
 }
 
@@ -1187,3 +1188,12 @@
 
     printf("\n");
 }
+
+void log_args(const std::string& message, int argc, const char *argv[]) {
+    std::string args;
+    for (int i = 0; i < argc; i++) {
+        args.append(argv[i]);
+        args.append(" ");
+    }
+    MYLOGI("%s: %s\n", message.c_str(), args.c_str());
+}
diff --git a/include/binder/IProcessInfoService.h b/include/binder/IProcessInfoService.h
index 34ce0f0..69dc9a7 100644
--- a/include/binder/IProcessInfoService.h
+++ b/include/binder/IProcessInfoService.h
@@ -44,16 +44,6 @@
 
 // ----------------------------------------------------------------------
 
-class BnProcessInfoService : public BnInterface<IProcessInfoService> {
-public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
 }; // namespace android
 
 #endif // ANDROID_I_PROCESS_INFO_SERVICE_H
diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h
index 0e4acee..61f631f 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.h
@@ -253,10 +253,25 @@
 
     static bool isExternalFormat(PixelFormat format);
 
+    struct PendingRelease {
+        PendingRelease() : isPending(false), currentTexture(-1),
+                graphicBuffer(), display(nullptr), fence(nullptr) {}
+
+        bool isPending;
+        int currentTexture;
+        sp<GraphicBuffer> graphicBuffer;
+        EGLDisplay display;
+        EGLSyncKHR fence;
+    };
+
     // This releases the buffer in the slot referenced by mCurrentTexture,
     // then updates state to refer to the BufferItem, which must be a
-    // newly-acquired buffer.
-    status_t updateAndReleaseLocked(const BufferItem& item);
+    // newly-acquired buffer. If pendingRelease is not null, the parameters
+    // which would have been passed to releaseBufferLocked upon the successful
+    // completion of the method will instead be returned to the caller, so that
+    // it may call releaseBufferLocked itself later.
+    status_t updateAndReleaseLocked(const BufferItem& item,
+            PendingRelease* pendingRelease = nullptr);
 
     // Binds mTexName and the current buffer to mTexTarget.  Uses
     // mCurrentTexture if it's set, mCurrentTextureImage if not.  If the
diff --git a/libs/binder/IProcessInfoService.cpp b/libs/binder/IProcessInfoService.cpp
index c37920d..76508b8 100644
--- a/libs/binder/IProcessInfoService.cpp
+++ b/libs/binder/IProcessInfoService.cpp
@@ -88,72 +88,4 @@
 
 // ----------------------------------------------------------------------
 
-status_t BnProcessInfoService::onTransact( uint32_t code, const Parcel& data, Parcel* reply,
-        uint32_t flags) {
-    switch(code) {
-        case GET_PROCESS_STATES_FROM_PIDS: {
-            CHECK_INTERFACE(IProcessInfoService, data, reply);
-            int32_t arrayLen = data.readInt32();
-            if (arrayLen <= 0) {
-                reply->writeNoException();
-                reply->writeInt32(0);
-                reply->writeInt32(NOT_ENOUGH_DATA);
-                return NO_ERROR;
-            }
-
-            size_t len = static_cast<size_t>(arrayLen);
-            int32_t pids[len];
-            status_t res = data.read(pids, len * sizeof(*pids));
-
-            // Ignore output array length returned in the parcel here, as the states array must
-            // always be the same length as the input PIDs array.
-            int32_t states[len];
-            for (size_t i = 0; i < len; i++) states[i] = -1;
-            if (res == NO_ERROR) {
-                res = getProcessStatesFromPids(len, /*in*/ pids, /*out*/ states);
-            }
-            reply->writeNoException();
-            reply->writeInt32Array(len, states);
-            reply->writeInt32(res);
-            return NO_ERROR;
-        } break;
-        case GET_PROCESS_STATES_AND_OOM_SCORES_FROM_PIDS: {
-            CHECK_INTERFACE(IProcessInfoService, data, reply);
-            int32_t arrayLen = data.readInt32();
-            if (arrayLen <= 0) {
-                reply->writeNoException();
-                reply->writeInt32(0);
-                reply->writeInt32(NOT_ENOUGH_DATA);
-                return NO_ERROR;
-            }
-
-            size_t len = static_cast<size_t>(arrayLen);
-            int32_t pids[len];
-            status_t res = data.read(pids, len * sizeof(*pids));
-
-            // Ignore output array length returned in the parcel here, as the
-            // states array must always be the same length as the input PIDs array.
-            int32_t states[len];
-            int32_t scores[len];
-            for (size_t i = 0; i < len; i++) {
-                states[i] = -1;
-                scores[i] = -10000;
-            }
-            if (res == NO_ERROR) {
-                res = getProcessStatesAndOomScoresFromPids(
-                        len, /*in*/ pids, /*out*/ states, /*out*/ scores);
-            }
-            reply->writeNoException();
-            reply->writeInt32Array(len, states);
-            reply->writeInt32Array(len, scores);
-            reply->writeInt32(res);
-            return NO_ERROR;
-        } break;
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
-    }
-}
-
-// ----------------------------------------------------------------------
-
 }; // namespace android
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 8466aaa..4029496 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -724,6 +724,7 @@
             "android.permission.DUMP"), pid, uid)) {
         result.appendFormat("Permission Denial: can't dump BufferQueueConsumer "
                 "from pid=%d, uid=%d\n", pid, uid);
+        android_errorWriteWithInfoLog(0x534e4554, "27046057", uid, NULL, 0);
     } else {
         mCore->dump(result, prefix);
     }
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 55059dd..149f5bd 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -370,7 +370,8 @@
     return err;
 }
 
-status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item)
+status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item,
+        PendingRelease* pendingRelease)
 {
     status_t err = NO_ERROR;
 
@@ -432,14 +433,23 @@
 
     // release old buffer
     if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
-        status_t status = releaseBufferLocked(
-                mCurrentTexture, mCurrentTextureImage->graphicBuffer(),
-                mEglDisplay, mEglSlots[mCurrentTexture].mEglFence);
-        if (status < NO_ERROR) {
-            GLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)",
-                   strerror(-status), status);
-            err = status;
-            // keep going, with error raised [?]
+        if (pendingRelease == nullptr) {
+            status_t status = releaseBufferLocked(
+                    mCurrentTexture, mCurrentTextureImage->graphicBuffer(),
+                    mEglDisplay, mEglSlots[mCurrentTexture].mEglFence);
+            if (status < NO_ERROR) {
+                GLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)",
+                        strerror(-status), status);
+                err = status;
+                // keep going, with error raised [?]
+            }
+        } else {
+            pendingRelease->currentTexture = mCurrentTexture;
+            pendingRelease->graphicBuffer =
+                    mCurrentTextureImage->graphicBuffer();
+            pendingRelease->display = mEglDisplay;
+            pendingRelease->fence = mEglSlots[mCurrentTexture].mEglFence;
+            pendingRelease->isPending = true;
         }
     }
 
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index e593a72..49f501d 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -92,7 +92,7 @@
         return -1;
 
     /* We are in the emulator, get GPU status value */
-    property_get("ro.kernel.qemu.gles",prop,"0");
+    property_get("qemu.gles",prop,"0");
     return atoi(prop);
 }
 
@@ -174,11 +174,45 @@
 #endif
 #endif
 
+static void setEmulatorGlesValue(void) {
+    char prop[PROPERTY_VALUE_MAX];
+    property_get("ro.kernel.qemu", prop, "0");
+    if (atoi(prop) != 1) return;
+
+    property_get("ro.kernel.qemu.gles",prop,"0");
+    if (atoi(prop) == 1) {
+        ALOGD("Emulator has host GPU support, qemu.gles is set to 1.");
+        property_set("qemu.gles", "1");
+        return;
+    }
+
+    // for now, checking the following
+    // directory is good enough for emulator system images
+    const char* vendor_lib_path =
+#if defined(__LP64__)
+        "/vendor/lib64/egl";
+#else
+        "/vendor/lib/egl";
+#endif
+
+    const bool has_vendor_lib = (access(vendor_lib_path, R_OK) == 0);
+    if (has_vendor_lib) {
+        ALOGD("Emulator has vendor provided software renderer, qemu.gles is set to 2.");
+        property_set("qemu.gles", "2");
+    } else {
+        ALOGD("Emulator without GPU support detected. "
+              "Fallback to legacy software renderer, qemu.gles is set to 0.");
+        property_set("qemu.gles", "0");
+    }
+}
+
 void* Loader::open(egl_connection_t* cnx)
 {
     void* dso;
     driver_t* hnd = 0;
 
+    setEmulatorGlesValue();
+
     dso = load_driver("GLES", cnx, EGL | GLESv1_CM | GLESv2);
     if (dso) {
         hnd = new driver_t(dso);
@@ -280,8 +314,6 @@
             int emulationStatus = checkGlesEmulationStatus();
             switch (emulationStatus) {
                 case 0:
-                    ALOGD("Emulator without GPU support detected. "
-                          "Fallback to legacy software renderer.");
 #if defined(__LP64__)
                     result.setTo("/system/lib64/egl/libGLES_android.so");
 #else
diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk
index d84ce42..ef17630 100644
--- a/services/sensorservice/Android.mk
+++ b/services/sensorservice/Android.mk
@@ -20,6 +20,8 @@
 
 LOCAL_CFLAGS:= -DLOG_TAG=\"SensorService\"
 
+LOCAL_CFLAGS += -Wall -Werror
+
 LOCAL_CFLAGS += -fvisibility=hidden
 
 LOCAL_SHARED_LIBRARIES := \
diff --git a/services/sensorservice/Fusion.cpp b/services/sensorservice/Fusion.cpp
index 359d289..e92b23d 100644
--- a/services/sensorservice/Fusion.cpp
+++ b/services/sensorservice/Fusion.cpp
@@ -72,8 +72,6 @@
  */
 static const float NOMINAL_GRAVITY = 9.81f;
 static const float FREE_FALL_THRESHOLD = 0.1f * (NOMINAL_GRAVITY);
-static const float FREE_FALL_THRESHOLD_SQ =
-        FREE_FALL_THRESHOLD*FREE_FALL_THRESHOLD;
 
 /*
  * The geomagnetic-field should be between 30uT and 60uT.
@@ -104,7 +102,6 @@
 static const float MIN_VALID_CROSS_PRODUCT_MAG_SQ =
     MIN_VALID_CROSS_PRODUCT_MAG*MIN_VALID_CROSS_PRODUCT_MAG;
 
-static const float W_EPS = 1e-4f;
 static const float SQRT_3 = 1.732f;
 static const float WVEC_EPS = 1e-4f/SQRT_3;
 // -----------------------------------------------------------------------
diff --git a/services/sensorservice/MostRecentEventLogger.cpp b/services/sensorservice/MostRecentEventLogger.cpp
index 0bd0e17..a5d8456 100644
--- a/services/sensorservice/MostRecentEventLogger.cpp
+++ b/services/sensorservice/MostRecentEventLogger.cpp
@@ -16,10 +16,12 @@
 
 #include "MostRecentEventLogger.h"
 
+#include <inttypes.h>
+
 namespace android {
 
 SensorService::MostRecentEventLogger::MostRecentEventLogger(int sensorType) :
-        mSensorType(sensorType), mNextInd(0) {
+        mNextInd(0), mSensorType(sensorType) {
 
     mBufSize = (sensorType == SENSOR_TYPE_STEP_COUNTER ||
                 sensorType == SENSOR_TYPE_SIGNIFICANT_MOTION ||
@@ -61,13 +63,13 @@
         }
         result.appendFormat("%d) ", eventNum++);
         if (mSensorType == SENSOR_TYPE_STEP_COUNTER) {
-            result.appendFormat("%llu,", mTrimmedSensorEventArr[i]->mStepCounter);
+            result.appendFormat("%" PRIu64 ",", mTrimmedSensorEventArr[i]->mStepCounter);
         } else {
             for (int j = 0; j < numData; ++j) {
                 result.appendFormat("%5.1f,", mTrimmedSensorEventArr[i]->mData[j]);
             }
         }
-        result.appendFormat("%lld %02d:%02d:%02d ", mTrimmedSensorEventArr[i]->mTimestamp,
+        result.appendFormat("%" PRId64 " %02d:%02d:%02d ", mTrimmedSensorEventArr[i]->mTimestamp,
                 mTrimmedSensorEventArr[i]->mHour, mTrimmedSensorEventArr[i]->mMin,
                 mTrimmedSensorEventArr[i]->mSec);
         i = (i + 1) % mBufSize;
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index d70b069..3d71aa8 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -19,6 +19,8 @@
     SurfaceFlingerConsumer.cpp \
     Transform.cpp \
     DisplayHardware/FramebufferSurface.cpp \
+    DisplayHardware/HWC2.cpp \
+    DisplayHardware/HWC2On1Adapter.cpp \
     DisplayHardware/HWComposer.cpp \
     DisplayHardware/PowerHAL.cpp \
     DisplayHardware/VirtualDisplaySurface.cpp \
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index bdf8f74..e9cbbca 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -82,7 +82,6 @@
       mFlags(),
       mPageFlipCount(),
       mIsSecure(isSecure),
-      mSecureLayerVisible(false),
       mLayerStack(NO_LAYER_STACK),
       mOrientation(),
       mPowerMode(HWC_POWER_MODE_OFF),
@@ -307,24 +306,12 @@
 
 void DisplayDevice::setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers) {
     mVisibleLayersSortedByZ = layers;
-    mSecureLayerVisible = false;
-    size_t count = layers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        const sp<Layer>& layer(layers[i]);
-        if (layer->isSecure()) {
-            mSecureLayerVisible = true;
-        }
-    }
 }
 
 const Vector< sp<Layer> >& DisplayDevice::getVisibleLayersSortedByZ() const {
     return mVisibleLayersSortedByZ;
 }
 
-bool DisplayDevice::getSecureLayerVisible() const {
-    return mSecureLayerVisible;
-}
-
 Region DisplayDevice::getDirtyRegion(bool repaintEverything) const {
     Region dirty;
     if (repaintEverything) {
@@ -506,13 +493,13 @@
     result.appendFormat(
         "+ DisplayDevice: %s\n"
         "   type=%x, hwcId=%d, layerStack=%u, (%4dx%4d), ANativeWindow=%p, orient=%2d (type=%08x), "
-        "flips=%u, isSecure=%d, secureVis=%d, powerMode=%d, activeConfig=%d, numLayers=%zu\n"
+        "flips=%u, isSecure=%d, powerMode=%d, activeConfig=%d, numLayers=%zu\n"
         "   v:[%d,%d,%d,%d], f:[%d,%d,%d,%d], s:[%d,%d,%d,%d],"
         "transform:[[%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f]]\n",
         mDisplayName.string(), mType, mHwcDisplayId,
         mLayerStack, mDisplayWidth, mDisplayHeight, mNativeWindow.get(),
         mOrientation, tr.getType(), getPageFlipCount(),
-        mIsSecure, mSecureLayerVisible, mPowerMode, mActiveConfig,
+        mIsSecure, mPowerMode, mActiveConfig,
         mVisibleLayersSortedByZ.size(),
         mViewport.left, mViewport.top, mViewport.right, mViewport.bottom,
         mFrame.left, mFrame.top, mFrame.right, mFrame.bottom,
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 8695a44..6d380d1 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -106,7 +106,6 @@
 
     void                    setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers);
     const Vector< sp<Layer> >& getVisibleLayersSortedByZ() const;
-    bool                    getSecureLayerVisible() const;
     Region                  getDirtyRegion(bool repaintEverything) const;
 
     void                    setLayerStack(uint32_t stack);
@@ -202,10 +201,6 @@
     // list of visible layers on that display
     Vector< sp<Layer> > mVisibleLayersSortedByZ;
 
-    // Whether we have a visible secure layer on this display
-    bool mSecureLayerVisible;
-
-
     /*
      * Transaction state
      */
diff --git a/services/surfaceflinger/DisplayHardware/FloatRect.h b/services/surfaceflinger/DisplayHardware/FloatRect.h
index 3b75cc0..9ad1040 100644
--- a/services/surfaceflinger/DisplayHardware/FloatRect.h
+++ b/services/surfaceflinger/DisplayHardware/FloatRect.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_SF_FLOAT_RECT
 #define ANDROID_SF_FLOAT_RECT
 
+#include <ui/Rect.h>
 #include <utils/TypeHelpers.h>
 
 namespace android {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
new file mode 100644
index 0000000..0e97a53
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -0,0 +1,992 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// #define LOG_NDEBUG 0
+
+#undef LOG_TAG
+#define LOG_TAG "HWC2"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "HWC2.h"
+
+#include "FloatRect.h"
+
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Region.h>
+
+#include <android/configuration.h>
+
+#include <inttypes.h>
+
+extern "C" {
+    static void hotplug_hook(hwc2_callback_data_t callbackData,
+            hwc2_display_t displayId, int32_t intConnected) {
+        auto device = static_cast<HWC2::Device*>(callbackData);
+        auto display = device->getDisplayById(displayId);
+        if (display) {
+            auto connected = static_cast<HWC2::Connection>(intConnected);
+            device->callHotplug(std::move(display), connected);
+        } else {
+            ALOGE("Hotplug callback called with unknown display %" PRIu64,
+                    displayId);
+        }
+    }
+
+    static void refresh_hook(hwc2_callback_data_t callbackData,
+            hwc2_display_t displayId) {
+        auto device = static_cast<HWC2::Device*>(callbackData);
+        auto display = device->getDisplayById(displayId);
+        if (display) {
+            device->callRefresh(std::move(display));
+        } else {
+            ALOGE("Refresh callback called with unknown display %" PRIu64,
+                    displayId);
+        }
+    }
+
+    static void vsync_hook(hwc2_callback_data_t callbackData,
+            hwc2_display_t displayId, int64_t timestamp) {
+        auto device = static_cast<HWC2::Device*>(callbackData);
+        auto display = device->getDisplayById(displayId);
+        if (display) {
+            device->callVsync(std::move(display), timestamp);
+        } else {
+            ALOGE("Vsync callback called with unknown display %" PRIu64,
+                    displayId);
+        }
+    }
+}
+
+using android::Fence;
+using android::FloatRect;
+using android::GraphicBuffer;
+using android::Rect;
+using android::Region;
+using android::sp;
+
+namespace HWC2 {
+
+// Device methods
+
+Device::Device(hwc2_device_t* device)
+  : mHwcDevice(device),
+    mCreateVirtualDisplay(nullptr),
+    mDestroyVirtualDisplay(nullptr),
+    mDump(nullptr),
+    mGetMaxVirtualDisplayCount(nullptr),
+    mRegisterCallback(nullptr),
+    mAcceptDisplayChanges(nullptr),
+    mCreateLayer(nullptr),
+    mDestroyLayer(nullptr),
+    mGetActiveConfig(nullptr),
+    mGetChangedCompositionTypes(nullptr),
+    mGetDisplayAttribute(nullptr),
+    mGetDisplayConfigs(nullptr),
+    mGetDisplayName(nullptr),
+    mGetDisplayRequests(nullptr),
+    mGetDisplayType(nullptr),
+    mGetDozeSupport(nullptr),
+    mGetReleaseFences(nullptr),
+    mPresentDisplay(nullptr),
+    mSetActiveConfig(nullptr),
+    mSetClientTarget(nullptr),
+    mSetOutputBuffer(nullptr),
+    mSetPowerMode(nullptr),
+    mSetVsyncEnabled(nullptr),
+    mValidateDisplay(nullptr),
+    mSetCursorPosition(nullptr),
+    mSetLayerBuffer(nullptr),
+    mSetLayerSurfaceDamage(nullptr),
+    mSetLayerBlendMode(nullptr),
+    mSetLayerColor(nullptr),
+    mSetLayerCompositionType(nullptr),
+    mSetLayerDisplayFrame(nullptr),
+    mSetLayerPlaneAlpha(nullptr),
+    mSetLayerSidebandStream(nullptr),
+    mSetLayerSourceCrop(nullptr),
+    mSetLayerTransform(nullptr),
+    mSetLayerVisibleRegion(nullptr),
+    mSetLayerZOrder(nullptr),
+    mCapabilities(),
+    mDisplays(),
+    mHotplug(),
+    mPendingHotplugs(),
+    mRefresh(),
+    mPendingRefreshes(),
+    mVsync(),
+    mPendingVsyncs()
+{
+    loadCapabilities();
+    loadFunctionPointers();
+    registerCallbacks();
+}
+
+Device::~Device()
+{
+    if (mHwcDevice == nullptr) {
+        return;
+    }
+
+    for (auto element : mDisplays) {
+        auto display = element.second;
+
+        DisplayType displayType = HWC2::DisplayType::Invalid;
+        auto error = display->getType(&displayType);
+        if (error != Error::None) {
+            ALOGE("~Device: Failed to determine type of display %" PRIu64
+                    ": %s (%d)", display->getId(), to_string(error).c_str(),
+                    static_cast<int32_t>(error));
+            continue;
+        }
+
+        if (displayType == HWC2::DisplayType::Physical) {
+            error = display->setVsyncEnabled(HWC2::Vsync::Disable);
+            if (error != Error::None) {
+                ALOGE("~Device: Failed to disable vsync for display %" PRIu64
+                        ": %s (%d)", display->getId(), to_string(error).c_str(),
+                        static_cast<int32_t>(error));
+            }
+        }
+    }
+
+    hwc2_close(mHwcDevice);
+}
+
+// Required by HWC2 device
+
+std::string Device::dump() const
+{
+    uint32_t numBytes = 0;
+    mDump(mHwcDevice, &numBytes, nullptr);
+
+    std::vector<char> buffer(numBytes);
+    mDump(mHwcDevice, &numBytes, buffer.data());
+
+    return std::string(buffer.data(), buffer.size());
+}
+
+uint32_t Device::getMaxVirtualDisplayCount() const
+{
+    return mGetMaxVirtualDisplayCount(mHwcDevice);
+}
+
+Error Device::createVirtualDisplay(uint32_t width, uint32_t height,
+        std::shared_ptr<Display>* outDisplay)
+{
+    ALOGI("Creating virtual display");
+
+    hwc2_display_t displayId = 0;
+    int32_t intError = mCreateVirtualDisplay(mHwcDevice, width, height,
+            &displayId);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+
+    ALOGI("Created virtual display");
+    *outDisplay = getDisplayById(displayId);
+    (*outDisplay)->setVirtual();
+    return Error::None;
+}
+
+void Device::registerHotplugCallback(HotplugCallback hotplug)
+{
+    ALOGV("registerHotplugCallback");
+    mHotplug = hotplug;
+    for (auto& pending : mPendingHotplugs) {
+        auto& display = pending.first;
+        auto connected = pending.second;
+        ALOGV("Sending pending hotplug(%" PRIu64 ", %s)", display->getId(),
+                to_string(connected).c_str());
+        mHotplug(std::move(display), connected);
+    }
+}
+
+void Device::registerRefreshCallback(RefreshCallback refresh)
+{
+    mRefresh = refresh;
+    for (auto& pending : mPendingRefreshes) {
+        mRefresh(std::move(pending));
+    }
+}
+
+void Device::registerVsyncCallback(VsyncCallback vsync)
+{
+    mVsync = vsync;
+    for (auto& pending : mPendingVsyncs) {
+        auto& display = pending.first;
+        auto timestamp = pending.second;
+        mVsync(std::move(display), timestamp);
+    }
+}
+
+// For use by Device callbacks
+
+void Device::callHotplug(std::shared_ptr<Display> display, Connection connected)
+{
+    if (connected == Connection::Connected) {
+        if (!display->isConnected()) {
+            display->loadConfigs();
+            display->setConnected(true);
+        }
+    } else {
+        display->setConnected(false);
+        mDisplays.erase(display->getId());
+    }
+
+    if (mHotplug) {
+        mHotplug(std::move(display), connected);
+    } else {
+        ALOGV("callHotplug called, but no valid callback registered, storing");
+        mPendingHotplugs.emplace_back(std::move(display), connected);
+    }
+}
+
+void Device::callRefresh(std::shared_ptr<Display> display)
+{
+    if (mRefresh) {
+        mRefresh(std::move(display));
+    } else {
+        ALOGV("callRefresh called, but no valid callback registered, storing");
+        mPendingRefreshes.emplace_back(std::move(display));
+    }
+}
+
+void Device::callVsync(std::shared_ptr<Display> display, nsecs_t timestamp)
+{
+    if (mVsync) {
+        mVsync(std::move(display), timestamp);
+    } else {
+        ALOGV("callVsync called, but no valid callback registered, storing");
+        mPendingVsyncs.emplace_back(std::move(display), timestamp);
+    }
+}
+
+// Other Device methods
+
+std::shared_ptr<Display> Device::getDisplayById(hwc2_display_t id) {
+    if (mDisplays.count(id) != 0) {
+        return mDisplays.at(id);
+    }
+
+    auto display = std::make_shared<Display>(*this, id);
+    mDisplays.emplace(id, display);
+    return display;
+}
+
+// Device initialization methods
+
+void Device::loadCapabilities()
+{
+    static_assert(sizeof(Capability) == sizeof(int32_t),
+            "Capability size has changed");
+    uint32_t numCapabilities = 0;
+    mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, nullptr);
+    mCapabilities.resize(numCapabilities);
+    auto asInt = reinterpret_cast<int32_t*>(mCapabilities.data());
+    mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, asInt);
+}
+
+void Device::loadFunctionPointers()
+{
+    // For all of these early returns, we log an error message inside
+    // loadFunctionPointer specifying which function failed to load
+
+    // Display function pointers
+    if(!loadFunctionPointer(FunctionDescriptor::CreateVirtualDisplay,
+            mCreateVirtualDisplay)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::DestroyVirtualDisplay,
+            mDestroyVirtualDisplay)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::Dump, mDump)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::GetMaxVirtualDisplayCount,
+            mGetMaxVirtualDisplayCount)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::RegisterCallback,
+            mRegisterCallback)) return;
+
+    // Device function pointers
+    if(!loadFunctionPointer(FunctionDescriptor::AcceptDisplayChanges,
+            mAcceptDisplayChanges)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::CreateLayer,
+            mCreateLayer)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::DestroyLayer,
+            mDestroyLayer)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::GetActiveConfig,
+            mGetActiveConfig)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::GetChangedCompositionTypes,
+            mGetChangedCompositionTypes)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::GetDisplayAttribute,
+            mGetDisplayAttribute)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::GetDisplayConfigs,
+            mGetDisplayConfigs)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::GetDisplayName,
+            mGetDisplayName)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::GetDisplayRequests,
+            mGetDisplayRequests)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::GetDisplayType,
+            mGetDisplayType)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::GetDozeSupport,
+            mGetDozeSupport)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::GetReleaseFences,
+            mGetReleaseFences)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::PresentDisplay,
+            mPresentDisplay)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetActiveConfig,
+            mSetActiveConfig)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetClientTarget,
+            mSetClientTarget)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetOutputBuffer,
+            mSetOutputBuffer)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetPowerMode,
+            mSetPowerMode)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetVsyncEnabled,
+            mSetVsyncEnabled)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::ValidateDisplay,
+            mValidateDisplay)) return;
+
+    // Layer function pointers
+    if(!loadFunctionPointer(FunctionDescriptor::SetCursorPosition,
+            mSetCursorPosition)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetLayerBuffer,
+            mSetLayerBuffer)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetLayerSurfaceDamage,
+            mSetLayerSurfaceDamage)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetLayerBlendMode,
+            mSetLayerBlendMode)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetLayerColor,
+            mSetLayerColor)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetLayerCompositionType,
+            mSetLayerCompositionType)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetLayerDisplayFrame,
+            mSetLayerDisplayFrame)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetLayerPlaneAlpha,
+            mSetLayerPlaneAlpha)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetLayerSidebandStream,
+            mSetLayerSidebandStream)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetLayerSourceCrop,
+            mSetLayerSourceCrop)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetLayerTransform,
+            mSetLayerTransform)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetLayerVisibleRegion,
+            mSetLayerVisibleRegion)) return;
+    if(!loadFunctionPointer(FunctionDescriptor::SetLayerZOrder,
+            mSetLayerZOrder)) return;
+}
+
+void Device::registerCallbacks()
+{
+    registerCallback<HWC2_PFN_HOTPLUG>(Callback::Hotplug, hotplug_hook);
+    registerCallback<HWC2_PFN_REFRESH>(Callback::Refresh, refresh_hook);
+    registerCallback<HWC2_PFN_VSYNC>(Callback::Vsync, vsync_hook);
+}
+
+
+// For use by Display
+
+void Device::destroyVirtualDisplay(hwc2_display_t display)
+{
+    ALOGI("Destroying virtual display");
+    int32_t intError = mDestroyVirtualDisplay(mHwcDevice, display);
+    auto error = static_cast<Error>(intError);
+    ALOGE_IF(error != Error::None, "destroyVirtualDisplay(%" PRIu64 ") failed:"
+            " %s (%d)", display, to_string(error).c_str(), intError);
+}
+
+// Display methods
+
+Display::Display(Device& device, hwc2_display_t id)
+  : mDevice(device),
+    mId(id),
+    mIsConnected(false),
+    mIsVirtual(false)
+{
+    ALOGV("Created display %" PRIu64, id);
+}
+
+Display::~Display()
+{
+    ALOGV("Destroyed display %" PRIu64, mId);
+    if (mIsVirtual) {
+        mDevice.destroyVirtualDisplay(mId);
+    }
+}
+
+Display::Config::Config(Display& display, hwc2_config_t id)
+  : mDisplay(display),
+    mId(id),
+    mWidth(-1),
+    mHeight(-1),
+    mVsyncPeriod(-1),
+    mDpiX(-1),
+    mDpiY(-1) {}
+
+Display::Config::Builder::Builder(Display& display, hwc2_config_t id)
+  : mConfig(new Config(display, id)) {}
+
+float Display::Config::Builder::getDefaultDensity() {
+    // Default density is based on TVs: 1080p displays get XHIGH density, lower-
+    // resolution displays get TV density. Maybe eventually we'll need to update
+    // it for 4k displays, though hopefully those will just report accurate DPI
+    // information to begin with. This is also used for virtual displays and
+    // older HWC implementations, so be careful about orientation.
+
+    auto longDimension = std::max(mConfig->mWidth, mConfig->mHeight);
+    if (longDimension >= 1080) {
+        return ACONFIGURATION_DENSITY_XHIGH;
+    } else {
+        return ACONFIGURATION_DENSITY_TV;
+    }
+}
+
+// Required by HWC2 display
+
+Error Display::acceptChanges()
+{
+    int32_t intError = mDevice.mAcceptDisplayChanges(mDevice.mHwcDevice, mId);
+    return static_cast<Error>(intError);
+}
+
+Error Display::createLayer(std::shared_ptr<Layer>* outLayer)
+{
+    hwc2_layer_t layerId = 0;
+    int32_t intError = mDevice.mCreateLayer(mDevice.mHwcDevice, mId, &layerId);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+
+    auto layer = std::make_shared<Layer>(shared_from_this(), layerId);
+    mLayers.emplace(layerId, layer);
+    *outLayer = std::move(layer);
+    return Error::None;
+}
+
+Error Display::getActiveConfig(
+        std::shared_ptr<const Display::Config>* outConfig) const
+{
+    ALOGV("[%" PRIu64 "] getActiveConfig", mId);
+    hwc2_config_t configId = 0;
+    int32_t intError = mDevice.mGetActiveConfig(mDevice.mHwcDevice, mId,
+            &configId);
+    auto error = static_cast<Error>(intError);
+
+    if (error != Error::None) {
+        return error;
+    }
+
+    if (mConfigs.count(configId) != 0) {
+        *outConfig = mConfigs.at(configId);
+    } else {
+        ALOGE("[%" PRIu64 "] getActiveConfig returned unknown config %u", mId,
+                configId);
+        // Return no error, but the caller needs to check for a null pointer to
+        // detect this case
+        *outConfig = nullptr;
+    }
+
+    return Error::None;
+}
+
+Error Display::getChangedCompositionTypes(
+        std::unordered_map<std::shared_ptr<Layer>, Composition>* outTypes)
+{
+    uint32_t numElements = 0;
+    int32_t intError = mDevice.mGetChangedCompositionTypes(mDevice.mHwcDevice,
+            mId, &numElements, nullptr, nullptr);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+
+    std::vector<hwc2_layer_t> layerIds(numElements);
+    std::vector<int32_t> types(numElements);
+    intError = mDevice.mGetChangedCompositionTypes(mDevice.mHwcDevice, mId,
+            &numElements, layerIds.data(), types.data());
+    error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+
+    outTypes->clear();
+    outTypes->reserve(numElements);
+    for (uint32_t element = 0; element < numElements; ++element) {
+        auto layer = getLayerById(layerIds[element]);
+        if (layer) {
+            auto type = static_cast<Composition>(types[element]);
+            ALOGV("getChangedCompositionTypes: adding %" PRIu64 " %s",
+                    layer->getId(), to_string(type).c_str());
+            outTypes->emplace(layer, type);
+        } else {
+            ALOGE("getChangedCompositionTypes: invalid layer %" PRIu64 " found"
+                    " on display %" PRIu64, layerIds[element], mId);
+        }
+    }
+
+    return Error::None;
+}
+
+std::vector<std::shared_ptr<const Display::Config>> Display::getConfigs() const
+{
+    std::vector<std::shared_ptr<const Config>> configs;
+    for (const auto& element : mConfigs) {
+        configs.emplace_back(element.second);
+    }
+    return configs;
+}
+
+Error Display::getName(std::string* outName) const
+{
+    uint32_t size;
+    int32_t intError = mDevice.mGetDisplayName(mDevice.mHwcDevice, mId, &size,
+            nullptr);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+
+    std::vector<char> rawName(size);
+    intError = mDevice.mGetDisplayName(mDevice.mHwcDevice, mId, &size,
+            rawName.data());
+    error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+
+    *outName = std::string(rawName.cbegin(), rawName.cend());
+    return Error::None;
+}
+
+Error Display::getRequests(HWC2::DisplayRequest* outDisplayRequests,
+        std::unordered_map<std::shared_ptr<Layer>, LayerRequest>*
+                outLayerRequests)
+{
+    int32_t intDisplayRequests = 0;
+    uint32_t numElements = 0;
+    int32_t intError = mDevice.mGetDisplayRequests(mDevice.mHwcDevice, mId,
+            &intDisplayRequests, &numElements, nullptr, nullptr);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+
+    std::vector<hwc2_layer_t> layerIds(numElements);
+    std::vector<int32_t> layerRequests(numElements);
+    intError = mDevice.mGetDisplayRequests(mDevice.mHwcDevice, mId,
+            &intDisplayRequests, &numElements, layerIds.data(),
+            layerRequests.data());
+    error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+
+    *outDisplayRequests = static_cast<DisplayRequest>(intDisplayRequests);
+    outLayerRequests->clear();
+    outLayerRequests->reserve(numElements);
+    for (uint32_t element = 0; element < numElements; ++element) {
+        auto layer = getLayerById(layerIds[element]);
+        if (layer) {
+            auto layerRequest =
+                    static_cast<LayerRequest>(layerRequests[element]);
+            outLayerRequests->emplace(layer, layerRequest);
+        } else {
+            ALOGE("getRequests: invalid layer %" PRIu64 " found on display %"
+                    PRIu64, layerIds[element], mId);
+        }
+    }
+
+    return Error::None;
+}
+
+Error Display::getType(DisplayType* outType) const
+{
+    int32_t intType = 0;
+    int32_t intError = mDevice.mGetDisplayType(mDevice.mHwcDevice, mId,
+            &intType);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+
+    *outType = static_cast<DisplayType>(intType);
+    return Error::None;
+}
+
+Error Display::supportsDoze(bool* outSupport) const
+{
+    int32_t intSupport = 0;
+    int32_t intError = mDevice.mGetDozeSupport(mDevice.mHwcDevice, mId,
+            &intSupport);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+    *outSupport = static_cast<bool>(intSupport);
+    return Error::None;
+}
+
+Error Display::getReleaseFences(
+        std::unordered_map<std::shared_ptr<Layer>, sp<Fence>>* outFences) const
+{
+    uint32_t numElements = 0;
+    int32_t intError = mDevice.mGetReleaseFences(mDevice.mHwcDevice, mId,
+            &numElements, nullptr, nullptr);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+
+    std::vector<hwc2_layer_t> layerIds(numElements);
+    std::vector<int32_t> fenceFds(numElements);
+    intError = mDevice.mGetReleaseFences(mDevice.mHwcDevice, mId, &numElements,
+            layerIds.data(), fenceFds.data());
+    error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+
+    std::unordered_map<std::shared_ptr<Layer>, sp<Fence>> releaseFences;
+    releaseFences.reserve(numElements);
+    for (uint32_t element = 0; element < numElements; ++element) {
+        auto layer = getLayerById(layerIds[element]);
+        if (layer) {
+            sp<Fence> fence(new Fence(fenceFds[element]));
+            releaseFences.emplace(std::move(layer), fence);
+        } else {
+            ALOGE("getReleaseFences: invalid layer %" PRIu64
+                    " found on display %" PRIu64, layerIds[element], mId);
+            return Error::BadLayer;
+        }
+    }
+
+    *outFences = std::move(releaseFences);
+    return Error::None;
+}
+
+Error Display::present(sp<Fence>* outRetireFence)
+{
+    int32_t retireFenceFd = 0;
+    int32_t intError = mDevice.mPresentDisplay(mDevice.mHwcDevice, mId,
+            &retireFenceFd);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        return error;
+    }
+
+    *outRetireFence = new Fence(retireFenceFd);
+    return Error::None;
+}
+
+Error Display::setActiveConfig(const std::shared_ptr<const Config>& config)
+{
+    if (config->getDisplayId() != mId) {
+        ALOGE("setActiveConfig received config %u for the wrong display %"
+                PRIu64 " (expected %" PRIu64 ")", config->getId(),
+                config->getDisplayId(), mId);
+        return Error::BadConfig;
+    }
+    int32_t intError = mDevice.mSetActiveConfig(mDevice.mHwcDevice, mId,
+            config->getId());
+    return static_cast<Error>(intError);
+}
+
+Error Display::setClientTarget(buffer_handle_t target,
+        const sp<Fence>& acquireFence, android_dataspace_t dataspace)
+{
+    int32_t fenceFd = acquireFence->dup();
+    int32_t intError = mDevice.mSetClientTarget(mDevice.mHwcDevice, mId, target,
+            fenceFd, static_cast<int32_t>(dataspace));
+    return static_cast<Error>(intError);
+}
+
+Error Display::setOutputBuffer(const sp<GraphicBuffer>& buffer,
+        const sp<Fence>& releaseFence)
+{
+    int32_t fenceFd = releaseFence->dup();
+    auto handle = buffer->getNativeBuffer()->handle;
+    int32_t intError = mDevice.mSetOutputBuffer(mDevice.mHwcDevice, mId, handle,
+            fenceFd);
+    return static_cast<Error>(intError);
+}
+
+Error Display::setPowerMode(PowerMode mode)
+{
+    auto intMode = static_cast<int32_t>(mode);
+    int32_t intError = mDevice.mSetPowerMode(mDevice.mHwcDevice, mId, intMode);
+    return static_cast<Error>(intError);
+}
+
+Error Display::setVsyncEnabled(Vsync enabled)
+{
+    auto intEnabled = static_cast<int32_t>(enabled);
+    int32_t intError = mDevice.mSetVsyncEnabled(mDevice.mHwcDevice, mId,
+            intEnabled);
+    return static_cast<Error>(intError);
+}
+
+Error Display::validate(uint32_t* outNumTypes, uint32_t* outNumRequests)
+{
+    uint32_t numTypes = 0;
+    uint32_t numRequests = 0;
+    int32_t intError = mDevice.mValidateDisplay(mDevice.mHwcDevice, mId,
+            &numTypes, &numRequests);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None && error != Error::HasChanges) {
+        return error;
+    }
+
+    *outNumTypes = numTypes;
+    *outNumRequests = numRequests;
+    return error;
+}
+
+// For use by Device
+
+int32_t Display::getAttribute(hwc2_config_t configId, Attribute attribute)
+{
+    int32_t value = 0;
+    int32_t intError = mDevice.mGetDisplayAttribute(mDevice.mHwcDevice, mId,
+            configId, static_cast<int32_t>(attribute), &value);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        ALOGE("getDisplayAttribute(%" PRIu64 ", %u, %s) failed: %s (%d)", mId,
+                configId, to_string(attribute).c_str(),
+                to_string(error).c_str(), intError);
+        return -1;
+    }
+    return value;
+}
+
+void Display::loadConfig(hwc2_config_t configId)
+{
+    ALOGV("[%" PRIu64 "] loadConfig(%u)", mId, configId);
+
+    auto config = Config::Builder(*this, configId)
+            .setWidth(getAttribute(configId, Attribute::Width))
+            .setHeight(getAttribute(configId, Attribute::Height))
+            .setVsyncPeriod(getAttribute(configId, Attribute::VsyncPeriod))
+            .setDpiX(getAttribute(configId, Attribute::DpiX))
+            .setDpiY(getAttribute(configId, Attribute::DpiY))
+            .build();
+    mConfigs.emplace(configId, std::move(config));
+}
+
+void Display::loadConfigs()
+{
+    ALOGV("[%" PRIu64 "] loadConfigs", mId);
+
+    uint32_t numConfigs = 0;
+    int32_t intError = mDevice.mGetDisplayConfigs(mDevice.mHwcDevice, mId,
+            &numConfigs, nullptr);
+    auto error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        ALOGE("[%" PRIu64 "] getDisplayConfigs [1] failed: %s (%d)", mId,
+                to_string(error).c_str(), intError);
+        return;
+    }
+
+    std::vector<hwc2_config_t> configIds(numConfigs);
+    intError = mDevice.mGetDisplayConfigs(mDevice.mHwcDevice, mId, &numConfigs,
+            configIds.data());
+    error = static_cast<Error>(intError);
+    if (error != Error::None) {
+        ALOGE("[%" PRIu64 "] getDisplayConfigs [2] failed: %s (%d)", mId,
+                to_string(error).c_str(), intError);
+        return;
+    }
+
+    for (auto configId : configIds) {
+        loadConfig(configId);
+    }
+}
+
+// For use by Layer
+
+void Display::destroyLayer(hwc2_layer_t layerId)
+{
+    int32_t intError = mDevice.mDestroyLayer(mDevice.mHwcDevice, mId, layerId);
+    auto error = static_cast<Error>(intError);
+    ALOGE_IF(error != Error::None, "destroyLayer(%" PRIu64 ", %" PRIu64 ")"
+            " failed: %s (%d)", mId, layerId, to_string(error).c_str(),
+            intError);
+    mLayers.erase(layerId);
+}
+
+// Other Display methods
+
+std::shared_ptr<Layer> Display::getLayerById(hwc2_layer_t id) const
+{
+    if (mLayers.count(id) == 0) {
+        return nullptr;
+    }
+
+    auto layer = mLayers.at(id).lock();
+    return layer;
+}
+
+// Layer methods
+
+Layer::Layer(const std::shared_ptr<Display>& display, hwc2_layer_t id)
+  : mDisplay(display),
+    mDisplayId(display->getId()),
+    mDevice(display->getDevice()),
+    mId(id)
+{
+    ALOGV("Created layer %" PRIu64 " on display %" PRIu64, id,
+            display->getId());
+}
+
+Layer::~Layer()
+{
+    auto display = mDisplay.lock();
+    if (display) {
+        display->destroyLayer(mId);
+    }
+}
+
+Error Layer::setCursorPosition(int32_t x, int32_t y)
+{
+    int32_t intError = mDevice.mSetCursorPosition(mDevice.mHwcDevice,
+            mDisplayId, mId, x, y);
+    return static_cast<Error>(intError);
+}
+
+Error Layer::setBuffer(buffer_handle_t buffer,
+        const sp<Fence>& acquireFence)
+{
+    int32_t fenceFd = acquireFence->dup();
+    int32_t intError = mDevice.mSetLayerBuffer(mDevice.mHwcDevice, mDisplayId,
+            mId, buffer, fenceFd);
+    return static_cast<Error>(intError);
+}
+
+Error Layer::setSurfaceDamage(const Region& damage)
+{
+    // We encode default full-screen damage as INVALID_RECT upstream, but as 0
+    // rects for HWC
+    int32_t intError = 0;
+    if (damage.isRect() && damage.getBounds() == Rect::INVALID_RECT) {
+        intError = mDevice.mSetLayerSurfaceDamage(mDevice.mHwcDevice,
+                mDisplayId, mId, {0, nullptr});
+    } else {
+        size_t rectCount = 0;
+        auto rectArray = damage.getArray(&rectCount);
+
+        std::vector<hwc_rect_t> hwcRects;
+        for (size_t rect = 0; rect < rectCount; ++rect) {
+            hwcRects.push_back({rectArray[rect].left, rectArray[rect].top,
+                    rectArray[rect].right, rectArray[rect].bottom});
+        }
+
+        hwc_region_t hwcRegion = {};
+        hwcRegion.numRects = rectCount;
+        hwcRegion.rects = hwcRects.data();
+
+        intError = mDevice.mSetLayerSurfaceDamage(mDevice.mHwcDevice,
+                mDisplayId, mId, hwcRegion);
+    }
+
+    return static_cast<Error>(intError);
+}
+
+Error Layer::setBlendMode(BlendMode mode)
+{
+    auto intMode = static_cast<int32_t>(mode);
+    int32_t intError = mDevice.mSetLayerBlendMode(mDevice.mHwcDevice,
+            mDisplayId, mId, intMode);
+    return static_cast<Error>(intError);
+}
+
+Error Layer::setColor(hwc_color_t color)
+{
+    int32_t intError = mDevice.mSetLayerColor(mDevice.mHwcDevice, mDisplayId,
+            mId, color);
+    return static_cast<Error>(intError);
+}
+
+Error Layer::setCompositionType(Composition type)
+{
+    auto intType = static_cast<int32_t>(type);
+    int32_t intError = mDevice.mSetLayerCompositionType(mDevice.mHwcDevice,
+            mDisplayId, mId, intType);
+    return static_cast<Error>(intError);
+}
+
+Error Layer::setDisplayFrame(const Rect& frame)
+{
+    hwc_rect_t hwcRect{frame.left, frame.top, frame.right, frame.bottom};
+    int32_t intError = mDevice.mSetLayerDisplayFrame(mDevice.mHwcDevice,
+            mDisplayId, mId, hwcRect);
+    return static_cast<Error>(intError);
+}
+
+Error Layer::setPlaneAlpha(float alpha)
+{
+    int32_t intError = mDevice.mSetLayerPlaneAlpha(mDevice.mHwcDevice,
+            mDisplayId, mId, alpha);
+    return static_cast<Error>(intError);
+}
+
+Error Layer::setSidebandStream(const native_handle_t* stream)
+{
+    int32_t intError = mDevice.mSetLayerSidebandStream(mDevice.mHwcDevice,
+            mDisplayId, mId, stream);
+    return static_cast<Error>(intError);
+}
+
+Error Layer::setSourceCrop(const FloatRect& crop)
+{
+    hwc_frect_t hwcRect{crop.left, crop.top, crop.right, crop.bottom};
+    int32_t intError = mDevice.mSetLayerSourceCrop(mDevice.mHwcDevice,
+            mDisplayId, mId, hwcRect);
+    return static_cast<Error>(intError);
+}
+
+Error Layer::setTransform(Transform transform)
+{
+    auto intTransform = static_cast<int32_t>(transform);
+    int32_t intError = mDevice.mSetLayerTransform(mDevice.mHwcDevice,
+            mDisplayId, mId, intTransform);
+    return static_cast<Error>(intError);
+}
+
+Error Layer::setVisibleRegion(const Region& region)
+{
+    size_t rectCount = 0;
+    auto rectArray = region.getArray(&rectCount);
+
+    std::vector<hwc_rect_t> hwcRects;
+    for (size_t rect = 0; rect < rectCount; ++rect) {
+        hwcRects.push_back({rectArray[rect].left, rectArray[rect].top,
+                rectArray[rect].right, rectArray[rect].bottom});
+    }
+
+    hwc_region_t hwcRegion = {};
+    hwcRegion.numRects = rectCount;
+    hwcRegion.rects = hwcRects.data();
+
+    int32_t intError = mDevice.mSetLayerVisibleRegion(mDevice.mHwcDevice,
+            mDisplayId, mId, hwcRegion);
+    return static_cast<Error>(intError);
+}
+
+Error Layer::setZOrder(uint32_t z)
+{
+    int32_t intError = mDevice.mSetLayerZOrder(mDevice.mHwcDevice, mDisplayId,
+            mId, z);
+    return static_cast<Error>(intError);
+}
+
+} // namespace HWC2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
new file mode 100644
index 0000000..a7bd28c
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef ANDROID_SF_HWC2_H
+#define ANDROID_SF_HWC2_H
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+#include <utils/Log.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#include <functional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+    class Fence;
+    class FloatRect;
+    class GraphicBuffer;
+    class Rect;
+    class Region;
+}
+
+namespace HWC2 {
+
+class Display;
+class Layer;
+
+typedef std::function<void(std::shared_ptr<Display>, Connection)>
+        HotplugCallback;
+typedef std::function<void(std::shared_ptr<Display>)> RefreshCallback;
+typedef std::function<void(std::shared_ptr<Display>, nsecs_t)> VsyncCallback;
+
+class Device
+{
+public:
+    Device(hwc2_device_t* device);
+    ~Device();
+
+    friend class HWC2::Display;
+    friend class HWC2::Layer;
+
+    // Required by HWC2
+
+    std::string dump() const;
+
+    const std::vector<Capability>& getCapabilities() const {
+        return mCapabilities;
+    };
+
+    uint32_t getMaxVirtualDisplayCount() const;
+    Error createVirtualDisplay(uint32_t width, uint32_t height,
+            std::shared_ptr<Display>* outDisplay);
+
+    void registerHotplugCallback(HotplugCallback hotplug);
+    void registerRefreshCallback(RefreshCallback refresh);
+    void registerVsyncCallback(VsyncCallback vsync);
+
+    // For use by callbacks
+
+    void callHotplug(std::shared_ptr<Display> display, Connection connected);
+    void callRefresh(std::shared_ptr<Display> display);
+    void callVsync(std::shared_ptr<Display> display, nsecs_t timestamp);
+
+    // Other Device methods
+
+    // This will create a Display if one is not found, but it will not be marked
+    // as connected
+    std::shared_ptr<Display> getDisplayById(hwc2_display_t id);
+
+private:
+    // Initialization methods
+
+    template <typename PFN>
+    [[clang::warn_unused_result]] bool loadFunctionPointer(
+            FunctionDescriptor desc, PFN& outPFN) {
+        auto intDesc = static_cast<int32_t>(desc);
+        auto pfn = mHwcDevice->getFunction(mHwcDevice, intDesc);
+        if (pfn != nullptr) {
+            outPFN = reinterpret_cast<PFN>(pfn);
+            return true;
+        } else {
+            ALOGE("Failed to load function %s", to_string(desc).c_str());
+            return false;
+        }
+    }
+
+    template <typename PFN, typename HOOK>
+    void registerCallback(Callback callback, HOOK hook) {
+        static_assert(std::is_same<PFN, HOOK>::value,
+                "Incompatible function pointer");
+        auto intCallback = static_cast<int32_t>(callback);
+        auto callbackData = static_cast<hwc2_callback_data_t>(this);
+        auto pfn = reinterpret_cast<hwc2_function_pointer_t>(hook);
+        mRegisterCallback(mHwcDevice, intCallback, callbackData, pfn);
+    }
+
+    void loadCapabilities();
+    void loadFunctionPointers();
+    void registerCallbacks();
+
+    // For use by Display
+
+    void destroyVirtualDisplay(hwc2_display_t display);
+
+    // Member variables
+
+    hwc2_device_t* mHwcDevice;
+
+    // Device function pointers
+    HWC2_PFN_CREATE_VIRTUAL_DISPLAY mCreateVirtualDisplay;
+    HWC2_PFN_DESTROY_VIRTUAL_DISPLAY mDestroyVirtualDisplay;
+    HWC2_PFN_DUMP mDump;
+    HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT mGetMaxVirtualDisplayCount;
+    HWC2_PFN_REGISTER_CALLBACK mRegisterCallback;
+
+    // Display function pointers
+    HWC2_PFN_ACCEPT_DISPLAY_CHANGES mAcceptDisplayChanges;
+    HWC2_PFN_CREATE_LAYER mCreateLayer;
+    HWC2_PFN_DESTROY_LAYER mDestroyLayer;
+    HWC2_PFN_GET_ACTIVE_CONFIG mGetActiveConfig;
+    HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES mGetChangedCompositionTypes;
+    HWC2_PFN_GET_DISPLAY_ATTRIBUTE mGetDisplayAttribute;
+    HWC2_PFN_GET_DISPLAY_CONFIGS mGetDisplayConfigs;
+    HWC2_PFN_GET_DISPLAY_NAME mGetDisplayName;
+    HWC2_PFN_GET_DISPLAY_REQUESTS mGetDisplayRequests;
+    HWC2_PFN_GET_DISPLAY_TYPE mGetDisplayType;
+    HWC2_PFN_GET_DOZE_SUPPORT mGetDozeSupport;
+    HWC2_PFN_GET_RELEASE_FENCES mGetReleaseFences;
+    HWC2_PFN_PRESENT_DISPLAY mPresentDisplay;
+    HWC2_PFN_SET_ACTIVE_CONFIG mSetActiveConfig;
+    HWC2_PFN_SET_CLIENT_TARGET mSetClientTarget;
+    HWC2_PFN_SET_OUTPUT_BUFFER mSetOutputBuffer;
+    HWC2_PFN_SET_POWER_MODE mSetPowerMode;
+    HWC2_PFN_SET_VSYNC_ENABLED mSetVsyncEnabled;
+    HWC2_PFN_VALIDATE_DISPLAY mValidateDisplay;
+
+    // Layer function pointers
+    HWC2_PFN_SET_CURSOR_POSITION mSetCursorPosition;
+    HWC2_PFN_SET_LAYER_BUFFER mSetLayerBuffer;
+    HWC2_PFN_SET_LAYER_SURFACE_DAMAGE mSetLayerSurfaceDamage;
+    HWC2_PFN_SET_LAYER_BLEND_MODE mSetLayerBlendMode;
+    HWC2_PFN_SET_LAYER_COLOR mSetLayerColor;
+    HWC2_PFN_SET_LAYER_COMPOSITION_TYPE mSetLayerCompositionType;
+    HWC2_PFN_SET_LAYER_DISPLAY_FRAME mSetLayerDisplayFrame;
+    HWC2_PFN_SET_LAYER_PLANE_ALPHA mSetLayerPlaneAlpha;
+    HWC2_PFN_SET_LAYER_SIDEBAND_STREAM mSetLayerSidebandStream;
+    HWC2_PFN_SET_LAYER_SOURCE_CROP mSetLayerSourceCrop;
+    HWC2_PFN_SET_LAYER_TRANSFORM mSetLayerTransform;
+    HWC2_PFN_SET_LAYER_VISIBLE_REGION mSetLayerVisibleRegion;
+    HWC2_PFN_SET_LAYER_Z_ORDER mSetLayerZOrder;
+
+    std::vector<Capability> mCapabilities;
+    std::unordered_map<hwc2_display_t, std::shared_ptr<Display>> mDisplays;
+
+    HotplugCallback mHotplug;
+    std::vector<std::pair<std::shared_ptr<Display>, Connection>>
+            mPendingHotplugs;
+    RefreshCallback mRefresh;
+    std::vector<std::shared_ptr<Display>> mPendingRefreshes;
+    VsyncCallback mVsync;
+    std::vector<std::pair<std::shared_ptr<Display>, nsecs_t>> mPendingVsyncs;
+};
+
+class Display : public std::enable_shared_from_this<Display>
+{
+public:
+    Display(Device& device, hwc2_display_t id);
+    ~Display();
+
+    friend class HWC2::Device;
+    friend class HWC2::Layer;
+
+    class Config
+    {
+    public:
+        class Builder
+        {
+        public:
+            Builder(Display& display, hwc2_config_t id);
+
+            std::shared_ptr<const Config> build() {
+                return std::const_pointer_cast<const Config>(
+                        std::move(mConfig));
+            }
+
+            Builder& setWidth(int32_t width) {
+                mConfig->mWidth = width;
+                return *this;
+            }
+            Builder& setHeight(int32_t height) {
+                mConfig->mHeight = height;
+                return *this;
+            }
+            Builder& setVsyncPeriod(int32_t vsyncPeriod) {
+                mConfig->mVsyncPeriod = vsyncPeriod;
+                return *this;
+            }
+            Builder& setDpiX(int32_t dpiX) {
+                if (dpiX == -1) {
+                    mConfig->mDpiX = getDefaultDensity();
+                } else {
+                    mConfig->mDpiX = dpiX / 1000.0f;
+                }
+                return *this;
+            }
+            Builder& setDpiY(int32_t dpiY) {
+                if (dpiY == -1) {
+                    mConfig->mDpiY = getDefaultDensity();
+                } else {
+                    mConfig->mDpiY = dpiY / 1000.0f;
+                }
+                return *this;
+            }
+
+        private:
+            float getDefaultDensity();
+            std::shared_ptr<Config> mConfig;
+        };
+
+        hwc2_display_t getDisplayId() const { return mDisplay.getId(); }
+        hwc2_config_t getId() const { return mId; }
+
+        int32_t getWidth() const { return mWidth; }
+        int32_t getHeight() const { return mHeight; }
+        nsecs_t getVsyncPeriod() const { return mVsyncPeriod; }
+        float getDpiX() const { return mDpiX; }
+        float getDpiY() const { return mDpiY; }
+
+    private:
+        Config(Display& display, hwc2_config_t id);
+
+        Display& mDisplay;
+        hwc2_config_t mId;
+
+        int32_t mWidth;
+        int32_t mHeight;
+        nsecs_t mVsyncPeriod;
+        float mDpiX;
+        float mDpiY;
+    };
+
+    // Required by HWC2
+
+    [[clang::warn_unused_result]] Error acceptChanges();
+    [[clang::warn_unused_result]] Error createLayer(
+            std::shared_ptr<Layer>* outLayer);
+    [[clang::warn_unused_result]] Error getActiveConfig(
+            std::shared_ptr<const Config>* outConfig) const;
+    [[clang::warn_unused_result]] Error getChangedCompositionTypes(
+            std::unordered_map<std::shared_ptr<Layer>, Composition>* outTypes);
+
+    // Doesn't call into the HWC2 device, so no errors are possible
+    std::vector<std::shared_ptr<const Config>> getConfigs() const;
+
+    [[clang::warn_unused_result]] Error getName(std::string* outName) const;
+    [[clang::warn_unused_result]] Error getRequests(
+            DisplayRequest* outDisplayRequests,
+            std::unordered_map<std::shared_ptr<Layer>, LayerRequest>*
+                    outLayerRequests);
+    [[clang::warn_unused_result]] Error getType(DisplayType* outType) const;
+    [[clang::warn_unused_result]] Error supportsDoze(bool* outSupport) const;
+    [[clang::warn_unused_result]] Error getReleaseFences(
+            std::unordered_map<std::shared_ptr<Layer>,
+                    android::sp<android::Fence>>* outFences) const;
+    [[clang::warn_unused_result]] Error present(
+            android::sp<android::Fence>* outRetireFence);
+    [[clang::warn_unused_result]] Error setActiveConfig(
+            const std::shared_ptr<const Config>& config);
+    [[clang::warn_unused_result]] Error setClientTarget(
+            buffer_handle_t target,
+            const android::sp<android::Fence>& acquireFence,
+            android_dataspace_t dataspace);
+    [[clang::warn_unused_result]] Error setOutputBuffer(
+            const android::sp<android::GraphicBuffer>& buffer,
+            const android::sp<android::Fence>& releaseFence);
+    [[clang::warn_unused_result]] Error setPowerMode(PowerMode mode);
+    [[clang::warn_unused_result]] Error setVsyncEnabled(Vsync enabled);
+    [[clang::warn_unused_result]] Error validate(uint32_t* outNumTypes,
+            uint32_t* outNumRequests);
+
+    // Other Display methods
+
+    Device& getDevice() const { return mDevice; }
+    hwc2_display_t getId() const { return mId; }
+    bool isConnected() const { return mIsConnected; }
+
+private:
+    // For use by Device
+
+    // Virtual displays are always connected
+    void setVirtual() {
+        mIsVirtual = true;
+        mIsConnected = true;
+    }
+
+    void setConnected(bool connected) { mIsConnected = connected; }
+    int32_t getAttribute(hwc2_config_t configId, Attribute attribute);
+    void loadConfig(hwc2_config_t configId);
+    void loadConfigs();
+
+    // For use by Layer
+    void destroyLayer(hwc2_layer_t layerId);
+
+    // This may fail (and return a null pointer) if no layer with this ID exists
+    // on this display
+    std::shared_ptr<Layer> getLayerById(hwc2_layer_t id) const;
+
+    // Member variables
+
+    Device& mDevice;
+    hwc2_display_t mId;
+    bool mIsConnected;
+    bool mIsVirtual;
+    std::unordered_map<hwc2_layer_t, std::weak_ptr<Layer>> mLayers;
+    std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
+};
+
+class Layer
+{
+public:
+    Layer(const std::shared_ptr<Display>& display, hwc2_layer_t id);
+    ~Layer();
+
+    bool isAbandoned() const { return mDisplay.expired(); }
+    hwc2_layer_t getId() const { return mId; }
+
+    [[clang::warn_unused_result]] Error setCursorPosition(int32_t x, int32_t y);
+    [[clang::warn_unused_result]] Error setBuffer(buffer_handle_t buffer,
+            const android::sp<android::Fence>& acquireFence);
+    [[clang::warn_unused_result]] Error setSurfaceDamage(
+            const android::Region& damage);
+
+    [[clang::warn_unused_result]] Error setBlendMode(BlendMode mode);
+    [[clang::warn_unused_result]] Error setColor(hwc_color_t color);
+    [[clang::warn_unused_result]] Error setCompositionType(Composition type);
+    [[clang::warn_unused_result]] Error setDisplayFrame(
+            const android::Rect& frame);
+    [[clang::warn_unused_result]] Error setPlaneAlpha(float alpha);
+    [[clang::warn_unused_result]] Error setSidebandStream(
+            const native_handle_t* stream);
+    [[clang::warn_unused_result]] Error setSourceCrop(
+            const android::FloatRect& crop);
+    [[clang::warn_unused_result]] Error setTransform(Transform transform);
+    [[clang::warn_unused_result]] Error setVisibleRegion(
+            const android::Region& region);
+    [[clang::warn_unused_result]] Error setZOrder(uint32_t z);
+
+private:
+    std::weak_ptr<Display> mDisplay;
+    hwc2_display_t mDisplayId;
+    Device& mDevice;
+    hwc2_layer_t mId;
+};
+
+} // namespace HWC2
+
+#endif // ANDROID_SF_HWC2_H
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
new file mode 100644
index 0000000..8cbd718
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
@@ -0,0 +1,2323 @@
+/*
+ * Copyright 2015 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.
+ */
+
+//#define LOG_NDEBUG 0
+
+#undef LOG_TAG
+#define LOG_TAG "HWC2On1Adapter"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "HWC2On1Adapter.h"
+
+#include <hardware/hwcomposer.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <cstdlib>
+#include <chrono>
+#include <inttypes.h>
+#include <sstream>
+
+using namespace std::chrono_literals;
+
+static bool operator==(const hwc_color_t& lhs, const hwc_color_t& rhs) {
+    return lhs.r == rhs.r &&
+            lhs.g == rhs.g &&
+            lhs.b == rhs.b &&
+            lhs.a == rhs.a;
+}
+
+static bool operator==(const hwc_rect_t& lhs, const hwc_rect_t& rhs) {
+    return lhs.left == rhs.left &&
+            lhs.top == rhs.top &&
+            lhs.right == rhs.right &&
+            lhs.bottom == rhs.bottom;
+}
+
+static bool operator==(const hwc_frect_t& lhs, const hwc_frect_t& rhs) {
+    return lhs.left == rhs.left &&
+            lhs.top == rhs.top &&
+            lhs.right == rhs.right &&
+            lhs.bottom == rhs.bottom;
+}
+
+template <typename T>
+static inline bool operator!=(const T& lhs, const T& rhs)
+{
+    return !(lhs == rhs);
+}
+
+static uint8_t getMinorVersion(struct hwc_composer_device_1* device)
+{
+    auto version = device->common.version & HARDWARE_API_VERSION_2_MAJ_MIN_MASK;
+    return (version >> 16) & 0xF;
+}
+
+template <typename PFN, typename T>
+static hwc2_function_pointer_t asFP(T function)
+{
+    static_assert(std::is_same<PFN, T>::value, "Incompatible function pointer");
+    return reinterpret_cast<hwc2_function_pointer_t>(function);
+}
+
+using namespace HWC2;
+
+namespace android {
+
+void HWC2On1Adapter::DisplayContentsDeleter::operator()(
+        hwc_display_contents_1_t* contents)
+{
+    if (contents != nullptr) {
+        for (size_t l = 0; l < contents->numHwLayers; ++l) {
+            auto& layer = contents->hwLayers[l];
+            std::free(const_cast<hwc_rect_t*>(layer.visibleRegionScreen.rects));
+        }
+    }
+    std::free(contents);
+}
+
+class HWC2On1Adapter::Callbacks : public hwc_procs_t {
+    public:
+        Callbacks(HWC2On1Adapter& adapter) : mAdapter(adapter) {
+            invalidate = &invalidateHook;
+            vsync = &vsyncHook;
+            hotplug = &hotplugHook;
+        }
+
+        static void invalidateHook(const hwc_procs_t* procs) {
+            auto callbacks = static_cast<const Callbacks*>(procs);
+            callbacks->mAdapter.hwc1Invalidate();
+        }
+
+        static void vsyncHook(const hwc_procs_t* procs, int display,
+                int64_t timestamp) {
+            auto callbacks = static_cast<const Callbacks*>(procs);
+            callbacks->mAdapter.hwc1Vsync(display, timestamp);
+        }
+
+        static void hotplugHook(const hwc_procs_t* procs, int display,
+                int connected) {
+            auto callbacks = static_cast<const Callbacks*>(procs);
+            callbacks->mAdapter.hwc1Hotplug(display, connected);
+        }
+
+    private:
+        HWC2On1Adapter& mAdapter;
+};
+
+static int closeHook(hw_device_t* /*device*/)
+{
+    // Do nothing, since the real work is done in the class destructor, but we
+    // need to provide a valid function pointer for hwc2_close to call
+    return 0;
+}
+
+HWC2On1Adapter::HWC2On1Adapter(hwc_composer_device_1_t* hwc1Device)
+  : mDumpString(),
+    mHwc1Device(hwc1Device),
+    mHwc1MinorVersion(getMinorVersion(hwc1Device)),
+    mHwc1SupportsVirtualDisplays(false),
+    mHwc1Callbacks(std::make_unique<Callbacks>(*this)),
+    mCapabilities(),
+    mLayers(),
+    mHwc1VirtualDisplay(),
+    mStateMutex(),
+    mCallbacks(),
+    mHasPendingInvalidate(false),
+    mPendingVsyncs(),
+    mPendingHotplugs(),
+    mDisplays(),
+    mHwc1DisplayMap()
+{
+    common.close = closeHook;
+    getCapabilities = getCapabilitiesHook;
+    getFunction = getFunctionHook;
+    populateCapabilities();
+    populatePrimary();
+    mHwc1Device->registerProcs(mHwc1Device,
+            static_cast<const hwc_procs_t*>(mHwc1Callbacks.get()));
+}
+
+HWC2On1Adapter::~HWC2On1Adapter() {
+    hwc_close_1(mHwc1Device);
+}
+
+void HWC2On1Adapter::doGetCapabilities(uint32_t* outCount,
+        int32_t* outCapabilities)
+{
+    if (outCapabilities == nullptr) {
+        *outCount = mCapabilities.size();
+        return;
+    }
+
+    auto capabilityIter = mCapabilities.cbegin();
+    for (size_t written = 0; written < *outCount; ++written) {
+        if (capabilityIter == mCapabilities.cend()) {
+            return;
+        }
+        outCapabilities[written] = static_cast<int32_t>(*capabilityIter);
+        ++capabilityIter;
+    }
+}
+
+hwc2_function_pointer_t HWC2On1Adapter::doGetFunction(
+        FunctionDescriptor descriptor)
+{
+    switch (descriptor) {
+        // Device functions
+        case FunctionDescriptor::CreateVirtualDisplay:
+            return asFP<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>(
+                    createVirtualDisplayHook);
+        case FunctionDescriptor::DestroyVirtualDisplay:
+            return asFP<HWC2_PFN_DESTROY_VIRTUAL_DISPLAY>(
+                    destroyVirtualDisplayHook);
+        case FunctionDescriptor::Dump:
+            return asFP<HWC2_PFN_DUMP>(dumpHook);
+        case FunctionDescriptor::GetMaxVirtualDisplayCount:
+            return asFP<HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT>(
+                    getMaxVirtualDisplayCountHook);
+        case FunctionDescriptor::RegisterCallback:
+            return asFP<HWC2_PFN_REGISTER_CALLBACK>(registerCallbackHook);
+
+        // Display functions
+        case FunctionDescriptor::AcceptDisplayChanges:
+            return asFP<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(
+                    displayHook<decltype(&Display::acceptChanges),
+                    &Display::acceptChanges>);
+        case FunctionDescriptor::CreateLayer:
+            return asFP<HWC2_PFN_CREATE_LAYER>(
+                    displayHook<decltype(&Display::createLayer),
+                    &Display::createLayer, hwc2_layer_t*>);
+        case FunctionDescriptor::DestroyLayer:
+            return asFP<HWC2_PFN_DESTROY_LAYER>(
+                    displayHook<decltype(&Display::destroyLayer),
+                    &Display::destroyLayer, hwc2_layer_t>);
+        case FunctionDescriptor::GetActiveConfig:
+            return asFP<HWC2_PFN_GET_ACTIVE_CONFIG>(
+                    displayHook<decltype(&Display::getActiveConfig),
+                    &Display::getActiveConfig, hwc2_config_t*>);
+        case FunctionDescriptor::GetChangedCompositionTypes:
+            return asFP<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>(
+                    displayHook<decltype(&Display::getChangedCompositionTypes),
+                    &Display::getChangedCompositionTypes, uint32_t*,
+                    hwc2_layer_t*, int32_t*>);
+        case FunctionDescriptor::GetDisplayAttribute:
+            return asFP<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>(
+                    getDisplayAttributeHook);
+        case FunctionDescriptor::GetDisplayConfigs:
+            return asFP<HWC2_PFN_GET_DISPLAY_CONFIGS>(
+                    displayHook<decltype(&Display::getConfigs),
+                    &Display::getConfigs, uint32_t*, hwc2_config_t*>);
+        case FunctionDescriptor::GetDisplayName:
+            return asFP<HWC2_PFN_GET_DISPLAY_NAME>(
+                    displayHook<decltype(&Display::getName),
+                    &Display::getName, uint32_t*, char*>);
+        case FunctionDescriptor::GetDisplayRequests:
+            return asFP<HWC2_PFN_GET_DISPLAY_REQUESTS>(
+                    displayHook<decltype(&Display::getRequests),
+                    &Display::getRequests, int32_t*, uint32_t*, hwc2_layer_t*,
+                    int32_t*>);
+        case FunctionDescriptor::GetDisplayType:
+            return asFP<HWC2_PFN_GET_DISPLAY_TYPE>(
+                    displayHook<decltype(&Display::getType),
+                    &Display::getType, int32_t*>);
+        case FunctionDescriptor::GetDozeSupport:
+            return asFP<HWC2_PFN_GET_DOZE_SUPPORT>(
+                    displayHook<decltype(&Display::getDozeSupport),
+                    &Display::getDozeSupport, int32_t*>);
+        case FunctionDescriptor::GetReleaseFences:
+            return asFP<HWC2_PFN_GET_RELEASE_FENCES>(
+                    displayHook<decltype(&Display::getReleaseFences),
+                    &Display::getReleaseFences, uint32_t*, hwc2_layer_t*,
+                    int32_t*>);
+        case FunctionDescriptor::PresentDisplay:
+            return asFP<HWC2_PFN_PRESENT_DISPLAY>(
+                    displayHook<decltype(&Display::present),
+                    &Display::present, int32_t*>);
+        case FunctionDescriptor::SetActiveConfig:
+            return asFP<HWC2_PFN_SET_ACTIVE_CONFIG>(
+                    displayHook<decltype(&Display::setActiveConfig),
+                    &Display::setActiveConfig, hwc2_config_t>);
+        case FunctionDescriptor::SetClientTarget:
+            return asFP<HWC2_PFN_SET_CLIENT_TARGET>(
+                    displayHook<decltype(&Display::setClientTarget),
+                    &Display::setClientTarget, buffer_handle_t, int32_t,
+                    int32_t>);
+        case FunctionDescriptor::SetOutputBuffer:
+            return asFP<HWC2_PFN_SET_OUTPUT_BUFFER>(
+                    displayHook<decltype(&Display::setOutputBuffer),
+                    &Display::setOutputBuffer, buffer_handle_t, int32_t>);
+        case FunctionDescriptor::SetPowerMode:
+            return asFP<HWC2_PFN_SET_POWER_MODE>(setPowerModeHook);
+        case FunctionDescriptor::SetVsyncEnabled:
+            return asFP<HWC2_PFN_SET_VSYNC_ENABLED>(setVsyncEnabledHook);
+        case FunctionDescriptor::ValidateDisplay:
+            return asFP<HWC2_PFN_VALIDATE_DISPLAY>(
+                    displayHook<decltype(&Display::validate),
+                    &Display::validate, uint32_t*, uint32_t*>);
+
+        // Layer functions
+        case FunctionDescriptor::SetCursorPosition:
+            return asFP<HWC2_PFN_SET_CURSOR_POSITION>(
+                    layerHook<decltype(&Layer::setCursorPosition),
+                    &Layer::setCursorPosition, int32_t, int32_t>);
+        case FunctionDescriptor::SetLayerBuffer:
+            return asFP<HWC2_PFN_SET_LAYER_BUFFER>(
+                    layerHook<decltype(&Layer::setBuffer), &Layer::setBuffer,
+                    buffer_handle_t, int32_t>);
+        case FunctionDescriptor::SetLayerSurfaceDamage:
+            return asFP<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>(
+                    layerHook<decltype(&Layer::setSurfaceDamage),
+                    &Layer::setSurfaceDamage, hwc_region_t>);
+
+        // Layer state functions
+        case FunctionDescriptor::SetLayerBlendMode:
+            return asFP<HWC2_PFN_SET_LAYER_BLEND_MODE>(
+                    setLayerBlendModeHook);
+        case FunctionDescriptor::SetLayerColor:
+            return asFP<HWC2_PFN_SET_LAYER_COLOR>(
+                    layerHook<decltype(&Layer::setColor), &Layer::setColor,
+                    hwc_color_t>);
+        case FunctionDescriptor::SetLayerCompositionType:
+            return asFP<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>(
+                    setLayerCompositionTypeHook);
+        case FunctionDescriptor::SetLayerDisplayFrame:
+            return asFP<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>(
+                    layerHook<decltype(&Layer::setDisplayFrame),
+                    &Layer::setDisplayFrame, hwc_rect_t>);
+        case FunctionDescriptor::SetLayerPlaneAlpha:
+            return asFP<HWC2_PFN_SET_LAYER_PLANE_ALPHA>(
+                    layerHook<decltype(&Layer::setPlaneAlpha),
+                    &Layer::setPlaneAlpha, float>);
+        case FunctionDescriptor::SetLayerSidebandStream:
+            return asFP<HWC2_PFN_SET_LAYER_SIDEBAND_STREAM>(
+                    layerHook<decltype(&Layer::setSidebandStream),
+                    &Layer::setSidebandStream, const native_handle_t*>);
+        case FunctionDescriptor::SetLayerSourceCrop:
+            return asFP<HWC2_PFN_SET_LAYER_SOURCE_CROP>(
+                    layerHook<decltype(&Layer::setSourceCrop),
+                    &Layer::setSourceCrop, hwc_frect_t>);
+        case FunctionDescriptor::SetLayerTransform:
+            return asFP<HWC2_PFN_SET_LAYER_TRANSFORM>(setLayerTransformHook);
+        case FunctionDescriptor::SetLayerVisibleRegion:
+            return asFP<HWC2_PFN_SET_LAYER_VISIBLE_REGION>(
+                    layerHook<decltype(&Layer::setVisibleRegion),
+                    &Layer::setVisibleRegion, hwc_region_t>);
+        case FunctionDescriptor::SetLayerZOrder:
+            return asFP<HWC2_PFN_SET_LAYER_Z_ORDER>(setLayerZOrderHook);
+
+        default:
+            ALOGE("doGetFunction: Unknown function descriptor: %d (%s)",
+                    static_cast<int32_t>(descriptor),
+                    to_string(descriptor).c_str());
+            return nullptr;
+    }
+}
+
+// Device functions
+
+Error HWC2On1Adapter::createVirtualDisplay(uint32_t width,
+        uint32_t height, hwc2_display_t* outDisplay)
+{
+    std::unique_lock<std::timed_mutex> lock(mStateMutex);
+
+    if (mHwc1VirtualDisplay) {
+        // We have already allocated our only HWC1 virtual display
+        ALOGE("createVirtualDisplay: HWC1 virtual display already allocated");
+        return Error::NoResources;
+    }
+
+    if (MAX_VIRTUAL_DISPLAY_DIMENSION != 0 &&
+            (width > MAX_VIRTUAL_DISPLAY_DIMENSION ||
+            height > MAX_VIRTUAL_DISPLAY_DIMENSION)) {
+        ALOGE("createVirtualDisplay: Can't create a virtual display with"
+                " a dimension > %u (tried %u x %u)",
+                MAX_VIRTUAL_DISPLAY_DIMENSION, width, height);
+        return Error::NoResources;
+    }
+
+    mHwc1VirtualDisplay = std::make_shared<HWC2On1Adapter::Display>(*this,
+            HWC2::DisplayType::Virtual);
+    mHwc1VirtualDisplay->populateConfigs(width, height);
+    const auto displayId = mHwc1VirtualDisplay->getId();
+    mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL] = displayId;
+    mHwc1VirtualDisplay->setHwc1Id(HWC_DISPLAY_VIRTUAL);
+    mDisplays.emplace(displayId, mHwc1VirtualDisplay);
+    *outDisplay = displayId;
+
+    return Error::None;
+}
+
+Error HWC2On1Adapter::destroyVirtualDisplay(hwc2_display_t displayId)
+{
+    std::unique_lock<std::timed_mutex> lock(mStateMutex);
+
+    if (!mHwc1VirtualDisplay || (mHwc1VirtualDisplay->getId() != displayId)) {
+        return Error::BadDisplay;
+    }
+
+    mHwc1VirtualDisplay.reset();
+    mHwc1DisplayMap.erase(HWC_DISPLAY_VIRTUAL);
+    mDisplays.erase(displayId);
+
+    return Error::None;
+}
+
+void HWC2On1Adapter::dump(uint32_t* outSize, char* outBuffer)
+{
+    if (outBuffer != nullptr) {
+        auto copiedBytes = mDumpString.copy(outBuffer, *outSize);
+        *outSize = static_cast<uint32_t>(copiedBytes);
+        return;
+    }
+
+    std::stringstream output;
+
+    output << "-- HWC2On1Adapter --\n";
+
+    output << "Adapting to a HWC 1." << static_cast<int>(mHwc1MinorVersion) <<
+            " device\n";
+
+    // Attempt to acquire the lock for 1 second, but proceed without the lock
+    // after that, so we can still get some information if we're deadlocked
+    std::unique_lock<std::timed_mutex> lock(mStateMutex, std::defer_lock);
+    lock.try_lock_for(1s);
+
+    if (mCapabilities.empty()) {
+        output << "Capabilities: None\n";
+    } else {
+        output << "Capabilities:\n";
+        for (auto capability : mCapabilities) {
+            output << "  " << to_string(capability) << '\n';
+        }
+    }
+
+    output << "Displays:\n";
+    for (const auto& element : mDisplays) {
+        const auto& display = element.second;
+        output << display->dump();
+    }
+    output << '\n';
+
+    if (mHwc1Device->dump) {
+        output << "HWC1 dump:\n";
+        std::vector<char> hwc1Dump(4096);
+        // Call with size - 1 to preserve a null character at the end
+        mHwc1Device->dump(mHwc1Device, hwc1Dump.data(),
+                static_cast<int>(hwc1Dump.size() - 1));
+        output << hwc1Dump.data();
+    }
+
+    mDumpString = output.str();
+    *outSize = static_cast<uint32_t>(mDumpString.size());
+}
+
+uint32_t HWC2On1Adapter::getMaxVirtualDisplayCount()
+{
+    return mHwc1SupportsVirtualDisplays ? 1 : 0;
+}
+
+static bool isValid(Callback descriptor) {
+    switch (descriptor) {
+        case Callback::Hotplug: // Fall-through
+        case Callback::Refresh: // Fall-through
+        case Callback::Vsync: return true;
+        default: return false;
+    }
+}
+
+Error HWC2On1Adapter::registerCallback(Callback descriptor,
+        hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer)
+{
+    if (!isValid(descriptor)) {
+        return Error::BadParameter;
+    }
+
+    ALOGV("registerCallback(%s, %p, %p)", to_string(descriptor).c_str(),
+            callbackData, pointer);
+
+    std::unique_lock<std::timed_mutex> lock(mStateMutex);
+
+    mCallbacks[descriptor] = {callbackData, pointer};
+
+    bool hasPendingInvalidate = false;
+    std::vector<hwc2_display_t> displayIds;
+    std::vector<std::pair<hwc2_display_t, int64_t>> pendingVsyncs;
+    std::vector<std::pair<hwc2_display_t, int>> pendingHotplugs;
+
+    if (descriptor == Callback::Refresh) {
+        hasPendingInvalidate = mHasPendingInvalidate;
+        if (hasPendingInvalidate) {
+            for (auto& displayPair : mDisplays) {
+                displayIds.emplace_back(displayPair.first);
+            }
+        }
+        mHasPendingInvalidate = false;
+    } else if (descriptor == Callback::Vsync) {
+        for (auto pending : mPendingVsyncs) {
+            auto hwc1DisplayId = pending.first;
+            if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+                ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d",
+                        hwc1DisplayId);
+                continue;
+            }
+            auto displayId = mHwc1DisplayMap[hwc1DisplayId];
+            auto timestamp = pending.second;
+            pendingVsyncs.emplace_back(displayId, timestamp);
+        }
+        mPendingVsyncs.clear();
+    } else if (descriptor == Callback::Hotplug) {
+        // Hotplug the primary display
+        pendingHotplugs.emplace_back(mHwc1DisplayMap[HWC_DISPLAY_PRIMARY],
+                static_cast<int32_t>(Connection::Connected));
+
+        for (auto pending : mPendingHotplugs) {
+            auto hwc1DisplayId = pending.first;
+            if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+                ALOGE("hwc1Hotplug: Couldn't find display for HWC1 id %d",
+                        hwc1DisplayId);
+                continue;
+            }
+            auto displayId = mHwc1DisplayMap[hwc1DisplayId];
+            auto connected = pending.second;
+            pendingHotplugs.emplace_back(displayId, connected);
+        }
+    }
+
+    // Call pending callbacks without the state lock held
+    lock.unlock();
+
+    if (hasPendingInvalidate) {
+        auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(pointer);
+        for (auto displayId : displayIds) {
+            refresh(callbackData, displayId);
+        }
+    }
+    if (!pendingVsyncs.empty()) {
+        auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(pointer);
+        for (auto& pendingVsync : pendingVsyncs) {
+            vsync(callbackData, pendingVsync.first, pendingVsync.second);
+        }
+    }
+    if (!pendingHotplugs.empty()) {
+        auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(pointer);
+        for (auto& pendingHotplug : pendingHotplugs) {
+            hotplug(callbackData, pendingHotplug.first, pendingHotplug.second);
+        }
+    }
+    return Error::None;
+}
+
+// Display functions
+
+std::atomic<hwc2_display_t> HWC2On1Adapter::Display::sNextId(1);
+
+HWC2On1Adapter::Display::Display(HWC2On1Adapter& device, HWC2::DisplayType type)
+  : mId(sNextId++),
+    mDevice(device),
+    mDirtyCount(0),
+    mStateMutex(),
+    mZIsDirty(false),
+    mHwc1RequestedContents(nullptr),
+    mHwc1ReceivedContents(nullptr),
+    mRetireFence(),
+    mChanges(),
+    mHwc1Id(-1),
+    mConfigs(),
+    mActiveConfig(nullptr),
+    mName(),
+    mType(type),
+    mPowerMode(PowerMode::Off),
+    mVsyncEnabled(Vsync::Invalid),
+    mClientTarget(),
+    mOutputBuffer(),
+    mLayers() {}
+
+Error HWC2On1Adapter::Display::acceptChanges()
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!mChanges) {
+        ALOGV("[%" PRIu64 "] acceptChanges failed, not validated", mId);
+        return Error::NotValidated;
+    }
+
+    ALOGV("[%" PRIu64 "] acceptChanges", mId);
+
+    for (auto& change : mChanges->getTypeChanges()) {
+        auto layerId = change.first;
+        auto type = change.second;
+        auto layer = mDevice.mLayers[layerId];
+        layer->setCompositionType(type);
+    }
+
+    mChanges->clearTypeChanges();
+
+    mHwc1RequestedContents = std::move(mHwc1ReceivedContents);
+
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::createLayer(hwc2_layer_t* outLayerId)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    auto layer = *mLayers.emplace(std::make_shared<Layer>(*this));
+    mDevice.mLayers.emplace(std::make_pair(layer->getId(), layer));
+    *outLayerId = layer->getId();
+    ALOGV("[%" PRIu64 "] created layer %" PRIu64, mId, *outLayerId);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::destroyLayer(hwc2_layer_t layerId)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    const auto mapLayer = mDevice.mLayers.find(layerId);
+    if (mapLayer == mDevice.mLayers.end()) {
+        ALOGV("[%" PRIu64 "] destroyLayer(%" PRIu64 ") failed: no such layer",
+                mId, layerId);
+        return Error::BadLayer;
+    }
+    const auto layer = mapLayer->second;
+    mDevice.mLayers.erase(mapLayer);
+    const auto zRange = mLayers.equal_range(layer);
+    for (auto current = zRange.first; current != zRange.second; ++current) {
+        if (**current == *layer) {
+            current = mLayers.erase(current);
+            break;
+        }
+    }
+    ALOGV("[%" PRIu64 "] destroyed layer %" PRIu64, mId, layerId);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getActiveConfig(hwc2_config_t* outConfig)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!mActiveConfig) {
+        ALOGV("[%" PRIu64 "] getActiveConfig --> %s", mId,
+                to_string(Error::BadConfig).c_str());
+        return Error::BadConfig;
+    }
+    auto configId = mActiveConfig->getId();
+    ALOGV("[%" PRIu64 "] getActiveConfig --> %u", mId, configId);
+    *outConfig = configId;
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getAttribute(hwc2_config_t configId,
+        Attribute attribute, int32_t* outValue)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) {
+        ALOGV("[%" PRIu64 "] getAttribute failed: bad config (%u)", mId,
+                configId);
+        return Error::BadConfig;
+    }
+    *outValue = mConfigs[configId]->getAttribute(attribute);
+    ALOGV("[%" PRIu64 "] getAttribute(%u, %s) --> %d", mId, configId,
+            to_string(attribute).c_str(), *outValue);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getChangedCompositionTypes(
+        uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outTypes)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!mChanges) {
+        ALOGE("[%" PRIu64 "] getChangedCompositionTypes failed: not validated",
+                mId);
+        return Error::NotValidated;
+    }
+
+    if ((outLayers == nullptr) || (outTypes == nullptr)) {
+        *outNumElements = mChanges->getTypeChanges().size();
+        return Error::None;
+    }
+
+    uint32_t numWritten = 0;
+    for (const auto& element : mChanges->getTypeChanges()) {
+        if (numWritten == *outNumElements) {
+            break;
+        }
+        auto layerId = element.first;
+        auto intType = static_cast<int32_t>(element.second);
+        ALOGV("Adding %" PRIu64 " %s", layerId,
+                to_string(element.second).c_str());
+        outLayers[numWritten] = layerId;
+        outTypes[numWritten] = intType;
+        ++numWritten;
+    }
+    *outNumElements = numWritten;
+
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getConfigs(uint32_t* outNumConfigs,
+        hwc2_config_t* outConfigs)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!outConfigs) {
+        *outNumConfigs = mConfigs.size();
+        return Error::None;
+    }
+    uint32_t numWritten = 0;
+    for (const auto& config : mConfigs) {
+        if (numWritten == *outNumConfigs) {
+            break;
+        }
+        outConfigs[numWritten] = config->getId();
+        ++numWritten;
+    }
+    *outNumConfigs = numWritten;
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getDozeSupport(int32_t* outSupport)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (mDevice.mHwc1MinorVersion < 4 || mHwc1Id != 0) {
+        *outSupport = 0;
+    } else {
+        *outSupport = 1;
+    }
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getName(uint32_t* outSize, char* outName)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!outName) {
+        *outSize = mName.size();
+        return Error::None;
+    }
+    auto numCopied = mName.copy(outName, *outSize);
+    *outSize = numCopied;
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getReleaseFences(uint32_t* outNumElements,
+        hwc2_layer_t* outLayers, int32_t* outFences)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    uint32_t numWritten = 0;
+    bool outputsNonNull = (outLayers != nullptr) && (outFences != nullptr);
+    for (const auto& layer : mLayers) {
+        if (outputsNonNull && (numWritten == *outNumElements)) {
+            break;
+        }
+
+        auto releaseFence = layer->getReleaseFence();
+        if (releaseFence != Fence::NO_FENCE) {
+            if (outputsNonNull) {
+                outLayers[numWritten] = layer->getId();
+                outFences[numWritten] = releaseFence->dup();
+            }
+            ++numWritten;
+        }
+    }
+    *outNumElements = numWritten;
+
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getRequests(int32_t* outDisplayRequests,
+        uint32_t* outNumElements, hwc2_layer_t* outLayers,
+        int32_t* outLayerRequests)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!mChanges) {
+        return Error::NotValidated;
+    }
+
+    if (outLayers == nullptr || outLayerRequests == nullptr) {
+        *outNumElements = mChanges->getNumLayerRequests();
+        return Error::None;
+    }
+
+    *outDisplayRequests = mChanges->getDisplayRequests();
+    uint32_t numWritten = 0;
+    for (const auto& request : mChanges->getLayerRequests()) {
+        if (numWritten == *outNumElements) {
+            break;
+        }
+        outLayers[numWritten] = request.first;
+        outLayerRequests[numWritten] = static_cast<int32_t>(request.second);
+        ++numWritten;
+    }
+
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::getType(int32_t* outType)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    *outType = static_cast<int32_t>(mType);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::present(int32_t* outRetireFence)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (mChanges) {
+        Error error = mDevice.setAllDisplays();
+        if (error != Error::None) {
+            ALOGE("[%" PRIu64 "] present: setAllDisplaysFailed (%s)", mId,
+                    to_string(error).c_str());
+            return error;
+        }
+    }
+
+    *outRetireFence = mRetireFence.get()->dup();
+    ALOGV("[%" PRIu64 "] present returning retire fence %d", mId,
+            *outRetireFence);
+
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setActiveConfig(hwc2_config_t configId)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    auto config = getConfig(configId);
+    if (!config) {
+        return Error::BadConfig;
+    }
+    mActiveConfig = config;
+    if (mDevice.mHwc1MinorVersion >= 4) {
+        int error = mDevice.mHwc1Device->setActiveConfig(mDevice.mHwc1Device,
+                mHwc1Id, static_cast<int>(configId));
+        ALOGE_IF(error != 0,
+                "setActiveConfig: Failed to set active config on HWC1 (%d)",
+                error);
+    }
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setClientTarget(buffer_handle_t target,
+        int32_t acquireFence, int32_t /*dataspace*/)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    ALOGV("[%" PRIu64 "] setClientTarget(%p, %d)", mId, target, acquireFence);
+    mClientTarget.setBuffer(target);
+    mClientTarget.setFence(acquireFence);
+    // dataspace can't be used by HWC1, so ignore it
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::setOutputBuffer(buffer_handle_t buffer,
+        int32_t releaseFence)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    ALOGV("[%" PRIu64 "] setOutputBuffer(%p, %d)", mId, buffer, releaseFence);
+    mOutputBuffer.setBuffer(buffer);
+    mOutputBuffer.setFence(releaseFence);
+    return Error::None;
+}
+
+static bool isValid(PowerMode mode)
+{
+    switch (mode) {
+        case PowerMode::Off: // Fall-through
+        case PowerMode::DozeSuspend: // Fall-through
+        case PowerMode::Doze: // Fall-through
+        case PowerMode::On: return true;
+        default: return false;
+    }
+}
+
+static int getHwc1PowerMode(PowerMode mode)
+{
+    switch (mode) {
+        case PowerMode::Off: return HWC_POWER_MODE_OFF;
+        case PowerMode::DozeSuspend: return HWC_POWER_MODE_DOZE_SUSPEND;
+        case PowerMode::Doze: return HWC_POWER_MODE_DOZE;
+        case PowerMode::On: return HWC_POWER_MODE_NORMAL;
+        default: return HWC_POWER_MODE_OFF;
+    }
+}
+
+Error HWC2On1Adapter::Display::setPowerMode(PowerMode mode)
+{
+    if (!isValid(mode)) {
+        return Error::BadParameter;
+    }
+    if (mode == mPowerMode) {
+        return Error::None;
+    }
+
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    int error = 0;
+    if (mDevice.mHwc1MinorVersion < 4) {
+        error = mDevice.mHwc1Device->blank(mDevice.mHwc1Device, mHwc1Id,
+                mode == PowerMode::Off);
+    } else {
+        error = mDevice.mHwc1Device->setPowerMode(mDevice.mHwc1Device,
+                mHwc1Id, getHwc1PowerMode(mode));
+    }
+    ALOGE_IF(error != 0, "setPowerMode: Failed to set power mode on HWC1 (%d)",
+            error);
+
+    ALOGV("[%" PRIu64 "] setPowerMode(%s)", mId, to_string(mode).c_str());
+    mPowerMode = mode;
+    return Error::None;
+}
+
+static bool isValid(Vsync enable) {
+    switch (enable) {
+        case Vsync::Enable: // Fall-through
+        case Vsync::Disable: return true;
+        default: return false;
+    }
+}
+
+Error HWC2On1Adapter::Display::setVsyncEnabled(Vsync enable)
+{
+    if (!isValid(enable)) {
+        return Error::BadParameter;
+    }
+    if (enable == mVsyncEnabled) {
+        return Error::None;
+    }
+
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    int error = mDevice.mHwc1Device->eventControl(mDevice.mHwc1Device,
+            mHwc1Id, HWC_EVENT_VSYNC, enable == Vsync::Enable);
+    ALOGE_IF(error != 0, "setVsyncEnabled: Failed to set vsync on HWC1 (%d)",
+            error);
+
+    mVsyncEnabled = enable;
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Display::validate(uint32_t* outNumTypes,
+        uint32_t* outNumRequests)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    ALOGV("[%" PRIu64 "] Entering validate", mId);
+
+    if (!mChanges) {
+        if (!mDevice.prepareAllDisplays()) {
+            return Error::BadDisplay;
+        }
+    }
+
+    *outNumTypes = mChanges->getNumTypes();
+    *outNumRequests = mChanges->getNumLayerRequests();
+    ALOGV("[%" PRIu64 "] validate --> %u types, %u requests", mId, *outNumTypes,
+            *outNumRequests);
+    for (auto request : mChanges->getTypeChanges()) {
+        ALOGV("Layer %" PRIu64 " --> %s", request.first,
+                to_string(request.second).c_str());
+    }
+    return *outNumTypes > 0 ? Error::HasChanges : Error::None;
+}
+
+// Display helpers
+
+Error HWC2On1Adapter::Display::updateLayerZ(hwc2_layer_t layerId, uint32_t z)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    const auto mapLayer = mDevice.mLayers.find(layerId);
+    if (mapLayer == mDevice.mLayers.end()) {
+        ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer", mId);
+        return Error::BadLayer;
+    }
+
+    const auto layer = mapLayer->second;
+    const auto zRange = mLayers.equal_range(layer);
+    bool layerOnDisplay = false;
+    for (auto current = zRange.first; current != zRange.second; ++current) {
+        if (**current == *layer) {
+            if ((*current)->getZ() == z) {
+                // Don't change anything if the Z hasn't changed
+                return Error::None;
+            }
+            current = mLayers.erase(current);
+            layerOnDisplay = true;
+            break;
+        }
+    }
+
+    if (!layerOnDisplay) {
+        ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer on display",
+                mId);
+        return Error::BadLayer;
+    }
+
+    layer->setZ(z);
+    mLayers.emplace(std::move(layer));
+    mZIsDirty = true;
+
+    return Error::None;
+}
+
+static constexpr uint32_t ATTRIBUTES[] = {
+    HWC_DISPLAY_VSYNC_PERIOD,
+    HWC_DISPLAY_WIDTH,
+    HWC_DISPLAY_HEIGHT,
+    HWC_DISPLAY_DPI_X,
+    HWC_DISPLAY_DPI_Y,
+    HWC_DISPLAY_NO_ATTRIBUTE,
+};
+static constexpr size_t NUM_ATTRIBUTES = sizeof(ATTRIBUTES) / sizeof(uint32_t);
+
+static constexpr uint32_t ATTRIBUTE_MAP[] = {
+    5, // HWC_DISPLAY_NO_ATTRIBUTE = 0
+    0, // HWC_DISPLAY_VSYNC_PERIOD = 1,
+    1, // HWC_DISPLAY_WIDTH = 2,
+    2, // HWC_DISPLAY_HEIGHT = 3,
+    3, // HWC_DISPLAY_DPI_X = 4,
+    4, // HWC_DISPLAY_DPI_Y = 5,
+};
+
+template <uint32_t attribute>
+static constexpr bool attributesMatch()
+{
+    return ATTRIBUTES[ATTRIBUTE_MAP[attribute]] == attribute;
+}
+static_assert(attributesMatch<HWC_DISPLAY_VSYNC_PERIOD>(),
+        "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_WIDTH>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_HEIGHT>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_DPI_X>(), "Tables out of sync");
+static_assert(attributesMatch<HWC_DISPLAY_DPI_Y>(), "Tables out of sync");
+
+void HWC2On1Adapter::Display::populateConfigs()
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    ALOGV("[%" PRIu64 "] populateConfigs", mId);
+
+    if (mHwc1Id == -1) {
+        ALOGE("populateConfigs: HWC1 ID not set");
+        return;
+    }
+
+    const size_t MAX_NUM_CONFIGS = 128;
+    uint32_t configs[MAX_NUM_CONFIGS] = {};
+    size_t numConfigs = MAX_NUM_CONFIGS;
+    mDevice.mHwc1Device->getDisplayConfigs(mDevice.mHwc1Device, mHwc1Id,
+            configs, &numConfigs);
+
+    for (size_t c = 0; c < numConfigs; ++c) {
+        uint32_t hwc1ConfigId = configs[c];
+        hwc2_config_t id = static_cast<hwc2_config_t>(mConfigs.size());
+        mConfigs.emplace_back(
+                std::make_shared<Config>(*this, id, hwc1ConfigId));
+        auto& config = mConfigs[id];
+
+        int32_t values[NUM_ATTRIBUTES] = {};
+        mDevice.mHwc1Device->getDisplayAttributes(mDevice.mHwc1Device, mHwc1Id,
+                hwc1ConfigId, ATTRIBUTES, values);
+
+        config->setAttribute(Attribute::VsyncPeriod,
+                values[ATTRIBUTE_MAP[HWC_DISPLAY_VSYNC_PERIOD]]);
+        config->setAttribute(Attribute::Width,
+                values[ATTRIBUTE_MAP[HWC_DISPLAY_WIDTH]]);
+        config->setAttribute(Attribute::Height,
+                values[ATTRIBUTE_MAP[HWC_DISPLAY_HEIGHT]]);
+        config->setAttribute(Attribute::DpiX,
+                values[ATTRIBUTE_MAP[HWC_DISPLAY_DPI_X]]);
+        config->setAttribute(Attribute::DpiY,
+                values[ATTRIBUTE_MAP[HWC_DISPLAY_DPI_Y]]);
+
+        ALOGV("Found config: %s", config->toString().c_str());
+    }
+
+    ALOGV("Getting active config");
+    if (mDevice.mHwc1Device->getActiveConfig != nullptr) {
+        auto activeConfig = mDevice.mHwc1Device->getActiveConfig(
+                mDevice.mHwc1Device, mHwc1Id);
+        if (activeConfig >= 0) {
+            ALOGV("Setting active config to %d", activeConfig);
+            mActiveConfig = mConfigs[activeConfig];
+        }
+    } else {
+        ALOGV("getActiveConfig is null, choosing config 0");
+        mActiveConfig = mConfigs[0];
+    }
+}
+
+void HWC2On1Adapter::Display::populateConfigs(uint32_t width, uint32_t height)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    mConfigs.emplace_back(std::make_shared<Config>(*this, 0, 0));
+    auto& config = mConfigs[0];
+
+    config->setAttribute(Attribute::Width, static_cast<int32_t>(width));
+    config->setAttribute(Attribute::Height, static_cast<int32_t>(height));
+    mActiveConfig = config;
+}
+
+bool HWC2On1Adapter::Display::prepare()
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    // Only prepare display contents for displays HWC1 knows about
+    if (mHwc1Id == -1) {
+        return true;
+    }
+
+    // It doesn't make sense to prepare a display for which there is no active
+    // config, so return early
+    if (!mActiveConfig) {
+        ALOGE("[%" PRIu64 "] Attempted to prepare, but no config active", mId);
+        return false;
+    }
+
+    ALOGV("[%" PRIu64 "] Entering prepare", mId);
+
+    auto currentCount = mHwc1RequestedContents ?
+            mHwc1RequestedContents->numHwLayers : 0;
+    auto requiredCount = mLayers.size() + 1;
+    ALOGV("[%" PRIu64 "]   Requires %zd layers, %zd allocated in %p", mId,
+            requiredCount, currentCount, mHwc1RequestedContents.get());
+
+    bool layerCountChanged = (currentCount != requiredCount);
+    if (layerCountChanged) {
+        reallocateHwc1Contents();
+    }
+
+    bool applyAllState = false;
+    if (layerCountChanged || mZIsDirty) {
+        assignHwc1LayerIds();
+        mZIsDirty = false;
+        applyAllState = true;
+    }
+
+    mHwc1RequestedContents->retireFenceFd = -1;
+    mHwc1RequestedContents->flags = 0;
+    if (isDirty() || applyAllState) {
+        mHwc1RequestedContents->flags |= HWC_GEOMETRY_CHANGED;
+    }
+
+    for (auto& layer : mLayers) {
+        auto& hwc1Layer = mHwc1RequestedContents->hwLayers[layer->getHwc1Id()];
+        hwc1Layer.releaseFenceFd = -1;
+        layer->applyState(hwc1Layer, applyAllState);
+    }
+
+    mHwc1RequestedContents->outbuf = mOutputBuffer.getBuffer();
+    mHwc1RequestedContents->outbufAcquireFenceFd = mOutputBuffer.getFence();
+
+    prepareFramebufferTarget();
+
+    return true;
+}
+
+static void cloneHWCRegion(hwc_region_t& region)
+{
+    auto size = sizeof(hwc_rect_t) * region.numRects;
+    auto newRects = static_cast<hwc_rect_t*>(std::malloc(size));
+    std::copy_n(region.rects, region.numRects, newRects);
+    region.rects = newRects;
+}
+
+HWC2On1Adapter::Display::HWC1Contents
+        HWC2On1Adapter::Display::cloneRequestedContents() const
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    size_t size = sizeof(hwc_display_contents_1_t) +
+            sizeof(hwc_layer_1_t) * (mHwc1RequestedContents->numHwLayers);
+    auto contents = static_cast<hwc_display_contents_1_t*>(std::malloc(size));
+    std::memcpy(contents, mHwc1RequestedContents.get(), size);
+    for (size_t layerId = 0; layerId < contents->numHwLayers; ++layerId) {
+        auto& layer = contents->hwLayers[layerId];
+        // Deep copy the regions to avoid double-frees
+        cloneHWCRegion(layer.visibleRegionScreen);
+        cloneHWCRegion(layer.surfaceDamage);
+    }
+    return HWC1Contents(contents);
+}
+
+void HWC2On1Adapter::Display::setReceivedContents(HWC1Contents contents)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    mHwc1ReceivedContents = std::move(contents);
+
+    mChanges.reset(new Changes);
+
+    size_t numLayers = mHwc1ReceivedContents->numHwLayers;
+    for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) {
+        const auto& receivedLayer = mHwc1ReceivedContents->hwLayers[hwc1Id];
+        if (mHwc1LayerMap.count(hwc1Id) == 0) {
+            ALOGE_IF(receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET,
+                    "setReceivedContents: HWC1 layer %zd doesn't have a"
+                    " matching HWC2 layer, and isn't the framebuffer target",
+                    hwc1Id);
+            continue;
+        }
+
+        Layer& layer = *mHwc1LayerMap[hwc1Id];
+        updateTypeChanges(receivedLayer, layer);
+        updateLayerRequests(receivedLayer, layer);
+    }
+}
+
+bool HWC2On1Adapter::Display::hasChanges() const
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+    return mChanges != nullptr;
+}
+
+Error HWC2On1Adapter::Display::set(hwc_display_contents_1& hwcContents)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    if (!mChanges || (mChanges->getNumTypes() > 0)) {
+        ALOGE("[%" PRIu64 "] set failed: not validated", mId);
+        return Error::NotValidated;
+    }
+
+    // Set up the client/framebuffer target
+    auto numLayers = hwcContents.numHwLayers;
+
+    // Close acquire fences on FRAMEBUFFER layers, since they will not be used
+    // by HWC
+    for (size_t l = 0; l < numLayers - 1; ++l) {
+        auto& layer = hwcContents.hwLayers[l];
+        if (layer.compositionType == HWC_FRAMEBUFFER) {
+            ALOGV("Closing fence %d for layer %zd", layer.acquireFenceFd, l);
+            close(layer.acquireFenceFd);
+            layer.acquireFenceFd = -1;
+        }
+    }
+
+    auto& clientTargetLayer = hwcContents.hwLayers[numLayers - 1];
+    if (clientTargetLayer.compositionType == HWC_FRAMEBUFFER_TARGET) {
+        clientTargetLayer.handle = mClientTarget.getBuffer();
+        clientTargetLayer.acquireFenceFd = mClientTarget.getFence();
+    } else {
+        ALOGE("[%" PRIu64 "] set: last HWC layer wasn't FRAMEBUFFER_TARGET",
+                mId);
+    }
+
+    mChanges.reset();
+
+    return Error::None;
+}
+
+void HWC2On1Adapter::Display::addRetireFence(int fenceFd)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+    mRetireFence.add(fenceFd);
+}
+
+void HWC2On1Adapter::Display::addReleaseFences(
+        const hwc_display_contents_1_t& hwcContents)
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    size_t numLayers = hwcContents.numHwLayers;
+    for (size_t hwc1Id = 0; hwc1Id < numLayers; ++hwc1Id) {
+        const auto& receivedLayer = hwcContents.hwLayers[hwc1Id];
+        if (mHwc1LayerMap.count(hwc1Id) == 0) {
+            if (receivedLayer.compositionType != HWC_FRAMEBUFFER_TARGET) {
+                ALOGE("addReleaseFences: HWC1 layer %zd doesn't have a"
+                        " matching HWC2 layer, and isn't the framebuffer"
+                        " target", hwc1Id);
+            }
+            // Close the framebuffer target release fence since we will use the
+            // display retire fence instead
+            if (receivedLayer.releaseFenceFd != -1) {
+                close(receivedLayer.releaseFenceFd);
+            }
+            continue;
+        }
+
+        Layer& layer = *mHwc1LayerMap[hwc1Id];
+        ALOGV("Adding release fence %d to layer %" PRIu64,
+                receivedLayer.releaseFenceFd, layer.getId());
+        layer.addReleaseFence(receivedLayer.releaseFenceFd);
+    }
+}
+
+static std::string hwc1CompositionString(int32_t type)
+{
+    switch (type) {
+        case HWC_FRAMEBUFFER: return "Framebuffer";
+        case HWC_OVERLAY: return "Overlay";
+        case HWC_BACKGROUND: return "Background";
+        case HWC_FRAMEBUFFER_TARGET: return "FramebufferTarget";
+        case HWC_SIDEBAND: return "Sideband";
+        case HWC_CURSOR_OVERLAY: return "CursorOverlay";
+        default:
+            return std::string("Unknown (") + std::to_string(type) + ")";
+    }
+}
+
+static std::string hwc1TransformString(int32_t transform)
+{
+    switch (transform) {
+        case 0: return "None";
+        case HWC_TRANSFORM_FLIP_H: return "FlipH";
+        case HWC_TRANSFORM_FLIP_V: return "FlipV";
+        case HWC_TRANSFORM_ROT_90: return "Rotate90";
+        case HWC_TRANSFORM_ROT_180: return "Rotate180";
+        case HWC_TRANSFORM_ROT_270: return "Rotate270";
+        case HWC_TRANSFORM_FLIP_H_ROT_90: return "FlipHRotate90";
+        case HWC_TRANSFORM_FLIP_V_ROT_90: return "FlipVRotate90";
+        default:
+            return std::string("Unknown (") + std::to_string(transform) + ")";
+    }
+}
+
+static std::string hwc1BlendModeString(int32_t mode)
+{
+    switch (mode) {
+        case HWC_BLENDING_NONE: return "None";
+        case HWC_BLENDING_PREMULT: return "Premultiplied";
+        case HWC_BLENDING_COVERAGE: return "Coverage";
+        default:
+            return std::string("Unknown (") + std::to_string(mode) + ")";
+    }
+}
+
+static std::string rectString(hwc_rect_t rect)
+{
+    std::stringstream output;
+    output << "[" << rect.left << ", " << rect.top << ", ";
+    output << rect.right << ", " << rect.bottom << "]";
+    return output.str();
+}
+
+static std::string approximateFloatString(float f)
+{
+    if (static_cast<int32_t>(f) == f) {
+        return std::to_string(static_cast<int32_t>(f));
+    }
+    int32_t truncated = static_cast<int32_t>(f * 10);
+    bool approximate = (static_cast<float>(truncated) != f * 10);
+    const size_t BUFFER_SIZE = 32;
+    char buffer[BUFFER_SIZE] = {};
+    auto bytesWritten = snprintf(buffer, BUFFER_SIZE,
+            "%s%.1f", approximate ? "~" : "", f);
+    return std::string(buffer, bytesWritten);
+}
+
+static std::string frectString(hwc_frect_t frect)
+{
+    std::stringstream output;
+    output << "[" << approximateFloatString(frect.left) << ", ";
+    output << approximateFloatString(frect.top) << ", ";
+    output << approximateFloatString(frect.right) << ", ";
+    output << approximateFloatString(frect.bottom) << "]";
+    return output.str();
+}
+
+static std::string colorString(hwc_color_t color)
+{
+    std::stringstream output;
+    output << "RGBA [";
+    output << static_cast<int32_t>(color.r) << ", ";
+    output << static_cast<int32_t>(color.g) << ", ";
+    output << static_cast<int32_t>(color.b) << ", ";
+    output << static_cast<int32_t>(color.a) << "]";
+    return output.str();
+}
+
+static std::string alphaString(float f)
+{
+    const size_t BUFFER_SIZE = 8;
+    char buffer[BUFFER_SIZE] = {};
+    auto bytesWritten = snprintf(buffer, BUFFER_SIZE, "%.3f", f);
+    return std::string(buffer, bytesWritten);
+}
+
+static std::string to_string(const hwc_layer_1_t& hwcLayer,
+        int32_t hwc1MinorVersion)
+{
+    const char* fill = "          ";
+
+    std::stringstream output;
+
+    output << "  Composition: " <<
+            hwc1CompositionString(hwcLayer.compositionType);
+
+    if (hwcLayer.compositionType == HWC_BACKGROUND) {
+        output << "  Color: " << colorString(hwcLayer.backgroundColor) << '\n';
+    } else if (hwcLayer.compositionType == HWC_SIDEBAND) {
+        output << "  Stream: " << hwcLayer.sidebandStream << '\n';
+    } else {
+        output << "  Buffer: " << hwcLayer.handle << "/" <<
+                hwcLayer.acquireFenceFd << '\n';
+    }
+
+    output << fill << "Display frame: " << rectString(hwcLayer.displayFrame) <<
+            '\n';
+
+    output << fill << "Source crop: ";
+    if (hwc1MinorVersion >= 3) {
+        output << frectString(hwcLayer.sourceCropf) << '\n';
+    } else {
+        output << rectString(hwcLayer.sourceCropi) << '\n';
+    }
+
+    output << fill << "Transform: " << hwc1TransformString(hwcLayer.transform);
+    output << "  Blend mode: " << hwc1BlendModeString(hwcLayer.blending);
+    if (hwcLayer.planeAlpha != 0xFF) {
+        output << "  Alpha: " << alphaString(hwcLayer.planeAlpha / 255.0f);
+    }
+    output << '\n';
+
+    if (hwcLayer.hints != 0) {
+        output << fill << "Hints:";
+        if ((hwcLayer.hints & HWC_HINT_TRIPLE_BUFFER) != 0) {
+            output << " TripleBuffer";
+        }
+        if ((hwcLayer.hints & HWC_HINT_CLEAR_FB) != 0) {
+            output << " ClearFB";
+        }
+        output << '\n';
+    }
+
+    if (hwcLayer.flags != 0) {
+        output << fill << "Flags:";
+        if ((hwcLayer.flags & HWC_SKIP_LAYER) != 0) {
+            output << " SkipLayer";
+        }
+        if ((hwcLayer.flags & HWC_IS_CURSOR_LAYER) != 0) {
+            output << " IsCursorLayer";
+        }
+        output << '\n';
+    }
+
+    return output.str();
+}
+
+static std::string to_string(const hwc_display_contents_1_t& hwcContents,
+        int32_t hwc1MinorVersion)
+{
+    const char* fill = "      ";
+
+    std::stringstream output;
+    output << fill << "Geometry changed: " <<
+            ((hwcContents.flags & HWC_GEOMETRY_CHANGED) != 0 ? "Y\n" : "N\n");
+
+    output << fill << hwcContents.numHwLayers << " Layer" <<
+            ((hwcContents.numHwLayers == 1) ? "\n" : "s\n");
+    for (size_t layer = 0; layer < hwcContents.numHwLayers; ++layer) {
+        output << fill << "  Layer " << layer;
+        output << to_string(hwcContents.hwLayers[layer], hwc1MinorVersion);
+    }
+
+    if (hwcContents.outbuf != nullptr) {
+        output << fill << "Output buffer: " << hwcContents.outbuf << "/" <<
+                hwcContents.outbufAcquireFenceFd << '\n';
+    }
+
+    return output.str();
+}
+
+std::string HWC2On1Adapter::Display::dump() const
+{
+    std::unique_lock<std::recursive_mutex> lock(mStateMutex);
+
+    std::stringstream output;
+
+    output << "  Display " << mId << ": ";
+    output << to_string(mType) << "  ";
+    output << "HWC1 ID: " << mHwc1Id << "  ";
+    output << "Power mode: " << to_string(mPowerMode) << "  ";
+    output << "Vsync: " << to_string(mVsyncEnabled) << '\n';
+
+    output << "    " << mConfigs.size() << " Config" <<
+            (mConfigs.size() == 1 ? "" : "s") << " (* Active)\n";
+    for (const auto& config : mConfigs) {
+        if (config == mActiveConfig) {
+            output << "    * " << config->toString();
+        } else {
+            output << "      " << config->toString();
+        }
+    }
+    output << '\n';
+
+    output << "    " << mLayers.size() << " Layer" <<
+            (mLayers.size() == 1 ? "" : "s") << '\n';
+    for (const auto& layer : mLayers) {
+        output << layer->dump();
+    }
+
+    output << "    Client target: " << mClientTarget.getBuffer() << '\n';
+
+    if (mOutputBuffer.getBuffer() != nullptr) {
+        output << "    Output buffer: " << mOutputBuffer.getBuffer() << '\n';
+    }
+
+    if (mHwc1ReceivedContents) {
+        output << "    Last received HWC1 state\n";
+        output << to_string(*mHwc1ReceivedContents, mDevice.mHwc1MinorVersion);
+    } else if (mHwc1RequestedContents) {
+        output << "    Last requested HWC1 state\n";
+        output << to_string(*mHwc1RequestedContents, mDevice.mHwc1MinorVersion);
+    }
+
+    return output.str();
+}
+
+void HWC2On1Adapter::Display::Config::setAttribute(HWC2::Attribute attribute,
+        int32_t value)
+{
+    mAttributes[attribute] = value;
+}
+
+int32_t HWC2On1Adapter::Display::Config::getAttribute(Attribute attribute) const
+{
+    if (mAttributes.count(attribute) == 0) {
+        return -1;
+    }
+    return mAttributes.at(attribute);
+}
+
+std::string HWC2On1Adapter::Display::Config::toString() const
+{
+    std::string output;
+
+    const size_t BUFFER_SIZE = 100;
+    char buffer[BUFFER_SIZE] = {};
+    auto writtenBytes = snprintf(buffer, BUFFER_SIZE,
+            "[%u] %u x %u", mHwcId,
+            mAttributes.at(HWC2::Attribute::Width),
+            mAttributes.at(HWC2::Attribute::Height));
+    output.append(buffer, writtenBytes);
+
+    if (mAttributes.count(HWC2::Attribute::VsyncPeriod) != 0) {
+        std::memset(buffer, 0, BUFFER_SIZE);
+        writtenBytes = snprintf(buffer, BUFFER_SIZE, " @ %.1f Hz",
+                1e9 / mAttributes.at(HWC2::Attribute::VsyncPeriod));
+        output.append(buffer, writtenBytes);
+    }
+
+    if (mAttributes.count(HWC2::Attribute::DpiX) != 0 &&
+            mAttributes.at(HWC2::Attribute::DpiX) != -1) {
+        std::memset(buffer, 0, BUFFER_SIZE);
+        writtenBytes = snprintf(buffer, BUFFER_SIZE,
+                ", DPI: %.1f x %.1f",
+                mAttributes.at(HWC2::Attribute::DpiX) / 1000.0f,
+                mAttributes.at(HWC2::Attribute::DpiY) / 1000.0f);
+        output.append(buffer, writtenBytes);
+    }
+
+    return output;
+}
+
+std::shared_ptr<const HWC2On1Adapter::Display::Config>
+        HWC2On1Adapter::Display::getConfig(hwc2_config_t configId) const
+{
+    if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) {
+        return nullptr;
+    }
+    return mConfigs[configId];
+}
+
+void HWC2On1Adapter::Display::reallocateHwc1Contents()
+{
+    // Allocate an additional layer for the framebuffer target
+    auto numLayers = mLayers.size() + 1;
+    size_t size = sizeof(hwc_display_contents_1_t) +
+            sizeof(hwc_layer_1_t) * numLayers;
+    ALOGV("[%" PRIu64 "] reallocateHwc1Contents creating %zd layer%s", mId,
+            numLayers, numLayers != 1 ? "s" : "");
+    auto contents =
+            static_cast<hwc_display_contents_1_t*>(std::calloc(size, 1));
+    contents->numHwLayers = numLayers;
+    mHwc1RequestedContents.reset(contents);
+}
+
+void HWC2On1Adapter::Display::assignHwc1LayerIds()
+{
+    mHwc1LayerMap.clear();
+    size_t nextHwc1Id = 0;
+    for (auto& layer : mLayers) {
+        mHwc1LayerMap[nextHwc1Id] = layer;
+        layer->setHwc1Id(nextHwc1Id++);
+    }
+}
+
+void HWC2On1Adapter::Display::updateTypeChanges(const hwc_layer_1_t& hwc1Layer,
+        const Layer& layer)
+{
+    auto layerId = layer.getId();
+    switch (hwc1Layer.compositionType) {
+        case HWC_FRAMEBUFFER:
+            if (layer.getCompositionType() != Composition::Client) {
+                mChanges->addTypeChange(layerId, Composition::Client);
+            }
+            break;
+        case HWC_OVERLAY:
+            if (layer.getCompositionType() != Composition::Device) {
+                mChanges->addTypeChange(layerId, Composition::Device);
+            }
+            break;
+        case HWC_BACKGROUND:
+            ALOGE_IF(layer.getCompositionType() != Composition::SolidColor,
+                    "updateTypeChanges: HWC1 requested BACKGROUND, but HWC2"
+                    " wasn't expecting SolidColor");
+            break;
+        case HWC_FRAMEBUFFER_TARGET:
+            // Do nothing, since it shouldn't be modified by HWC1
+            break;
+        case HWC_SIDEBAND:
+            ALOGE_IF(layer.getCompositionType() != Composition::Sideband,
+                    "updateTypeChanges: HWC1 requested SIDEBAND, but HWC2"
+                    " wasn't expecting Sideband");
+            break;
+        case HWC_CURSOR_OVERLAY:
+            ALOGE_IF(layer.getCompositionType() != Composition::Cursor,
+                    "updateTypeChanges: HWC1 requested CURSOR_OVERLAY, but"
+                    " HWC2 wasn't expecting Cursor");
+            break;
+    }
+}
+
+void HWC2On1Adapter::Display::updateLayerRequests(
+        const hwc_layer_1_t& hwc1Layer, const Layer& layer)
+{
+    if ((hwc1Layer.hints & HWC_HINT_CLEAR_FB) != 0) {
+        mChanges->addLayerRequest(layer.getId(),
+                LayerRequest::ClearClientTarget);
+    }
+}
+
+void HWC2On1Adapter::Display::prepareFramebufferTarget()
+{
+    // We check that mActiveConfig is valid in Display::prepare
+    int32_t width = mActiveConfig->getAttribute(Attribute::Width);
+    int32_t height = mActiveConfig->getAttribute(Attribute::Height);
+
+    auto& hwc1Target = mHwc1RequestedContents->hwLayers[mLayers.size()];
+    hwc1Target.compositionType = HWC_FRAMEBUFFER_TARGET;
+    hwc1Target.releaseFenceFd = -1;
+    hwc1Target.hints = 0;
+    hwc1Target.flags = 0;
+    hwc1Target.transform = 0;
+    hwc1Target.blending = HWC_BLENDING_PREMULT;
+    if (mDevice.getHwc1MinorVersion() < 3) {
+        hwc1Target.sourceCropi = {0, 0, width, height};
+    } else {
+        hwc1Target.sourceCropf = {0.0f, 0.0f, static_cast<float>(width),
+                static_cast<float>(height)};
+    }
+    hwc1Target.displayFrame = {0, 0, width, height};
+    hwc1Target.planeAlpha = 255;
+    hwc1Target.visibleRegionScreen.numRects = 1;
+    auto rects = static_cast<hwc_rect_t*>(std::malloc(sizeof(hwc_rect_t)));
+    rects[0].left = 0;
+    rects[0].top = 0;
+    rects[0].right = width;
+    rects[0].bottom = height;
+    hwc1Target.visibleRegionScreen.rects = rects;
+
+    // We will set this to the correct value in set
+    hwc1Target.acquireFenceFd = -1;
+}
+
+// Layer functions
+
+std::atomic<hwc2_layer_t> HWC2On1Adapter::Layer::sNextId(1);
+
+HWC2On1Adapter::Layer::Layer(Display& display)
+  : mId(sNextId++),
+    mDisplay(display),
+    mBlendMode(*this, BlendMode::None),
+    mColor(*this, {0, 0, 0, 0}),
+    mCompositionType(*this, Composition::Invalid),
+    mDisplayFrame(*this, {0, 0, -1, -1}),
+    mPlaneAlpha(*this, 0.0f),
+    mSidebandStream(*this, nullptr),
+    mSourceCrop(*this, {0.0f, 0.0f, -1.0f, -1.0f}),
+    mTransform(*this, Transform::None),
+    mVisibleRegion(*this, std::vector<hwc_rect_t>()),
+    mZ(0),
+    mHwc1Id(0),
+    mHasUnsupportedPlaneAlpha(false) {}
+
+bool HWC2On1Adapter::SortLayersByZ::operator()(
+        const std::shared_ptr<Layer>& lhs, const std::shared_ptr<Layer>& rhs)
+{
+    return lhs->getZ() < rhs->getZ();
+}
+
+Error HWC2On1Adapter::Layer::setBuffer(buffer_handle_t buffer,
+        int32_t acquireFence)
+{
+    ALOGV("Setting acquireFence to %d for layer %" PRIu64, acquireFence, mId);
+    mBuffer.setBuffer(buffer);
+    mBuffer.setFence(acquireFence);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setCursorPosition(int32_t x, int32_t y)
+{
+    if (mCompositionType.getValue() != Composition::Cursor) {
+        return Error::BadLayer;
+    }
+
+    if (mDisplay.hasChanges()) {
+        return Error::NotValidated;
+    }
+
+    auto displayId = mDisplay.getHwc1Id();
+    auto hwc1Device = mDisplay.getDevice().getHwc1Device();
+    hwc1Device->setCursorPositionAsync(hwc1Device, displayId, x, y);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setSurfaceDamage(hwc_region_t damage)
+{
+    mSurfaceDamage.resize(damage.numRects);
+    std::copy_n(damage.rects, damage.numRects, mSurfaceDamage.begin());
+    return Error::None;
+}
+
+// Layer state functions
+
+Error HWC2On1Adapter::Layer::setBlendMode(BlendMode mode)
+{
+    mBlendMode.setPending(mode);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setColor(hwc_color_t color)
+{
+    mColor.setPending(color);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setCompositionType(Composition type)
+{
+    mCompositionType.setPending(type);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setDisplayFrame(hwc_rect_t frame)
+{
+    mDisplayFrame.setPending(frame);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setPlaneAlpha(float alpha)
+{
+    mPlaneAlpha.setPending(alpha);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setSidebandStream(const native_handle_t* stream)
+{
+    mSidebandStream.setPending(stream);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setSourceCrop(hwc_frect_t crop)
+{
+    mSourceCrop.setPending(crop);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setTransform(Transform transform)
+{
+    mTransform.setPending(transform);
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setVisibleRegion(hwc_region_t rawVisible)
+{
+    std::vector<hwc_rect_t> visible(rawVisible.rects,
+            rawVisible.rects + rawVisible.numRects);
+    mVisibleRegion.setPending(std::move(visible));
+    return Error::None;
+}
+
+Error HWC2On1Adapter::Layer::setZ(uint32_t z)
+{
+    mZ = z;
+    return Error::None;
+}
+
+void HWC2On1Adapter::Layer::addReleaseFence(int fenceFd)
+{
+    ALOGV("addReleaseFence %d to layer %" PRIu64, fenceFd, mId);
+    mReleaseFence.add(fenceFd);
+}
+
+const sp<Fence>& HWC2On1Adapter::Layer::getReleaseFence() const
+{
+    return mReleaseFence.get();
+}
+
+void HWC2On1Adapter::Layer::applyState(hwc_layer_1_t& hwc1Layer,
+        bool applyAllState)
+{
+    applyCommonState(hwc1Layer, applyAllState);
+    auto compositionType = mCompositionType.getPendingValue();
+    if (compositionType == Composition::SolidColor) {
+        applySolidColorState(hwc1Layer, applyAllState);
+    } else if (compositionType == Composition::Sideband) {
+        applySidebandState(hwc1Layer, applyAllState);
+    } else {
+        applyBufferState(hwc1Layer);
+    }
+    applyCompositionType(hwc1Layer, applyAllState);
+}
+
+// Layer dump helpers
+
+static std::string regionStrings(const std::vector<hwc_rect_t>& visibleRegion,
+        const std::vector<hwc_rect_t>& surfaceDamage)
+{
+    std::string regions;
+    regions += "        Visible Region";
+    regions.resize(40, ' ');
+    regions += "Surface Damage\n";
+
+    size_t numPrinted = 0;
+    size_t maxSize = std::max(visibleRegion.size(), surfaceDamage.size());
+    while (numPrinted < maxSize) {
+        std::string line("        ");
+        if (visibleRegion.empty() && numPrinted == 0) {
+            line += "None";
+        } else if (numPrinted < visibleRegion.size()) {
+            line += rectString(visibleRegion[numPrinted]);
+        }
+        line.resize(40, ' ');
+        if (surfaceDamage.empty() && numPrinted == 0) {
+            line += "None";
+        } else if (numPrinted < surfaceDamage.size()) {
+            line += rectString(surfaceDamage[numPrinted]);
+        }
+        line += '\n';
+        regions += line;
+        ++numPrinted;
+    }
+    return regions;
+}
+
+std::string HWC2On1Adapter::Layer::dump() const
+{
+    std::stringstream output;
+    const char* fill = "      ";
+
+    output << fill << to_string(mCompositionType.getPendingValue());
+    output << " Layer  HWC2/1: " << mId << "/" << mHwc1Id << "  ";
+    output << "Z: " << mZ;
+    if (mCompositionType.getValue() == HWC2::Composition::SolidColor) {
+        output << "  " << colorString(mColor.getValue());
+    } else if (mCompositionType.getValue() == HWC2::Composition::Sideband) {
+        output << "  Handle: " << mSidebandStream.getValue() << '\n';
+    } else {
+        output << "  Buffer: " << mBuffer.getBuffer() << "/" <<
+                mBuffer.getFence() << '\n';
+        output << fill << "  Display frame [LTRB]: " <<
+                rectString(mDisplayFrame.getValue()) << '\n';
+        output << fill << "  Source crop: " <<
+                frectString(mSourceCrop.getValue()) << '\n';
+        output << fill << "  Transform: " << to_string(mTransform.getValue());
+        output << "  Blend mode: " << to_string(mBlendMode.getValue());
+        if (mPlaneAlpha.getValue() != 1.0f) {
+            output << "  Alpha: " <<
+                alphaString(mPlaneAlpha.getValue()) << '\n';
+        } else {
+            output << '\n';
+        }
+        output << regionStrings(mVisibleRegion.getValue(), mSurfaceDamage);
+    }
+    return output.str();
+}
+
+static int getHwc1Blending(HWC2::BlendMode blendMode)
+{
+    switch (blendMode) {
+        case BlendMode::Coverage: return HWC_BLENDING_COVERAGE;
+        case BlendMode::Premultiplied: return HWC_BLENDING_PREMULT;
+        default: return HWC_BLENDING_NONE;
+    }
+}
+
+void HWC2On1Adapter::Layer::applyCommonState(hwc_layer_1_t& hwc1Layer,
+        bool applyAllState)
+{
+    auto minorVersion = mDisplay.getDevice().getHwc1MinorVersion();
+    if (applyAllState || mBlendMode.isDirty()) {
+        hwc1Layer.blending = getHwc1Blending(mBlendMode.getPendingValue());
+        mBlendMode.latch();
+    }
+    if (applyAllState || mDisplayFrame.isDirty()) {
+        hwc1Layer.displayFrame = mDisplayFrame.getPendingValue();
+        mDisplayFrame.latch();
+    }
+    if (applyAllState || mPlaneAlpha.isDirty()) {
+        auto pendingAlpha = mPlaneAlpha.getPendingValue();
+        if (minorVersion < 2) {
+            mHasUnsupportedPlaneAlpha = pendingAlpha < 1.0f;
+        } else {
+            hwc1Layer.planeAlpha =
+                    static_cast<uint8_t>(255.0f * pendingAlpha + 0.5f);
+        }
+        mPlaneAlpha.latch();
+    }
+    if (applyAllState || mSourceCrop.isDirty()) {
+        if (minorVersion < 3) {
+            auto pending = mSourceCrop.getPendingValue();
+            hwc1Layer.sourceCropi.left =
+                    static_cast<int32_t>(std::ceil(pending.left));
+            hwc1Layer.sourceCropi.top =
+                    static_cast<int32_t>(std::ceil(pending.top));
+            hwc1Layer.sourceCropi.right =
+                    static_cast<int32_t>(std::floor(pending.right));
+            hwc1Layer.sourceCropi.bottom =
+                    static_cast<int32_t>(std::floor(pending.bottom));
+        } else {
+            hwc1Layer.sourceCropf = mSourceCrop.getPendingValue();
+        }
+        mSourceCrop.latch();
+    }
+    if (applyAllState || mTransform.isDirty()) {
+        hwc1Layer.transform =
+                static_cast<uint32_t>(mTransform.getPendingValue());
+        mTransform.latch();
+    }
+    if (applyAllState || mVisibleRegion.isDirty()) {
+        auto& hwc1VisibleRegion = hwc1Layer.visibleRegionScreen;
+
+        std::free(const_cast<hwc_rect_t*>(hwc1VisibleRegion.rects));
+
+        auto pending = mVisibleRegion.getPendingValue();
+        hwc_rect_t* newRects = static_cast<hwc_rect_t*>(
+                std::malloc(sizeof(hwc_rect_t) * pending.size()));
+        std::copy(pending.begin(), pending.end(), newRects);
+        hwc1VisibleRegion.rects = const_cast<const hwc_rect_t*>(newRects);
+        hwc1VisibleRegion.numRects = pending.size();
+        mVisibleRegion.latch();
+    }
+}
+
+void HWC2On1Adapter::Layer::applySolidColorState(hwc_layer_1_t& hwc1Layer,
+        bool applyAllState)
+{
+    if (applyAllState || mColor.isDirty()) {
+        hwc1Layer.backgroundColor = mColor.getPendingValue();
+        mColor.latch();
+    }
+}
+
+void HWC2On1Adapter::Layer::applySidebandState(hwc_layer_1_t& hwc1Layer,
+        bool applyAllState)
+{
+    if (applyAllState || mSidebandStream.isDirty()) {
+        hwc1Layer.sidebandStream = mSidebandStream.getPendingValue();
+        mSidebandStream.latch();
+    }
+}
+
+void HWC2On1Adapter::Layer::applyBufferState(hwc_layer_1_t& hwc1Layer)
+{
+    hwc1Layer.handle = mBuffer.getBuffer();
+    hwc1Layer.acquireFenceFd = mBuffer.getFence();
+}
+
+void HWC2On1Adapter::Layer::applyCompositionType(hwc_layer_1_t& hwc1Layer,
+        bool applyAllState)
+{
+    if (mHasUnsupportedPlaneAlpha) {
+        hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+        hwc1Layer.flags = HWC_SKIP_LAYER;
+        return;
+    }
+
+    if (applyAllState || mCompositionType.isDirty()) {
+        hwc1Layer.flags = 0;
+        switch (mCompositionType.getPendingValue()) {
+            case Composition::Client:
+                hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+                hwc1Layer.flags |= HWC_SKIP_LAYER;
+                break;
+            case Composition::Device:
+                hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+                break;
+            case Composition::SolidColor:
+                hwc1Layer.compositionType = HWC_BACKGROUND;
+                break;
+            case Composition::Cursor:
+                hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+                if (mDisplay.getDevice().getHwc1MinorVersion() >= 4) {
+                    hwc1Layer.hints |= HWC_IS_CURSOR_LAYER;
+                }
+                break;
+            case Composition::Sideband:
+                if (mDisplay.getDevice().getHwc1MinorVersion() < 4) {
+                    hwc1Layer.compositionType = HWC_SIDEBAND;
+                } else {
+                    hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+                    hwc1Layer.flags |= HWC_SKIP_LAYER;
+                }
+                break;
+            default:
+                hwc1Layer.compositionType = HWC_FRAMEBUFFER;
+                hwc1Layer.flags |= HWC_SKIP_LAYER;
+                break;
+        }
+        ALOGV("Layer %" PRIu64 " %s set to %d", mId,
+                to_string(mCompositionType.getPendingValue()).c_str(),
+                hwc1Layer.compositionType);
+        ALOGV_IF(hwc1Layer.flags & HWC_SKIP_LAYER, "    and skipping");
+        mCompositionType.latch();
+    }
+}
+
+// Adapter helpers
+
+void HWC2On1Adapter::populateCapabilities()
+{
+    ALOGV("populateCapabilities");
+    if (mHwc1MinorVersion >= 3U) {
+        int supportedTypes = 0;
+        auto result = mHwc1Device->query(mHwc1Device,
+                HWC_DISPLAY_TYPES_SUPPORTED, &supportedTypes);
+        if ((result == 0) && ((supportedTypes & HWC_DISPLAY_VIRTUAL) != 0)) {
+            ALOGI("Found support for HWC virtual displays");
+            mHwc1SupportsVirtualDisplays = true;
+        }
+    }
+    if (mHwc1MinorVersion >= 4U) {
+        mCapabilities.insert(Capability::SidebandStream);
+    }
+}
+
+HWC2On1Adapter::Display* HWC2On1Adapter::getDisplay(hwc2_display_t id)
+{
+    std::unique_lock<std::timed_mutex> lock(mStateMutex);
+
+    auto display = mDisplays.find(id);
+    if (display == mDisplays.end()) {
+        return nullptr;
+    }
+
+    return display->second.get();
+}
+
+std::tuple<HWC2On1Adapter::Layer*, Error> HWC2On1Adapter::getLayer(
+        hwc2_display_t displayId, hwc2_layer_t layerId)
+{
+    auto display = getDisplay(displayId);
+    if (!display) {
+        return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadDisplay);
+    }
+
+    auto layerEntry = mLayers.find(layerId);
+    if (layerEntry == mLayers.end()) {
+        return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer);
+    }
+
+    auto layer = layerEntry->second;
+    if (layer->getDisplay().getId() != displayId) {
+        return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer);
+    }
+    return std::make_tuple(layer.get(), Error::None);
+}
+
+void HWC2On1Adapter::populatePrimary()
+{
+    ALOGV("populatePrimary");
+
+    std::unique_lock<std::timed_mutex> lock(mStateMutex);
+
+    auto display =
+            std::make_shared<Display>(*this, HWC2::DisplayType::Physical);
+    mHwc1DisplayMap[HWC_DISPLAY_PRIMARY] = display->getId();
+    display->setHwc1Id(HWC_DISPLAY_PRIMARY);
+    display->populateConfigs();
+    mDisplays.emplace(display->getId(), std::move(display));
+}
+
+bool HWC2On1Adapter::prepareAllDisplays()
+{
+    ATRACE_CALL();
+
+    std::unique_lock<std::timed_mutex> lock(mStateMutex);
+
+    for (const auto& displayPair : mDisplays) {
+        auto& display = displayPair.second;
+        if (!display->prepare()) {
+            return false;
+        }
+    }
+
+    if (mHwc1DisplayMap.count(0) == 0) {
+        ALOGE("prepareAllDisplays: Unable to find primary HWC1 display");
+        return false;
+    }
+
+    // Always push the primary display
+    std::vector<HWC2On1Adapter::Display::HWC1Contents> requestedContents;
+    auto primaryDisplayId = mHwc1DisplayMap[HWC_DISPLAY_PRIMARY];
+    auto& primaryDisplay = mDisplays[primaryDisplayId];
+    auto primaryDisplayContents = primaryDisplay->cloneRequestedContents();
+    requestedContents.push_back(std::move(primaryDisplayContents));
+
+    // Push the external display, if present
+    if (mHwc1DisplayMap.count(HWC_DISPLAY_EXTERNAL) != 0) {
+        auto externalDisplayId = mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL];
+        auto& externalDisplay = mDisplays[externalDisplayId];
+        auto externalDisplayContents =
+                externalDisplay->cloneRequestedContents();
+        requestedContents.push_back(std::move(externalDisplayContents));
+    } else {
+        // Even if an external display isn't present, we still need to send
+        // at least two displays down to HWC1
+        requestedContents.push_back(nullptr);
+    }
+
+    // Push the hardware virtual display, if supported and present
+    if (mHwc1MinorVersion >= 3) {
+        if (mHwc1DisplayMap.count(HWC_DISPLAY_VIRTUAL) != 0) {
+            auto virtualDisplayId = mHwc1DisplayMap[HWC_DISPLAY_VIRTUAL];
+            auto& virtualDisplay = mDisplays[virtualDisplayId];
+            auto virtualDisplayContents =
+                    virtualDisplay->cloneRequestedContents();
+            requestedContents.push_back(std::move(virtualDisplayContents));
+        } else {
+            requestedContents.push_back(nullptr);
+        }
+    }
+
+    mHwc1Contents.clear();
+    for (auto& displayContents : requestedContents) {
+        mHwc1Contents.push_back(displayContents.get());
+        if (!displayContents) {
+            continue;
+        }
+
+        ALOGV("Display %zd layers:", mHwc1Contents.size() - 1);
+        for (size_t l = 0; l < displayContents->numHwLayers; ++l) {
+            auto& layer = displayContents->hwLayers[l];
+            ALOGV("  %zd: %d", l, layer.compositionType);
+        }
+    }
+
+    ALOGV("Calling HWC1 prepare");
+    {
+        ATRACE_NAME("HWC1 prepare");
+        mHwc1Device->prepare(mHwc1Device, mHwc1Contents.size(),
+                mHwc1Contents.data());
+    }
+
+    for (size_t c = 0; c < mHwc1Contents.size(); ++c) {
+        auto& contents = mHwc1Contents[c];
+        if (!contents) {
+            continue;
+        }
+        ALOGV("Display %zd layers:", c);
+        for (size_t l = 0; l < contents->numHwLayers; ++l) {
+            ALOGV("  %zd: %d", l, contents->hwLayers[l].compositionType);
+        }
+    }
+
+    // Return the received contents to their respective displays
+    for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) {
+        if (mHwc1Contents[hwc1Id] == nullptr) {
+            continue;
+        }
+
+        auto displayId = mHwc1DisplayMap[hwc1Id];
+        auto& display = mDisplays[displayId];
+        display->setReceivedContents(std::move(requestedContents[hwc1Id]));
+    }
+
+    return true;
+}
+
+Error HWC2On1Adapter::setAllDisplays()
+{
+    ATRACE_CALL();
+
+    std::unique_lock<std::timed_mutex> lock(mStateMutex);
+
+    // Make sure we're ready to validate
+    for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) {
+        if (mHwc1Contents[hwc1Id] == nullptr) {
+            continue;
+        }
+
+        auto displayId = mHwc1DisplayMap[hwc1Id];
+        auto& display = mDisplays[displayId];
+        Error error = display->set(*mHwc1Contents[hwc1Id]);
+        if (error != Error::None) {
+            ALOGE("setAllDisplays: Failed to set display %zd: %s", hwc1Id,
+                    to_string(error).c_str());
+            return error;
+        }
+    }
+
+    ALOGV("Calling HWC1 set");
+    {
+        ATRACE_NAME("HWC1 set");
+        mHwc1Device->set(mHwc1Device, mHwc1Contents.size(),
+                mHwc1Contents.data());
+    }
+
+    // Add retire and release fences
+    for (size_t hwc1Id = 0; hwc1Id < mHwc1Contents.size(); ++hwc1Id) {
+        if (mHwc1Contents[hwc1Id] == nullptr) {
+            continue;
+        }
+
+        auto displayId = mHwc1DisplayMap[hwc1Id];
+        auto& display = mDisplays[displayId];
+        auto retireFenceFd = mHwc1Contents[hwc1Id]->retireFenceFd;
+        ALOGV("setAllDisplays: Adding retire fence %d to display %zd",
+                retireFenceFd, hwc1Id);
+        display->addRetireFence(mHwc1Contents[hwc1Id]->retireFenceFd);
+        display->addReleaseFences(*mHwc1Contents[hwc1Id]);
+    }
+
+    return Error::None;
+}
+
+void HWC2On1Adapter::hwc1Invalidate()
+{
+    ALOGV("Received hwc1Invalidate");
+
+    std::unique_lock<std::timed_mutex> lock(mStateMutex);
+
+    // If the HWC2-side callback hasn't been registered yet, buffer this until
+    // it is registered
+    if (mCallbacks.count(Callback::Refresh) == 0) {
+        mHasPendingInvalidate = true;
+        return;
+    }
+
+    const auto& callbackInfo = mCallbacks[Callback::Refresh];
+    std::vector<hwc2_display_t> displays;
+    for (const auto& displayPair : mDisplays) {
+        displays.emplace_back(displayPair.first);
+    }
+
+    // Call back without the state lock held
+    lock.unlock();
+
+    auto refresh = reinterpret_cast<HWC2_PFN_REFRESH>(callbackInfo.pointer);
+    for (auto display : displays) {
+        refresh(callbackInfo.data, display);
+    }
+}
+
+void HWC2On1Adapter::hwc1Vsync(int hwc1DisplayId, int64_t timestamp)
+{
+    ALOGV("Received hwc1Vsync(%d, %" PRId64 ")", hwc1DisplayId, timestamp);
+
+    std::unique_lock<std::timed_mutex> lock(mStateMutex);
+
+    // If the HWC2-side callback hasn't been registered yet, buffer this until
+    // it is registered
+    if (mCallbacks.count(Callback::Vsync) == 0) {
+        mPendingVsyncs.emplace_back(hwc1DisplayId, timestamp);
+        return;
+    }
+
+    if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+        ALOGE("hwc1Vsync: Couldn't find display for HWC1 id %d", hwc1DisplayId);
+        return;
+    }
+
+    const auto& callbackInfo = mCallbacks[Callback::Vsync];
+    auto displayId = mHwc1DisplayMap[hwc1DisplayId];
+
+    // Call back without the state lock held
+    lock.unlock();
+
+    auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(callbackInfo.pointer);
+    vsync(callbackInfo.data, displayId, timestamp);
+}
+
+void HWC2On1Adapter::hwc1Hotplug(int hwc1DisplayId, int connected)
+{
+    ALOGV("Received hwc1Hotplug(%d, %d)", hwc1DisplayId, connected);
+
+    if (hwc1DisplayId != HWC_DISPLAY_EXTERNAL) {
+        ALOGE("hwc1Hotplug: Received hotplug for non-external display");
+        return;
+    }
+
+    std::unique_lock<std::timed_mutex> lock(mStateMutex);
+
+    // If the HWC2-side callback hasn't been registered yet, buffer this until
+    // it is registered
+    if (mCallbacks.count(Callback::Hotplug) == 0) {
+        mPendingHotplugs.emplace_back(hwc1DisplayId, connected);
+        return;
+    }
+
+    hwc2_display_t displayId = UINT64_MAX;
+    if (mHwc1DisplayMap.count(hwc1DisplayId) == 0) {
+        if (connected == 0) {
+            ALOGW("hwc1Hotplug: Received disconnect for unconnected display");
+            return;
+        }
+
+        // Create a new display on connect
+        auto display = std::make_shared<HWC2On1Adapter::Display>(*this,
+                HWC2::DisplayType::Physical);
+        display->setHwc1Id(HWC_DISPLAY_EXTERNAL);
+        display->populateConfigs();
+        displayId = display->getId();
+        mHwc1DisplayMap[HWC_DISPLAY_EXTERNAL] = displayId;
+        mDisplays.emplace(displayId, std::move(display));
+    } else {
+        if (connected != 0) {
+            ALOGW("hwc1Hotplug: Received connect for previously connected "
+                    "display");
+            return;
+        }
+
+        // Disconnect an existing display
+        displayId = mHwc1DisplayMap[hwc1DisplayId];
+        mHwc1DisplayMap.erase(HWC_DISPLAY_EXTERNAL);
+        mDisplays.erase(displayId);
+    }
+
+    const auto& callbackInfo = mCallbacks[Callback::Hotplug];
+
+    // Call back without the state lock held
+    lock.unlock();
+
+    auto hotplug = reinterpret_cast<HWC2_PFN_HOTPLUG>(callbackInfo.pointer);
+    auto hwc2Connected = (connected == 0) ?
+            HWC2::Connection::Disconnected : HWC2::Connection::Connected;
+    hotplug(callbackInfo.data, displayId, static_cast<int32_t>(hwc2Connected));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
new file mode 100644
index 0000000..500c48f
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.h
@@ -0,0 +1,627 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#ifndef ANDROID_SF_HWC2_ON_1_ADAPTER_H
+#define ANDROID_SF_HWC2_ON_1_ADAPTER_H
+
+#define HWC2_INCLUDE_STRINGIFICATION
+#define HWC2_USE_CPP11
+#include <hardware/hwcomposer2.h>
+#undef HWC2_INCLUDE_STRINGIFICATION
+#undef HWC2_USE_CPP11
+
+#include <ui/Fence.h>
+
+#include <atomic>
+#include <map>
+#include <mutex>
+#include <queue>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+struct hwc_composer_device_1;
+struct hwc_display_contents_1;
+struct hwc_layer_1;
+
+namespace android {
+
+class HWC2On1Adapter : public hwc2_device_t
+{
+public:
+    HWC2On1Adapter(struct hwc_composer_device_1* hwc1Device);
+    ~HWC2On1Adapter();
+
+    struct hwc_composer_device_1* getHwc1Device() const { return mHwc1Device; }
+    uint8_t getHwc1MinorVersion() const { return mHwc1MinorVersion; }
+
+private:
+    static inline HWC2On1Adapter* getAdapter(hwc2_device_t* device) {
+        return static_cast<HWC2On1Adapter*>(device);
+    }
+
+    // getCapabilities
+
+    void doGetCapabilities(uint32_t* outCount,
+            int32_t* /*hwc2_capability_t*/ outCapabilities);
+    static void getCapabilitiesHook(hwc2_device_t* device, uint32_t* outCount,
+            int32_t* /*hwc2_capability_t*/ outCapabilities) {
+        getAdapter(device)->doGetCapabilities(outCount, outCapabilities);
+    }
+
+    // getFunction
+
+    hwc2_function_pointer_t doGetFunction(HWC2::FunctionDescriptor descriptor);
+    static hwc2_function_pointer_t getFunctionHook(hwc2_device_t* device,
+            int32_t intDesc) {
+        auto descriptor = static_cast<HWC2::FunctionDescriptor>(intDesc);
+        return getAdapter(device)->doGetFunction(descriptor);
+    }
+
+    // Device functions
+
+    HWC2::Error createVirtualDisplay(uint32_t width, uint32_t height,
+            hwc2_display_t* outDisplay);
+    static int32_t createVirtualDisplayHook(hwc2_device_t* device,
+            uint32_t width, uint32_t height, hwc2_display_t* outDisplay) {
+        auto error = getAdapter(device)->createVirtualDisplay(width, height,
+                outDisplay);
+        return static_cast<int32_t>(error);
+    }
+
+    HWC2::Error destroyVirtualDisplay(hwc2_display_t display);
+    static int32_t destroyVirtualDisplayHook(hwc2_device_t* device,
+            hwc2_display_t display) {
+        auto error = getAdapter(device)->destroyVirtualDisplay(display);
+        return static_cast<int32_t>(error);
+    }
+
+    std::string mDumpString;
+    void dump(uint32_t* outSize, char* outBuffer);
+    static void dumpHook(hwc2_device_t* device, uint32_t* outSize,
+            char* outBuffer) {
+        getAdapter(device)->dump(outSize, outBuffer);
+    }
+
+    uint32_t getMaxVirtualDisplayCount();
+    static uint32_t getMaxVirtualDisplayCountHook(hwc2_device_t* device) {
+        return getAdapter(device)->getMaxVirtualDisplayCount();
+    }
+
+    HWC2::Error registerCallback(HWC2::Callback descriptor,
+            hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer);
+    static int32_t registerCallbackHook(hwc2_device_t* device,
+            int32_t intDesc, hwc2_callback_data_t callbackData,
+            hwc2_function_pointer_t pointer) {
+        auto descriptor = static_cast<HWC2::Callback>(intDesc);
+        auto error = getAdapter(device)->registerCallback(descriptor,
+                callbackData, pointer);
+        return static_cast<int32_t>(error);
+    }
+
+    // Display functions
+
+    class Layer;
+
+    class SortLayersByZ {
+        public:
+            bool operator()(const std::shared_ptr<Layer>& lhs,
+                    const std::shared_ptr<Layer>& rhs);
+    };
+
+    class DisplayContentsDeleter {
+        public:
+            void operator()(struct hwc_display_contents_1* contents);
+    };
+
+    class DeferredFence {
+        public:
+            DeferredFence()
+              : mMutex(),
+                mFences({Fence::NO_FENCE, Fence::NO_FENCE}) {}
+
+            void add(int32_t fenceFd) {
+                mFences.emplace(new Fence(fenceFd));
+                mFences.pop();
+            }
+
+            const sp<Fence>& get() const {
+                return mFences.front();
+            }
+
+        private:
+            mutable std::mutex mMutex;
+            std::queue<sp<Fence>> mFences;
+    };
+
+    class FencedBuffer {
+        public:
+            FencedBuffer() : mBuffer(nullptr), mFence(Fence::NO_FENCE) {}
+
+            void setBuffer(buffer_handle_t buffer) { mBuffer = buffer; }
+            void setFence(int fenceFd) { mFence = new Fence(fenceFd); }
+
+            buffer_handle_t getBuffer() const { return mBuffer; }
+            int getFence() const { return mFence->dup(); }
+
+        private:
+            buffer_handle_t mBuffer;
+            sp<Fence> mFence;
+    };
+
+    class Display {
+        public:
+            typedef std::unique_ptr<hwc_display_contents_1,
+                    DisplayContentsDeleter> HWC1Contents;
+
+            Display(HWC2On1Adapter& device, HWC2::DisplayType type);
+
+            hwc2_display_t getId() const { return mId; }
+            HWC2On1Adapter& getDevice() const { return mDevice; }
+
+            // Does not require locking because it is set before adding the
+            // Displays to the Adapter's list of displays
+            void setHwc1Id(int32_t id) { mHwc1Id = id; }
+            int32_t getHwc1Id() const { return mHwc1Id; }
+
+            void incDirty() { ++mDirtyCount; }
+            void decDirty() { --mDirtyCount; }
+            bool isDirty() const { return mDirtyCount > 0 || mZIsDirty; }
+
+            // HWC2 Display functions
+            HWC2::Error acceptChanges();
+            HWC2::Error createLayer(hwc2_layer_t* outLayerId);
+            HWC2::Error destroyLayer(hwc2_layer_t layerId);
+            HWC2::Error getActiveConfig(hwc2_config_t* outConfigId);
+            HWC2::Error getAttribute(hwc2_config_t configId,
+                    HWC2::Attribute attribute, int32_t* outValue);
+            HWC2::Error getChangedCompositionTypes(uint32_t* outNumElements,
+                    hwc2_layer_t* outLayers, int32_t* outTypes);
+            HWC2::Error getConfigs(uint32_t* outNumConfigs,
+                    hwc2_config_t* outConfigIds);
+            HWC2::Error getDozeSupport(int32_t* outSupport);
+            HWC2::Error getName(uint32_t* outSize, char* outName);
+            HWC2::Error getReleaseFences(uint32_t* outNumElements,
+                    hwc2_layer_t* outLayers, int32_t* outFences);
+            HWC2::Error getRequests(int32_t* outDisplayRequests,
+                    uint32_t* outNumElements, hwc2_layer_t* outLayers,
+                    int32_t* outLayerRequests);
+            HWC2::Error getType(int32_t* outType);
+            HWC2::Error present(int32_t* outRetireFence);
+            HWC2::Error setActiveConfig(hwc2_config_t configId);
+            HWC2::Error setClientTarget(buffer_handle_t target,
+                    int32_t acquireFence, int32_t dataspace);
+            HWC2::Error setOutputBuffer(buffer_handle_t buffer,
+                    int32_t releaseFence);
+            HWC2::Error setPowerMode(HWC2::PowerMode mode);
+            HWC2::Error setVsyncEnabled(HWC2::Vsync enabled);
+            HWC2::Error validate(uint32_t* outNumTypes,
+                    uint32_t* outNumRequests);
+
+            HWC2::Error updateLayerZ(hwc2_layer_t layerId, uint32_t z);
+
+            // Read configs from HWC1 device
+            void populateConfigs();
+
+            // Set configs for a virtual display
+            void populateConfigs(uint32_t width, uint32_t height);
+
+            bool prepare();
+            HWC1Contents cloneRequestedContents() const;
+            void setReceivedContents(HWC1Contents contents);
+            bool hasChanges() const;
+            HWC2::Error set(hwc_display_contents_1& hwcContents);
+            void addRetireFence(int fenceFd);
+            void addReleaseFences(const hwc_display_contents_1& hwcContents);
+
+            std::string dump() const;
+
+        private:
+            class Config {
+                public:
+                    Config(Display& display, hwc2_config_t id, uint32_t hwcId)
+                      : mDisplay(display),
+                        mId(id),
+                        mHwcId(hwcId) {}
+
+                    bool isOnDisplay(const Display& display) const {
+                        return display.getId() == mDisplay.getId();
+                    }
+
+                    hwc2_config_t getId() const { return mId; }
+                    uint32_t getHwcId() const { return mHwcId; }
+
+                    void setAttribute(HWC2::Attribute attribute, int32_t value);
+                    int32_t getAttribute(HWC2::Attribute attribute) const;
+
+                    std::string toString() const;
+
+                private:
+                    Display& mDisplay;
+                    const hwc2_config_t mId;
+                    const uint32_t mHwcId;
+                    std::unordered_map<HWC2::Attribute, int32_t> mAttributes;
+            };
+
+            class Changes {
+                public:
+                    uint32_t getNumTypes() const {
+                        return static_cast<uint32_t>(mTypeChanges.size());
+                    }
+
+                    uint32_t getNumLayerRequests() const {
+                        return static_cast<uint32_t>(mLayerRequests.size());
+                    }
+
+                    const std::unordered_map<hwc2_layer_t, HWC2::Composition>&
+                            getTypeChanges() const {
+                        return mTypeChanges;
+                    }
+
+                    const std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>&
+                            getLayerRequests() const {
+                        return mLayerRequests;
+                    }
+
+                    int32_t getDisplayRequests() const {
+                        int32_t requests = 0;
+                        for (auto request : mDisplayRequests) {
+                            requests |= static_cast<int32_t>(request);
+                        }
+                        return requests;
+                    }
+
+                    void addTypeChange(hwc2_layer_t layerId,
+                            HWC2::Composition type) {
+                        mTypeChanges.insert({layerId, type});
+                    }
+
+                    void clearTypeChanges() { mTypeChanges.clear(); }
+
+                    void addLayerRequest(hwc2_layer_t layerId,
+                            HWC2::LayerRequest request) {
+                        mLayerRequests.insert({layerId, request});
+                    }
+
+                private:
+                    std::unordered_map<hwc2_layer_t, HWC2::Composition>
+                            mTypeChanges;
+                    std::unordered_map<hwc2_layer_t, HWC2::LayerRequest>
+                            mLayerRequests;
+                    std::unordered_set<HWC2::DisplayRequest> mDisplayRequests;
+            };
+
+            std::shared_ptr<const Config>
+                    getConfig(hwc2_config_t configId) const;
+
+            void reallocateHwc1Contents();
+            void assignHwc1LayerIds();
+
+            void updateTypeChanges(const struct hwc_layer_1& hwc1Layer,
+                    const Layer& layer);
+            void updateLayerRequests(const struct hwc_layer_1& hwc1Layer,
+                    const Layer& layer);
+
+            void prepareFramebufferTarget();
+
+            static std::atomic<hwc2_display_t> sNextId;
+            const hwc2_display_t mId;
+            HWC2On1Adapter& mDevice;
+
+            std::atomic<size_t> mDirtyCount;
+
+            // The state of this display should only be modified from
+            // SurfaceFlinger's main loop, with the exception of when dump is
+            // called. To prevent a bad state from crashing us during a dump
+            // call, all public calls into Display must acquire this mutex.
+            //
+            // It is recursive because we don't want to deadlock in validate
+            // (or present) when we call HWC2On1Adapter::prepareAllDisplays
+            // (or setAllDisplays), which calls back into Display functions
+            // which require locking.
+            mutable std::recursive_mutex mStateMutex;
+
+            bool mZIsDirty;
+            HWC1Contents mHwc1RequestedContents;
+            HWC1Contents mHwc1ReceivedContents;
+            DeferredFence mRetireFence;
+
+            // Will only be non-null after the layer has been validated but
+            // before it has been presented
+            std::unique_ptr<Changes> mChanges;
+
+            int32_t mHwc1Id;
+            std::vector<std::shared_ptr<Config>> mConfigs;
+            std::shared_ptr<const Config> mActiveConfig;
+            std::string mName;
+            HWC2::DisplayType mType;
+            HWC2::PowerMode mPowerMode;
+            HWC2::Vsync mVsyncEnabled;
+
+            FencedBuffer mClientTarget;
+            FencedBuffer mOutputBuffer;
+
+            std::multiset<std::shared_ptr<Layer>, SortLayersByZ> mLayers;
+            std::unordered_map<size_t, std::shared_ptr<Layer>> mHwc1LayerMap;
+    };
+
+    template <typename ...Args>
+    static int32_t callDisplayFunction(hwc2_device_t* device,
+            hwc2_display_t displayId, HWC2::Error (Display::*member)(Args...),
+            Args... args) {
+        auto display = getAdapter(device)->getDisplay(displayId);
+        if (!display) {
+            return static_cast<int32_t>(HWC2::Error::BadDisplay);
+        }
+        auto error = ((*display).*member)(std::forward<Args>(args)...);
+        return static_cast<int32_t>(error);
+    }
+
+    template <typename MF, MF memFunc, typename ...Args>
+    static int32_t displayHook(hwc2_device_t* device, hwc2_display_t displayId,
+            Args... args) {
+        return HWC2On1Adapter::callDisplayFunction(device, displayId, memFunc,
+                std::forward<Args>(args)...);
+    }
+
+    static int32_t getDisplayAttributeHook(hwc2_device_t* device,
+            hwc2_display_t display, hwc2_config_t config,
+            int32_t intAttribute, int32_t* outValue) {
+        auto attribute = static_cast<HWC2::Attribute>(intAttribute);
+        return callDisplayFunction(device, display, &Display::getAttribute,
+                config, attribute, outValue);
+    }
+
+    static int32_t setPowerModeHook(hwc2_device_t* device,
+            hwc2_display_t display, int32_t intMode) {
+        auto mode = static_cast<HWC2::PowerMode>(intMode);
+        return callDisplayFunction(device, display, &Display::setPowerMode,
+                mode);
+    }
+
+    static int32_t setVsyncEnabledHook(hwc2_device_t* device,
+            hwc2_display_t display, int32_t intEnabled) {
+        auto enabled = static_cast<HWC2::Vsync>(intEnabled);
+        return callDisplayFunction(device, display, &Display::setVsyncEnabled,
+                enabled);
+    }
+
+    // Layer functions
+
+    template <typename T>
+    class LatchedState {
+        public:
+            LatchedState(Layer& parent, T initialValue)
+              : mParent(parent),
+                mPendingValue(initialValue),
+                mValue(initialValue) {}
+
+            void setPending(T value) {
+                if (value == mPendingValue) {
+                    return;
+                }
+                if (mPendingValue == mValue) {
+                    mParent.incDirty();
+                } else if (value == mValue) {
+                    mParent.decDirty();
+                }
+                mPendingValue = value;
+            }
+
+            T getValue() const { return mValue; }
+            T getPendingValue() const { return mPendingValue; }
+
+            bool isDirty() const { return mPendingValue != mValue; }
+
+            void latch() {
+                if (isDirty()) {
+                    mValue = mPendingValue;
+                    mParent.decDirty();
+                }
+            }
+
+        private:
+            Layer& mParent;
+            T mPendingValue;
+            T mValue;
+    };
+
+    class Layer {
+        public:
+            Layer(Display& display);
+
+            bool operator==(const Layer& other) { return mId == other.mId; }
+            bool operator!=(const Layer& other) { return !(*this == other); }
+
+            hwc2_layer_t getId() const { return mId; }
+            Display& getDisplay() const { return mDisplay; }
+
+            void incDirty() { if (mDirtyCount++ == 0) mDisplay.incDirty(); }
+            void decDirty() { if (--mDirtyCount == 0) mDisplay.decDirty(); }
+            bool isDirty() const { return mDirtyCount > 0; }
+
+            // HWC2 Layer functions
+            HWC2::Error setBuffer(buffer_handle_t buffer, int32_t acquireFence);
+            HWC2::Error setCursorPosition(int32_t x, int32_t y);
+            HWC2::Error setSurfaceDamage(hwc_region_t damage);
+
+            // HWC2 Layer state functions
+            HWC2::Error setBlendMode(HWC2::BlendMode mode);
+            HWC2::Error setColor(hwc_color_t color);
+            HWC2::Error setCompositionType(HWC2::Composition type);
+            HWC2::Error setDisplayFrame(hwc_rect_t frame);
+            HWC2::Error setPlaneAlpha(float alpha);
+            HWC2::Error setSidebandStream(const native_handle_t* stream);
+            HWC2::Error setSourceCrop(hwc_frect_t crop);
+            HWC2::Error setTransform(HWC2::Transform transform);
+            HWC2::Error setVisibleRegion(hwc_region_t visible);
+            HWC2::Error setZ(uint32_t z);
+
+            HWC2::Composition getCompositionType() const {
+                return mCompositionType.getValue();
+            }
+            uint32_t getZ() const { return mZ; }
+
+            void addReleaseFence(int fenceFd);
+            const sp<Fence>& getReleaseFence() const;
+
+            void setHwc1Id(size_t id) { mHwc1Id = id; }
+            size_t getHwc1Id() const { return mHwc1Id; }
+
+            void applyState(struct hwc_layer_1& hwc1Layer, bool applyAllState);
+
+            std::string dump() const;
+
+        private:
+            void applyCommonState(struct hwc_layer_1& hwc1Layer,
+                    bool applyAllState);
+            void applySolidColorState(struct hwc_layer_1& hwc1Layer,
+                    bool applyAllState);
+            void applySidebandState(struct hwc_layer_1& hwc1Layer,
+                    bool applyAllState);
+            void applyBufferState(struct hwc_layer_1& hwc1Layer);
+            void applyCompositionType(struct hwc_layer_1& hwc1Layer,
+                    bool applyAllState);
+
+            static std::atomic<hwc2_layer_t> sNextId;
+            const hwc2_layer_t mId;
+            Display& mDisplay;
+            size_t mDirtyCount;
+
+            FencedBuffer mBuffer;
+            std::vector<hwc_rect_t> mSurfaceDamage;
+
+            LatchedState<HWC2::BlendMode> mBlendMode;
+            LatchedState<hwc_color_t> mColor;
+            LatchedState<HWC2::Composition> mCompositionType;
+            LatchedState<hwc_rect_t> mDisplayFrame;
+            LatchedState<float> mPlaneAlpha;
+            LatchedState<const native_handle_t*> mSidebandStream;
+            LatchedState<hwc_frect_t> mSourceCrop;
+            LatchedState<HWC2::Transform> mTransform;
+            LatchedState<std::vector<hwc_rect_t>> mVisibleRegion;
+            uint32_t mZ;
+
+            DeferredFence mReleaseFence;
+
+            size_t mHwc1Id;
+            bool mHasUnsupportedPlaneAlpha;
+    };
+
+    template <typename ...Args>
+    static int32_t callLayerFunction(hwc2_device_t* device,
+            hwc2_display_t displayId, hwc2_layer_t layerId,
+            HWC2::Error (Layer::*member)(Args...), Args... args) {
+        auto result = getAdapter(device)->getLayer(displayId, layerId);
+        auto error = std::get<HWC2::Error>(result);
+        if (error == HWC2::Error::None) {
+            auto layer = std::get<Layer*>(result);
+            error = ((*layer).*member)(std::forward<Args>(args)...);
+        }
+        return static_cast<int32_t>(error);
+    }
+
+    template <typename MF, MF memFunc, typename ...Args>
+    static int32_t layerHook(hwc2_device_t* device, hwc2_display_t displayId,
+            hwc2_layer_t layerId, Args... args) {
+        return HWC2On1Adapter::callLayerFunction(device, displayId, layerId,
+                memFunc, std::forward<Args>(args)...);
+    }
+
+    // Layer state functions
+
+    static int32_t setLayerBlendModeHook(hwc2_device_t* device,
+            hwc2_display_t display, hwc2_layer_t layer, int32_t intMode) {
+        auto mode = static_cast<HWC2::BlendMode>(intMode);
+        return callLayerFunction(device, display, layer,
+                &Layer::setBlendMode, mode);
+    }
+
+    static int32_t setLayerCompositionTypeHook(hwc2_device_t* device,
+            hwc2_display_t display, hwc2_layer_t layer, int32_t intType) {
+        auto type = static_cast<HWC2::Composition>(intType);
+        return callLayerFunction(device, display, layer,
+                &Layer::setCompositionType, type);
+    }
+
+    static int32_t setLayerTransformHook(hwc2_device_t* device,
+            hwc2_display_t display, hwc2_layer_t layer, int32_t intTransform) {
+        auto transform = static_cast<HWC2::Transform>(intTransform);
+        return callLayerFunction(device, display, layer, &Layer::setTransform,
+                transform);
+    }
+
+    static int32_t setLayerZOrderHook(hwc2_device_t* device,
+            hwc2_display_t display, hwc2_layer_t layer, uint32_t z) {
+        return callDisplayFunction(device, display, &Display::updateLayerZ,
+                layer, z);
+    }
+
+    // Adapter internals
+
+    void populateCapabilities();
+    Display* getDisplay(hwc2_display_t id);
+    std::tuple<Layer*, HWC2::Error> getLayer(hwc2_display_t displayId,
+            hwc2_layer_t layerId);
+    void populatePrimary();
+
+    bool prepareAllDisplays();
+    std::vector<struct hwc_display_contents_1*> mHwc1Contents;
+    HWC2::Error setAllDisplays();
+
+    void hwc1Invalidate();
+    void hwc1Vsync(int hwc1DisplayId, int64_t timestamp);
+    void hwc1Hotplug(int hwc1DisplayId, int connected);
+
+    // These are set in the constructor and before any asynchronous events are
+    // possible
+
+    struct hwc_composer_device_1* const mHwc1Device;
+    const uint8_t mHwc1MinorVersion;
+    bool mHwc1SupportsVirtualDisplays;
+
+    class Callbacks;
+    const std::unique_ptr<Callbacks> mHwc1Callbacks;
+
+    std::unordered_set<HWC2::Capability> mCapabilities;
+
+    // These are only accessed from the main SurfaceFlinger thread (not from
+    // callbacks or dump
+
+    std::map<hwc2_layer_t, std::shared_ptr<Layer>> mLayers;
+    std::shared_ptr<Display> mHwc1VirtualDisplay;
+
+    // These are potentially accessed from multiple threads, and are protected
+    // by this mutex
+    std::timed_mutex mStateMutex;
+
+    struct CallbackInfo {
+        hwc2_callback_data_t data;
+        hwc2_function_pointer_t pointer;
+    };
+    std::unordered_map<HWC2::Callback, CallbackInfo> mCallbacks;
+    bool mHasPendingInvalidate;
+    std::vector<std::pair<int, int64_t>> mPendingVsyncs;
+    std::vector<std::pair<int, int>> mPendingHotplugs;
+
+    std::map<hwc2_display_t, std::shared_ptr<Display>> mDisplays;
+    std::unordered_map<int, hwc2_display_t> mHwc1DisplayMap;
+};
+
+} // namespace android
+
+#endif
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1e33847..737cc82 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3183,14 +3183,7 @@
     // if we have secure windows on this display, never allow the screen capture
     // unless the producer interface is local (i.e.: we can take a screenshot for
     // ourselves).
-    if (!IInterface::asBinder(producer)->localBinder()) {
-        Mutex::Autolock _l(mStateLock);
-        sp<const DisplayDevice> hw(getDisplayDevice(display));
-        if (hw->getSecureLayerVisible()) {
-            ALOGW("FB is protected: PERMISSION_DENIED");
-            return PERMISSION_DENIED;
-        }
-    }
+    bool isLocalScreenshot = IInterface::asBinder(producer)->localBinder();
 
     // Convert to surfaceflinger's internal rotation type.
     Transform::orientation_flags rotationFlags;
@@ -3223,19 +3216,22 @@
         bool useIdentityTransform;
         Transform::orientation_flags rotation;
         status_t result;
+        bool isLocalScreenshot;
     public:
         MessageCaptureScreen(SurfaceFlinger* flinger,
                 const sp<IBinder>& display,
                 const sp<IGraphicBufferProducer>& producer,
                 Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
                 uint32_t minLayerZ, uint32_t maxLayerZ,
-                bool useIdentityTransform, Transform::orientation_flags rotation)
+                bool useIdentityTransform,
+                Transform::orientation_flags rotation,
+                bool isLocalScreenshot)
             : flinger(flinger), display(display), producer(producer),
               sourceCrop(sourceCrop), reqWidth(reqWidth), reqHeight(reqHeight),
               minLayerZ(minLayerZ), maxLayerZ(maxLayerZ),
               useIdentityTransform(useIdentityTransform),
-              rotation(rotation),
-              result(PERMISSION_DENIED)
+              rotation(rotation), result(PERMISSION_DENIED),
+              isLocalScreenshot(isLocalScreenshot)
         {
         }
         status_t getResult() const {
@@ -3246,7 +3242,7 @@
             sp<const DisplayDevice> hw(flinger->getDisplayDevice(display));
             result = flinger->captureScreenImplLocked(hw, producer,
                     sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ,
-                    useIdentityTransform, rotation);
+                    useIdentityTransform, rotation, isLocalScreenshot);
             static_cast<GraphicProducerWrapper*>(IInterface::asBinder(producer).get())->exit(result);
             return true;
         }
@@ -3269,7 +3265,7 @@
     sp<MessageBase> msg = new MessageCaptureScreen(this,
             display, IGraphicBufferProducer::asInterface( wrapper ),
             sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ,
-            useIdentityTransform, rotationFlags);
+            useIdentityTransform, rotationFlags, isLocalScreenshot);
 
     status_t res = postMessageAsync(msg);
     if (res == NO_ERROR) {
@@ -3353,7 +3349,8 @@
         const sp<IGraphicBufferProducer>& producer,
         Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
         uint32_t minLayerZ, uint32_t maxLayerZ,
-        bool useIdentityTransform, Transform::orientation_flags rotation)
+        bool useIdentityTransform, Transform::orientation_flags rotation,
+        bool isLocalScreenshot)
 {
     ATRACE_CALL();
 
@@ -3374,6 +3371,24 @@
     reqWidth  = (!reqWidth)  ? hw_w : reqWidth;
     reqHeight = (!reqHeight) ? hw_h : reqHeight;
 
+    bool secureLayerIsVisible = false;
+    const LayerVector& layers(mDrawingState.layersSortedByZ);
+    const size_t count = layers.size();
+    for (size_t i = 0 ; i < count ; ++i) {
+        const sp<Layer>& layer(layers[i]);
+        const Layer::State& state(layer->getDrawingState());
+        if (state.layerStack == hw->getLayerStack() && state.z >= minLayerZ &&
+                state.z <= maxLayerZ && layer->isVisible() &&
+                layer->isSecure()) {
+            secureLayerIsVisible = true;
+        }
+    }
+
+    if (!isLocalScreenshot && secureLayerIsVisible) {
+        ALOGW("FB is protected: PERMISSION_DENIED");
+        return PERMISSION_DENIED;
+    }
+
     // create a surface (because we're a producer, and we need to
     // dequeue/queue a buffer)
     sp<Surface> sur = new Surface(producer, false);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 4e0160a..4101a70 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -329,7 +329,8 @@
             const sp<IGraphicBufferProducer>& producer,
             Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
             uint32_t minLayerZ, uint32_t maxLayerZ,
-            bool useIdentityTransform, Transform::orientation_flags rotation);
+            bool useIdentityTransform, Transform::orientation_flags rotation,
+            bool isLocalScreenshot);
 
     /* ------------------------------------------------------------------------
      * EGL
diff --git a/vulkan/api/templates/vulkan_h.tmpl b/vulkan/api/templates/vulkan_h.tmpl
index b2a77ec..83a5e40 100644
--- a/vulkan/api/templates/vulkan_h.tmpl
+++ b/vulkan/api/templates/vulkan_h.tmpl
@@ -17,7 +17,7 @@
 #endif

 /*
-** Copyright (c) 2015 The Khronos Group Inc.
+** Copyright (c) 2015-2016 The Khronos Group Inc.
 **
 ** Permission is hereby granted, free of charge, to any person obtaining a
 ** copy of this software and/or associated documentation files (the
@@ -47,12 +47,16 @@
 #define VK_VERSION_1_0 1
 #include "vk_platform.h"

-#define VK_MAKE_VERSION(major, minor, patch) ((major << 22) | (minor << 12) | patch)
+#define VK_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch))

 // Vulkan API version supported by this file
 #define VK_API_VERSION \
     VK_MAKE_VERSION({{Global "VERSION_MAJOR"}}, {{Global "VERSION_MINOR"}}, {{Global "VERSION_PATCH"}})

+#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22)
+#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff)
+#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff)

 #if defined(__cplusplus) && ((defined(_MSC_VER) && _MSC_VER >= 1800 || __cplusplus >= 201103L)
     #define VK_NULL_HANDLE nullptr
 #else
diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api
index 9b1e684..10565ab 100644
--- a/vulkan/api/vulkan.api
+++ b/vulkan/api/vulkan.api
@@ -28,7 +28,7 @@
 // API version (major.minor.patch)
 define VERSION_MAJOR 1
 define VERSION_MINOR 0
-define VERSION_PATCH 2
+define VERSION_PATCH 3
 
 // API limits
 define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256
@@ -75,7 +75,7 @@
 @extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_SPEC_VERSION     5
 @extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_NAME             "VK_KHR_win32_surface"
 
-@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION       2
+@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION       1
 @extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_NAME               "VK_EXT_debug_report"
 
 
@@ -1299,9 +1299,9 @@
 type VkFlags VkDebugReportFlagsEXT
 @extension("VK_EXT_debug_report")
 bitfield VkDebugReportFlagBitsEXT {
-    VK_DEBUG_REPORT_INFO_BIT_EXT                            = 0x00000001,
-    VK_DEBUG_REPORT_WARN_BIT_EXT                            = 0x00000002,
-    VK_DEBUG_REPORT_PERF_WARN_BIT_EXT                       = 0x00000004,
+    VK_DEBUG_REPORT_INFORMATION_BIT_EXT                     = 0x00000001,
+    VK_DEBUG_REPORT_WARNING_BIT_EXT                         = 0x00000002,
+    VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT             = 0x00000004,
     VK_DEBUG_REPORT_ERROR_BIT_EXT                           = 0x00000008,
     VK_DEBUG_REPORT_DEBUG_BIT_EXT                           = 0x00000010,
 }
@@ -5130,7 +5130,7 @@
 cmd VkResult vkGetPhysicalDeviceWin32PresentationSupportKHR(
         VkPhysicalDevice                        physicalDevice,
         u32                                     queueFamilyIndex) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)    
+    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
     return ?
 }
 
diff --git a/vulkan/include/vulkan/vk_ext_debug_report.h b/vulkan/include/vulkan/vk_ext_debug_report.h
deleted file mode 100644
index c391033..0000000
--- a/vulkan/include/vulkan/vk_ext_debug_report.h
+++ /dev/null
@@ -1,149 +0,0 @@
-//
-// File: vk_ext_debug_report.h
-//
-/*
- *
- * Copyright (C) 2015 Valve Corporation
- * Copyright (C) 2015 Google Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Author: Cody Northrop <cody@lunarg.com>
- * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
- * Author: Tony Barbour <tony@LunarG.com>
- *
- */
-
-#pragma once
-
-#include "vulkan/vulkan.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif // __cplusplus
-
-/*
-***************************************************************************************************
-*   DebugReport Vulkan Extension API
-***************************************************************************************************
-*/
-#define VK_EXT_debug_report 1
-VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT)
-
-#define VK_EXT_DEBUG_REPORT_SPEC_VERSION    2
-#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report"
-
-
-typedef enum VkDebugReportObjectTypeEXT {
-    VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT = 0,
-    VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT = 1,
-    VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT = 2,
-    VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT = 3,
-    VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT = 4,
-    VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT = 5,
-    VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT = 6,
-    VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT = 7,
-    VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT = 8,
-    VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT = 9,
-    VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT = 10,
-    VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT = 11,
-    VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT = 12,
-    VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT = 13,
-    VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT = 14,
-    VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT = 15,
-    VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT = 16,
-    VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT = 17,
-    VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT = 18,
-    VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT = 19,
-    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT = 20,
-    VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT = 21,
-    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT = 22,
-    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT = 23,
-    VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT = 24,
-    VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT = 25,
-    VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26,
-    VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27,
-    VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = 28,
-} VkDebugReportObjectTypeEXT;
-
-typedef enum VkDebugReportErrorEXT {
-    VK_DEBUG_REPORT_ERROR_NONE_EXT = 0,
-    VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT = 1,
-} VkDebugReportErrorEXT;
-
-typedef enum VkDebugReportFlagBitsEXT {
-    VK_DEBUG_REPORT_INFO_BIT_EXT = 0x00000001,
-    VK_DEBUG_REPORT_WARN_BIT_EXT = 0x00000002,
-    VK_DEBUG_REPORT_PERF_WARN_BIT_EXT = 0x00000004,
-    VK_DEBUG_REPORT_ERROR_BIT_EXT = 0x00000008,
-    VK_DEBUG_REPORT_DEBUG_BIT_EXT = 0x00000010,
-} VkDebugReportFlagBitsEXT;
-typedef VkFlags VkDebugReportFlagsEXT;
-
-typedef VkBool32 (VKAPI_PTR *PFN_vkDebugReportCallbackEXT)(
-    VkDebugReportFlagsEXT                       flags,
-    VkDebugReportObjectTypeEXT                  objectType,
-    uint64_t                                    object,
-    size_t                                      location,
-    int32_t                                     messageCode,
-    const char*                                 pLayerPrefix,
-    const char*                                 pMessage,
-    void*                                       pUserData);
-
-
-typedef struct VkDebugReportCallbackCreateInfoEXT {
-    VkStructureType                             sType;
-    const void*                                 pNext;
-    VkDebugReportFlagsEXT                       flags;
-    PFN_vkDebugReportCallbackEXT                pfnCallback;
-    void*                                       pUserData;
-} VkDebugReportCallbackCreateInfoEXT;
-
-typedef VkResult (VKAPI_PTR *PFN_vkCreateDebugReportCallbackEXT)(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback);
-typedef void (VKAPI_PTR *PFN_vkDestroyDebugReportCallbackEXT)(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator);
-typedef void (VKAPI_PTR *PFN_vkDebugReportMessageEXT)(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage);
-
-#ifndef VK_NO_PROTOTYPES
-VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugReportCallbackEXT(
-    VkInstance                                  instance,
-    const VkDebugReportCallbackCreateInfoEXT*   pCreateInfo,
-    const VkAllocationCallbacks*                pAllocator,
-    VkDebugReportCallbackEXT*                   pCallback);
-
-VKAPI_ATTR void VKAPI_CALL vkDestroyDebugReportCallbackEXT(
-    VkInstance                                  instance,
-    VkDebugReportCallbackEXT                    callback,
-    const VkAllocationCallbacks*                pAllocator);
-
-VKAPI_ATTR void VKAPI_CALL vkDebugReportMessageEXT(
-    VkInstance                                  instance,
-    VkDebugReportFlagsEXT                       flags,
-    VkDebugReportObjectTypeEXT                  objectType,
-    uint64_t                                    object,
-    size_t                                      location,
-    int32_t                                     messageCode,
-    const char*                                 pLayerPrefix,
-    const char*                                 pMessage);
-#endif
-
-#ifdef __cplusplus
-} // extern "C"
-#endif // __cplusplus
-
diff --git a/vulkan/include/vulkan/vk_layer_interface.h b/vulkan/include/vulkan/vk_layer_interface.h
index 8aef495..5aae51e 100644
--- a/vulkan/include/vulkan/vk_layer_interface.h
+++ b/vulkan/include/vulkan/vk_layer_interface.h
@@ -31,7 +31,6 @@
 #pragma once
 
 #include <vulkan/vulkan.h>
-#include <vulkan/vk_ext_debug_report.h>
 
 // ------------------------------------------------------------------------------------------------
 // CreateInstance and CreateDevice support structures
diff --git a/vulkan/include/vulkan/vulkan.h b/vulkan/include/vulkan/vulkan.h
index 9940f85..cd6a71a 100644
--- a/vulkan/include/vulkan/vulkan.h
+++ b/vulkan/include/vulkan/vulkan.h
@@ -6,7 +6,7 @@
 #endif
 
 /*
-** Copyright (c) 2015 The Khronos Group Inc.
+** Copyright (c) 2015-2016 The Khronos Group Inc.
 **
 ** Permission is hereby granted, free of charge, to any person obtaining a
 ** copy of this software and/or associated documentation files (the
@@ -38,11 +38,14 @@
 #include "vk_platform.h"
 
 #define VK_MAKE_VERSION(major, minor, patch) \
-    ((major << 22) | (minor << 12) | patch)
+    (((major) << 22) | ((minor) << 12) | (patch))
 
 // Vulkan API version supported by this file
-#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 2)
+#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 3)
 
+#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22)
+#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff)
+#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff)
 
 #define VK_NULL_HANDLE 0
         
@@ -3664,6 +3667,107 @@
 #endif
 #endif /* VK_USE_PLATFORM_WIN32_KHR */
 
+#define VK_EXT_debug_report 1
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT)
+
+#define VK_EXT_DEBUG_REPORT_SPEC_VERSION  1
+#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report"
+
+
+typedef enum VkDebugReportObjectTypeEXT {
+    VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT = 0,
+    VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT = 1,
+    VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT = 2,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT = 3,
+    VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT = 4,
+    VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT = 5,
+    VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT = 6,
+    VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT = 7,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT = 8,
+    VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT = 9,
+    VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT = 10,
+    VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT = 11,
+    VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT = 12,
+    VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT = 13,
+    VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT = 14,
+    VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT = 15,
+    VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT = 16,
+    VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT = 17,
+    VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT = 18,
+    VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT = 19,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT = 20,
+    VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT = 21,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT = 22,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT = 23,
+    VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT = 24,
+    VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT = 25,
+    VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26,
+    VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27,
+    VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = 28,
+} VkDebugReportObjectTypeEXT;
+
+typedef enum VkDebugReportErrorEXT {
+    VK_DEBUG_REPORT_ERROR_NONE_EXT = 0,
+    VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT = 1,
+} VkDebugReportErrorEXT;
+
+
+typedef enum VkDebugReportFlagBitsEXT {
+    VK_DEBUG_REPORT_INFORMATION_BIT_EXT = 0x00000001,
+    VK_DEBUG_REPORT_WARNING_BIT_EXT = 0x00000002,
+    VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT = 0x00000004,
+    VK_DEBUG_REPORT_ERROR_BIT_EXT = 0x00000008,
+    VK_DEBUG_REPORT_DEBUG_BIT_EXT = 0x00000010,
+} VkDebugReportFlagBitsEXT;
+typedef VkFlags VkDebugReportFlagsEXT;
+
+typedef VkBool32 (VKAPI_PTR *PFN_vkDebugReportCallbackEXT)(
+    VkDebugReportFlagsEXT                       flags,
+    VkDebugReportObjectTypeEXT                  objectType,
+    uint64_t                                    object,
+    size_t                                      location,
+    int32_t                                     messageCode,
+    const char*                                 pLayerPrefix,
+    const char*                                 pMessage,
+    void*                                       pUserData);
+
+
+typedef struct VkDebugReportCallbackCreateInfoEXT {
+    VkStructureType                 sType;
+    const void*                     pNext;
+    VkDebugReportFlagsEXT           flags;
+    PFN_vkDebugReportCallbackEXT    pfnCallback;
+    void*                           pUserData;
+} VkDebugReportCallbackCreateInfoEXT;
+
+
+typedef VkResult (VKAPI_PTR *PFN_vkCreateDebugReportCallbackEXT)(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback);
+typedef void (VKAPI_PTR *PFN_vkDestroyDebugReportCallbackEXT)(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator);
+typedef void (VKAPI_PTR *PFN_vkDebugReportMessageEXT)(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage);
+
+#ifndef VK_NO_PROTOTYPES
+VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugReportCallbackEXT(
+    VkInstance                                  instance,
+    const VkDebugReportCallbackCreateInfoEXT*   pCreateInfo,
+    const VkAllocationCallbacks*                pAllocator,
+    VkDebugReportCallbackEXT*                   pCallback);
+
+VKAPI_ATTR void VKAPI_CALL vkDestroyDebugReportCallbackEXT(
+    VkInstance                                  instance,
+    VkDebugReportCallbackEXT                    callback,
+    const VkAllocationCallbacks*                pAllocator);
+
+VKAPI_ATTR void VKAPI_CALL vkDebugReportMessageEXT(
+    VkInstance                                  instance,
+    VkDebugReportFlagsEXT                       flags,
+    VkDebugReportObjectTypeEXT                  objectType,
+    uint64_t                                    object,
+    size_t                                      location,
+    int32_t                                     messageCode,
+    const char*                                 pLayerPrefix,
+    const char*                                 pMessage);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/vulkan/libvulkan/debug_report.h b/vulkan/libvulkan/debug_report.h
index 5bce240..c6f7570 100644
--- a/vulkan/libvulkan/debug_report.h
+++ b/vulkan/libvulkan/debug_report.h
@@ -18,7 +18,6 @@
 #define LIBVULKAN_DEBUG_REPORT_H 1
 
 #include <shared_mutex>
-#include <vulkan/vk_ext_debug_report.h>
 
 namespace vulkan {
 
diff --git a/vulkan/libvulkan/dispatch.tmpl b/vulkan/libvulkan/dispatch.tmpl
index 306aae4..0a0338e 100644
--- a/vulkan/libvulkan/dispatch.tmpl
+++ b/vulkan/libvulkan/dispatch.tmpl
@@ -46,7 +46,6 @@

 #define VK_USE_PLATFORM_ANDROID_KHR
 #include <vulkan/vk_android_native_buffer.h>
-#include <vulkan/vk_ext_debug_report.h>
 #include <vulkan/vulkan.h>

 namespace vulkan {
diff --git a/vulkan/libvulkan/dispatch_gen.h b/vulkan/libvulkan/dispatch_gen.h
index 7bab6ca..cef4ccf 100644
--- a/vulkan/libvulkan/dispatch_gen.h
+++ b/vulkan/libvulkan/dispatch_gen.h
@@ -18,7 +18,6 @@
 
 #define VK_USE_PLATFORM_ANDROID_KHR
 #include <vulkan/vk_android_native_buffer.h>
-#include <vulkan/vk_ext_debug_report.h>
 #include <vulkan/vulkan.h>
 
 namespace vulkan {
diff --git a/vulkan/libvulkan/loader.cpp b/vulkan/libvulkan/loader.cpp
index 1a57c22..fc60f35 100644
--- a/vulkan/libvulkan/loader.cpp
+++ b/vulkan/libvulkan/loader.cpp
@@ -496,7 +496,7 @@
                                  void* /*user_data*/) {
     if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
         ALOGE("[%s] Code %d : %s", layer_prefix, message_code, message);
-    } else if (flags & VK_DEBUG_REPORT_WARN_BIT_EXT) {
+    } else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
         ALOGW("[%s] Code %d : %s", layer_prefix, message_code, message);
     }
     return false;
@@ -1205,7 +1205,7 @@
         const VkDebugReportCallbackCreateInfoEXT callback_create_info = {
             .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT,
             .flags =
-                VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARN_BIT_EXT,
+                VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT,
             .pfnCallback = LogDebugMessageCallback,
         };
         PFN_vkCreateDebugReportCallbackEXT create_debug_report_callback =
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index f8de675..5fefc62 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -107,6 +107,79 @@
     }
 }
 
+const VkSurfaceTransformFlagsKHR kSupportedTransforms =
+    VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR |
+    VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR |
+    VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR |
+    VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR |
+    // TODO(jessehall): See TODO in TranslateNativeToVulkanTransform.
+    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR |
+    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR |
+    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR |
+    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR |
+    VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR;
+
+VkSurfaceTransformFlagBitsKHR TranslateNativeToVulkanTransform(int native) {
+    // Native and Vulkan transforms are isomorphic, but are represented
+    // differently. Vulkan transforms are built up of an optional horizontal
+    // mirror, followed by a clockwise 0/90/180/270-degree rotation. Native
+    // transforms are built up from a horizontal flip, vertical flip, and
+    // 90-degree rotation, all optional but always in that order.
+
+    // TODO(jessehall): For now, only support pure rotations, not
+    // flip or flip-and-rotate, until I have more time to test them and build
+    // sample code. As far as I know we never actually use anything besides
+    // pure rotations anyway.
+
+    switch (native) {
+        case 0:  // 0x0
+            return VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+        // case NATIVE_WINDOW_TRANSFORM_FLIP_H:  // 0x1
+        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR;
+        // case NATIVE_WINDOW_TRANSFORM_FLIP_V:  // 0x2
+        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR;
+        case NATIVE_WINDOW_TRANSFORM_ROT_180:  // FLIP_H | FLIP_V
+            return VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR;
+        case NATIVE_WINDOW_TRANSFORM_ROT_90:  // 0x4
+            return VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR;
+        // case NATIVE_WINDOW_TRANSFORM_FLIP_H | NATIVE_WINDOW_TRANSFORM_ROT_90:
+        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR;
+        // case NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_ROT_90:
+        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR;
+        case NATIVE_WINDOW_TRANSFORM_ROT_270:  // FLIP_H | FLIP_V | ROT_90
+            return VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;
+        case NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY:
+        default:
+            return VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+    }
+}
+
+int InvertTransformToNative(VkSurfaceTransformFlagBitsKHR 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;
+        // TODO(jessehall): See TODO in TranslateNativeToVulkanTransform.
+        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR:
+        //     return NATIVE_WINDOW_TRANSFORM_FLIP_H;
+        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR:
+        //     return NATIVE_WINDOW_TRANSFORM_FLIP_H |
+        //            NATIVE_WINDOW_TRANSFORM_ROT_90;
+        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR:
+        //     return NATIVE_WINDOW_TRANSFORM_FLIP_V;
+        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR:
+        //     return NATIVE_WINDOW_TRANSFORM_FLIP_V |
+        //            NATIVE_WINDOW_TRANSFORM_ROT_90;
+        case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
+        case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR:
+        default:
+            return 0;
+    }
+}
+
 // ----------------------------------------------------------------------------
 
 struct Surface {
@@ -238,6 +311,14 @@
         return VK_ERROR_INITIALIZATION_FAILED;
     }
 
+    int transform_hint;
+    err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
+    if (err != 0) {
+        ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
+              strerror(-err), err);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
     // TODO(jessehall): Figure out what the min/max values should be.
     capabilities->minImageCount = 2;
     capabilities->maxImageCount = 3;
@@ -252,12 +333,9 @@
 
     capabilities->maxImageArrayLayers = 1;
 
-    // TODO(jessehall): We can support all transforms, fix this once
-    // implemented.
-    capabilities->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
-
-    // TODO(jessehall): Implement based on NATIVE_WINDOW_TRANSFORM_HINT.
-    capabilities->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+    capabilities->supportedTransforms = kSupportedTransforms;
+    capabilities->currentTransform =
+        TranslateNativeToVulkanTransform(transform_hint);
 
     // On Android, window composition is a WindowManager property, not something
     // associated with the bufferqueue. It can't be changed from here.
@@ -347,8 +425,9 @@
              "color spaces other than SRGB_NONLINEAR not yet implemented");
     ALOGE_IF(create_info->oldSwapchain,
              "swapchain re-creation not yet implemented");
-    ALOGE_IF(create_info->preTransform != VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
-             "swapchain preTransform not yet implemented");
+    ALOGE_IF((create_info->preTransform & ~kSupportedTransforms) != 0,
+             "swapchain preTransform %d not supported",
+             create_info->preTransform);
     ALOGW_IF(!(create_info->presentMode == VK_PRESENT_MODE_FIFO_KHR ||
                create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR),
              "swapchain present mode %d not supported",
@@ -402,6 +481,26 @@
         return VK_ERROR_INITIALIZATION_FAILED;
     }
 
+    // VkSwapchainCreateInfo::preTransform indicates the transformation the app
+    // applied during rendering. native_window_set_transform() expects the
+    // inverse: 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(
+        surface.window.get(),
+        InvertTransformToNative(create_info->preTransform));
+    if (err != 0) {
+        // TODO(jessehall): Improve error reporting. Can we enumerate possible
+        // errors and translate them to valid Vulkan result codes?
+        ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
+              InvertTransformToNative(create_info->preTransform),
+              strerror(-err), err);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
+
     err = native_window_set_scaling_mode(
         surface.window.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
     if (err != 0) {
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index cd61e86..6f57238 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -15,7 +15,6 @@
  */
 
 #include <hardware/hwvulkan.h>
-#include <vulkan/vk_ext_debug_report.h>
 
 #include <algorithm>
 #include <array>
diff --git a/vulkan/nulldrv/null_driver.tmpl b/vulkan/nulldrv/null_driver.tmpl
index 7488692..3a84971 100644
--- a/vulkan/nulldrv/null_driver.tmpl
+++ b/vulkan/nulldrv/null_driver.tmpl
@@ -49,7 +49,6 @@
 #define NULLDRV_NULL_DRIVER_H 1

 #include <vulkan/vk_android_native_buffer.h>
-#include <vulkan/vk_ext_debug_report.h>
 #include <vulkan/vulkan.h>

 namespace null_driver {«
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 87ff681..98952b8 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -20,7 +20,6 @@
 #define NULLDRV_NULL_DRIVER_H 1
 
 #include <vulkan/vk_android_native_buffer.h>
-#include <vulkan/vk_ext_debug_report.h>
 #include <vulkan/vulkan.h>
 
 namespace null_driver {
diff --git a/vulkan/tools/Android.mk b/vulkan/tools/Android.mk
index 31d6089..337e683 100644
--- a/vulkan/tools/Android.mk
+++ b/vulkan/tools/Android.mk
@@ -21,7 +21,8 @@
 LOCAL_CFLAGS += -Weverything -Werror -Wno-padded -Wno-undef -Wno-switch-enum
 LOCAL_CPPFLAGS := -std=c++1y \
 	-Wno-c++98-compat-pedantic \
-	-Wno-c99-extensions
+	-Wno-c99-extensions \
+	-Wno-old-style-cast
 
 LOCAL_C_INCLUDES := \
 	frameworks/native/vulkan/include
diff --git a/vulkan/tools/vkinfo.cpp b/vulkan/tools/vkinfo.cpp
index 3dc6653..e97e5f5 100644
--- a/vulkan/tools/vkinfo.cpp
+++ b/vulkan/tools/vkinfo.cpp
@@ -22,7 +22,6 @@
 #include <vector>
 
 #include <vulkan/vulkan.h>
-#include <vulkan/vk_ext_debug_report.h>
 
 #define LOG_TAG "vkinfo"
 #include <log/log.h>
@@ -32,6 +31,7 @@
 struct Options {
     bool layer_description;
     bool layer_extensions;
+    bool unsupported_features;
     bool validate;
 };
 
@@ -257,8 +257,17 @@
     // clang-format on
     uint32_t num_layers = sizeof(kValidationLayers) / sizeof(char*);
 
+    const VkApplicationInfo application_info = {
+        .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
+        .pApplicationName = "vkinfo",
+        .applicationVersion = 0,
+        .pEngineName = "vkinfo",
+        .engineVersion = 0,
+        .apiVersion = VK_API_VERSION,
+    };
     const VkInstanceCreateInfo create_info = {
         .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+        .pApplicationInfo = &application_info,
         .enabledExtensionCount = num_extensions,
         .ppEnabledExtensionNames = extensions,
         .enabledLayerCount = (options.validate) ? num_layers : 0,
@@ -304,16 +313,6 @@
            (kIndent.size() - (kIndentSize * std::min(n, kMaxIndent) + 1));
 }
 
-uint32_t ExtractMajorVersion(uint32_t version) {
-    return (version >> 22) & 0x3FF;
-}
-uint32_t ExtractMinorVersion(uint32_t version) {
-    return (version >> 12) & 0x3FF;
-}
-uint32_t ExtractPatchVersion(uint32_t version) {
-    return (version >> 0) & 0xFFF;
-}
-
 const char* VkPhysicalDeviceTypeStr(VkPhysicalDeviceType type) {
     switch (type) {
         case VK_PHYSICAL_DEVICE_TYPE_OTHER:
@@ -345,9 +344,9 @@
     size_t indent) {
     for (size_t i = 0; i < layers.size(); i++) {
         printf("%s%s %u.%u.%u/%u\n", Indent(indent), layers[i].layerName,
-               ExtractMajorVersion(layers[i].specVersion),
-               ExtractMinorVersion(layers[i].specVersion),
-               ExtractPatchVersion(layers[i].specVersion),
+               VK_VERSION_MAJOR(layers[i].specVersion),
+               VK_VERSION_MINOR(layers[i].specVersion),
+               VK_VERSION_PATCH(layers[i].specVersion),
                layers[i].implementationVersion);
         if (options.layer_description)
             printf("%s%s\n", Indent(indent + 1), layers[i].description);
@@ -361,6 +360,128 @@
     }
 }
 
+void PrintAllFeatures(const char* indent,
+                      const VkPhysicalDeviceFeatures& features) {
+    // clang-format off
+    printf("%srobustBufferAccess: %s\n", indent, features.robustBufferAccess ? "YES" : "NO");
+    printf("%sfullDrawIndexUint32: %s\n", indent, features.fullDrawIndexUint32 ? "YES" : "NO");
+    printf("%simageCubeArray: %s\n", indent, features.imageCubeArray ? "YES" : "NO");
+    printf("%sindependentBlend: %s\n", indent, features.independentBlend ? "YES" : "NO");
+    printf("%sgeometryShader: %s\n", indent, features.geometryShader ? "YES" : "NO");
+    printf("%stessellationShader: %s\n", indent, features.tessellationShader ? "YES" : "NO");
+    printf("%ssampleRateShading: %s\n", indent, features.sampleRateShading ? "YES" : "NO");
+    printf("%sdualSrcBlend: %s\n", indent, features.dualSrcBlend ? "YES" : "NO");
+    printf("%slogicOp: %s\n", indent, features.logicOp ? "YES" : "NO");
+    printf("%smultiDrawIndirect: %s\n", indent, features.multiDrawIndirect ? "YES" : "NO");
+    printf("%sdrawIndirectFirstInstance: %s\n", indent, features.drawIndirectFirstInstance ? "YES" : "NO");
+    printf("%sdepthClamp: %s\n", indent, features.depthClamp ? "YES" : "NO");
+    printf("%sdepthBiasClamp: %s\n", indent, features.depthBiasClamp ? "YES" : "NO");
+    printf("%sfillModeNonSolid: %s\n", indent, features.fillModeNonSolid ? "YES" : "NO");
+    printf("%sdepthBounds: %s\n", indent, features.depthBounds ? "YES" : "NO");
+    printf("%swideLines: %s\n", indent, features.wideLines ? "YES" : "NO");
+    printf("%slargePoints: %s\n", indent, features.largePoints ? "YES" : "NO");
+    printf("%salphaToOne: %s\n", indent, features.alphaToOne ? "YES" : "NO");
+    printf("%smultiViewport: %s\n", indent, features.multiViewport ? "YES" : "NO");
+    printf("%ssamplerAnisotropy: %s\n", indent, features.samplerAnisotropy ? "YES" : "NO");
+    printf("%stextureCompressionETC2: %s\n", indent, features.textureCompressionETC2 ? "YES" : "NO");
+    printf("%stextureCompressionASTC_LDR: %s\n", indent, features.textureCompressionASTC_LDR ? "YES" : "NO");
+    printf("%stextureCompressionBC: %s\n", indent, features.textureCompressionBC ? "YES" : "NO");
+    printf("%socclusionQueryPrecise: %s\n", indent, features.occlusionQueryPrecise ? "YES" : "NO");
+    printf("%spipelineStatisticsQuery: %s\n", indent, features.pipelineStatisticsQuery ? "YES" : "NO");
+    printf("%svertexPipelineStoresAndAtomics: %s\n", indent, features.vertexPipelineStoresAndAtomics ? "YES" : "NO");
+    printf("%sfragmentStoresAndAtomics: %s\n", indent, features.fragmentStoresAndAtomics ? "YES" : "NO");
+    printf("%sshaderTessellationAndGeometryPointSize: %s\n", indent, features.shaderTessellationAndGeometryPointSize ? "YES" : "NO");
+    printf("%sshaderImageGatherExtended: %s\n", indent, features.shaderImageGatherExtended ? "YES" : "NO");
+    printf("%sshaderStorageImageExtendedFormats: %s\n", indent, features.shaderStorageImageExtendedFormats ? "YES" : "NO");
+    printf("%sshaderStorageImageMultisample: %s\n", indent, features.shaderStorageImageMultisample ? "YES" : "NO");
+    printf("%sshaderStorageImageReadWithoutFormat: %s\n", indent, features.shaderStorageImageReadWithoutFormat ? "YES" : "NO");
+    printf("%sshaderStorageImageWriteWithoutFormat: %s\n", indent, features.shaderStorageImageWriteWithoutFormat ? "YES" : "NO");
+    printf("%sshaderUniformBufferArrayDynamicIndexing: %s\n", indent, features.shaderUniformBufferArrayDynamicIndexing ? "YES" : "NO");
+    printf("%sshaderSampledImageArrayDynamicIndexing: %s\n", indent, features.shaderSampledImageArrayDynamicIndexing ? "YES" : "NO");
+    printf("%sshaderStorageBufferArrayDynamicIndexing: %s\n", indent, features.shaderStorageBufferArrayDynamicIndexing ? "YES" : "NO");
+    printf("%sshaderStorageImageArrayDynamicIndexing: %s\n", indent, features.shaderStorageImageArrayDynamicIndexing ? "YES" : "NO");
+    printf("%sshaderClipDistance: %s\n", indent, features.shaderClipDistance ? "YES" : "NO");
+    printf("%sshaderCullDistance: %s\n", indent, features.shaderCullDistance ? "YES" : "NO");
+    printf("%sshaderFloat64: %s\n", indent, features.shaderFloat64 ? "YES" : "NO");
+    printf("%sshaderInt64: %s\n", indent, features.shaderInt64 ? "YES" : "NO");
+    printf("%sshaderInt16: %s\n", indent, features.shaderInt16 ? "YES" : "NO");
+    printf("%sshaderResourceResidency: %s\n", indent, features.shaderResourceResidency ? "YES" : "NO");
+    printf("%sshaderResourceMinLod: %s\n", indent, features.shaderResourceMinLod ? "YES" : "NO");
+    printf("%ssparseBinding: %s\n", indent, features.sparseBinding ? "YES" : "NO");
+    printf("%ssparseResidencyBuffer: %s\n", indent, features.sparseResidencyBuffer ? "YES" : "NO");
+    printf("%ssparseResidencyImage2D: %s\n", indent, features.sparseResidencyImage2D ? "YES" : "NO");
+    printf("%ssparseResidencyImage3D: %s\n", indent, features.sparseResidencyImage3D ? "YES" : "NO");
+    printf("%ssparseResidency2Samples: %s\n", indent, features.sparseResidency2Samples ? "YES" : "NO");
+    printf("%ssparseResidency4Samples: %s\n", indent, features.sparseResidency4Samples ? "YES" : "NO");
+    printf("%ssparseResidency8Samples: %s\n", indent, features.sparseResidency8Samples ? "YES" : "NO");
+    printf("%ssparseResidency16Samples: %s\n", indent, features.sparseResidency16Samples ? "YES" : "NO");
+    printf("%ssparseResidencyAliased: %s\n", indent, features.sparseResidencyAliased ? "YES" : "NO");
+    printf("%svariableMultisampleRate: %s\n", indent, features.variableMultisampleRate ? "YES" : "NO");
+    printf("%sinheritedQueries: %s\n", indent, features.inheritedQueries ? "YES" : "NO");
+    // clang-format on
+}
+
+void PrintSupportedFeatures(const char* indent,
+                            const VkPhysicalDeviceFeatures& features) {
+    // clang-format off
+    if (features.robustBufferAccess) printf("%srobustBufferAccess\n", indent);
+    if (features.fullDrawIndexUint32) printf("%sfullDrawIndexUint32\n", indent);
+    if (features.imageCubeArray) printf("%simageCubeArray\n", indent);
+    if (features.independentBlend) printf("%sindependentBlend\n", indent);
+    if (features.geometryShader) printf("%sgeometryShader\n", indent);
+    if (features.tessellationShader) printf("%stessellationShader\n", indent);
+    if (features.sampleRateShading) printf("%ssampleRateShading\n", indent);
+    if (features.dualSrcBlend) printf("%sdualSrcBlend\n", indent);
+    if (features.logicOp) printf("%slogicOp\n", indent);
+    if (features.multiDrawIndirect) printf("%smultiDrawIndirect\n", indent);
+    if (features.drawIndirectFirstInstance) printf("%sdrawIndirectFirstInstance\n", indent);
+    if (features.depthClamp) printf("%sdepthClamp\n", indent);
+    if (features.depthBiasClamp) printf("%sdepthBiasClamp\n", indent);
+    if (features.fillModeNonSolid) printf("%sfillModeNonSolid\n", indent);
+    if (features.depthBounds) printf("%sdepthBounds\n", indent);
+    if (features.wideLines) printf("%swideLines\n", indent);
+    if (features.largePoints) printf("%slargePoints\n", indent);
+    if (features.alphaToOne) printf("%salphaToOne\n", indent);
+    if (features.multiViewport) printf("%smultiViewport\n", indent);
+    if (features.samplerAnisotropy) printf("%ssamplerAnisotropy\n", indent);
+    if (features.textureCompressionETC2) printf("%stextureCompressionETC2\n", indent);
+    if (features.textureCompressionASTC_LDR) printf("%stextureCompressionASTC_LDR\n", indent);
+    if (features.textureCompressionBC) printf("%stextureCompressionBC\n", indent);
+    if (features.occlusionQueryPrecise) printf("%socclusionQueryPrecise\n", indent);
+    if (features.pipelineStatisticsQuery) printf("%spipelineStatisticsQuery\n", indent);
+    if (features.vertexPipelineStoresAndAtomics) printf("%svertexPipelineStoresAndAtomics\n", indent);
+    if (features.fragmentStoresAndAtomics) printf("%sfragmentStoresAndAtomics\n", indent);
+    if (features.shaderTessellationAndGeometryPointSize) printf("%sshaderTessellationAndGeometryPointSize\n", indent);
+    if (features.shaderImageGatherExtended) printf("%sshaderImageGatherExtended\n", indent);
+    if (features.shaderStorageImageExtendedFormats) printf("%sshaderStorageImageExtendedFormats\n", indent);
+    if (features.shaderStorageImageMultisample) printf("%sshaderStorageImageMultisample\n", indent);
+    if (features.shaderStorageImageReadWithoutFormat) printf("%sshaderStorageImageReadWithoutFormat\n", indent);
+    if (features.shaderStorageImageWriteWithoutFormat) printf("%sshaderStorageImageWriteWithoutFormat\n", indent);
+    if (features.shaderUniformBufferArrayDynamicIndexing) printf("%sshaderUniformBufferArrayDynamicIndexing\n", indent);
+    if (features.shaderSampledImageArrayDynamicIndexing) printf("%sshaderSampledImageArrayDynamicIndexing\n", indent);
+    if (features.shaderStorageBufferArrayDynamicIndexing) printf("%sshaderStorageBufferArrayDynamicIndexing\n", indent);
+    if (features.shaderStorageImageArrayDynamicIndexing) printf("%sshaderStorageImageArrayDynamicIndexing\n", indent);
+    if (features.shaderClipDistance) printf("%sshaderClipDistance\n", indent);
+    if (features.shaderCullDistance) printf("%sshaderCullDistance\n", indent);
+    if (features.shaderFloat64) printf("%sshaderFloat64\n", indent);
+    if (features.shaderInt64) printf("%sshaderInt64\n", indent);
+    if (features.shaderInt16) printf("%sshaderInt16\n", indent);
+    if (features.shaderResourceResidency) printf("%sshaderResourceResidency\n", indent);
+    if (features.shaderResourceMinLod) printf("%sshaderResourceMinLod\n", indent);
+    if (features.sparseBinding) printf("%ssparseBinding\n", indent);
+    if (features.sparseResidencyBuffer) printf("%ssparseResidencyBuffer\n", indent);
+    if (features.sparseResidencyImage2D) printf("%ssparseResidencyImage2D\n", indent);
+    if (features.sparseResidencyImage3D) printf("%ssparseResidencyImage3D\n", indent);
+    if (features.sparseResidency2Samples) printf("%ssparseResidency2Samples\n", indent);
+    if (features.sparseResidency4Samples) printf("%ssparseResidency4Samples\n", indent);
+    if (features.sparseResidency8Samples) printf("%ssparseResidency8Samples\n", indent);
+    if (features.sparseResidency16Samples) printf("%ssparseResidency16Samples\n", indent);
+    if (features.sparseResidencyAliased) printf("%ssparseResidencyAliased\n", indent);
+    if (features.variableMultisampleRate) printf("%svariableMultisampleRate\n", indent);
+    if (features.inheritedQueries) printf("%sinheritedQueries\n", indent);
+    // clang-format on
+}
+
 void PrintGpuInfo(const GpuInfo& info, const Options& options, size_t indent) {
     VkResult result;
     std::ostringstream strbuf;
@@ -368,9 +489,9 @@
     printf("%s\"%s\" (%s) %u.%u.%u/%#x [%04x:%04x]\n", Indent(indent),
            info.properties.deviceName,
            VkPhysicalDeviceTypeStr(info.properties.deviceType),
-           ExtractMajorVersion(info.properties.apiVersion),
-           ExtractMinorVersion(info.properties.apiVersion),
-           ExtractPatchVersion(info.properties.apiVersion),
+           VK_VERSION_MAJOR(info.properties.apiVersion),
+           VK_VERSION_MINOR(info.properties.apiVersion),
+           VK_VERSION_PATCH(info.properties.apiVersion),
            info.properties.driverVersion, info.properties.vendorID,
            info.properties.deviceID);
 
@@ -425,64 +546,12 @@
             qprops.minImageTransferGranularity.depth);
     }
 
-    // clang-format off
     printf("%sFeatures:\n", Indent(indent + 1));
-    printf("%srobustBufferAccess: %s\n", Indent(indent + 2), info.features.robustBufferAccess ? "YES" : "NO");
-    printf("%sfullDrawIndexUint32: %s\n", Indent(indent + 2), info.features.fullDrawIndexUint32 ? "YES" : "NO");
-    printf("%simageCubeArray: %s\n", Indent(indent + 2), info.features.imageCubeArray ? "YES" : "NO");
-    printf("%sindependentBlend: %s\n", Indent(indent + 2), info.features.independentBlend ? "YES" : "NO");
-    printf("%sgeometryShader: %s\n", Indent(indent + 2), info.features.geometryShader ? "YES" : "NO");
-    printf("%stessellationShader: %s\n", Indent(indent + 2), info.features.tessellationShader ? "YES" : "NO");
-    printf("%ssampleRateShading: %s\n", Indent(indent + 2), info.features.sampleRateShading ? "YES" : "NO");
-    printf("%sdualSrcBlend: %s\n", Indent(indent + 2), info.features.dualSrcBlend ? "YES" : "NO");
-    printf("%slogicOp: %s\n", Indent(indent + 2), info.features.logicOp ? "YES" : "NO");
-    printf("%smultiDrawIndirect: %s\n", Indent(indent + 2), info.features.multiDrawIndirect ? "YES" : "NO");
-    printf("%sdrawIndirectFirstInstance: %s\n", Indent(indent + 2), info.features.drawIndirectFirstInstance ? "YES" : "NO");
-    printf("%sdepthClamp: %s\n", Indent(indent + 2), info.features.depthClamp ? "YES" : "NO");
-    printf("%sdepthBiasClamp: %s\n", Indent(indent + 2), info.features.depthBiasClamp ? "YES" : "NO");
-    printf("%sfillModeNonSolid: %s\n", Indent(indent + 2), info.features.fillModeNonSolid ? "YES" : "NO");
-    printf("%sdepthBounds: %s\n", Indent(indent + 2), info.features.depthBounds ? "YES" : "NO");
-    printf("%swideLines: %s\n", Indent(indent + 2), info.features.wideLines ? "YES" : "NO");
-    printf("%slargePoints: %s\n", Indent(indent + 2), info.features.largePoints ? "YES" : "NO");
-    printf("%salphaToOne: %s\n", Indent(indent + 2), info.features.alphaToOne ? "YES" : "NO");
-    printf("%smultiViewport: %s\n", Indent(indent + 2), info.features.multiViewport ? "YES" : "NO");
-    printf("%ssamplerAnisotropy: %s\n", Indent(indent + 2), info.features.samplerAnisotropy ? "YES" : "NO");
-    printf("%stextureCompressionETC2: %s\n", Indent(indent + 2), info.features.textureCompressionETC2 ? "YES" : "NO");
-    printf("%stextureCompressionASTC_LDR: %s\n", Indent(indent + 2), info.features.textureCompressionASTC_LDR ? "YES" : "NO");
-    printf("%stextureCompressionBC: %s\n", Indent(indent + 2), info.features.textureCompressionBC ? "YES" : "NO");
-    printf("%socclusionQueryPrecise: %s\n", Indent(indent + 2), info.features.occlusionQueryPrecise ? "YES" : "NO");
-    printf("%spipelineStatisticsQuery: %s\n", Indent(indent + 2), info.features.pipelineStatisticsQuery ? "YES" : "NO");
-    printf("%svertexPipelineStoresAndAtomics: %s\n", Indent(indent + 2), info.features.vertexPipelineStoresAndAtomics ? "YES" : "NO");
-    printf("%sfragmentStoresAndAtomics: %s\n", Indent(indent + 2), info.features.fragmentStoresAndAtomics ? "YES" : "NO");
-    printf("%sshaderTessellationAndGeometryPointSize: %s\n", Indent(indent + 2), info.features.shaderTessellationAndGeometryPointSize ? "YES" : "NO");
-    printf("%sshaderImageGatherExtended: %s\n", Indent(indent + 2), info.features.shaderImageGatherExtended ? "YES" : "NO");
-    printf("%sshaderStorageImageExtendedFormats: %s\n", Indent(indent + 2), info.features.shaderStorageImageExtendedFormats ? "YES" : "NO");
-    printf("%sshaderStorageImageMultisample: %s\n", Indent(indent + 2), info.features.shaderStorageImageMultisample ? "YES" : "NO");
-    printf("%sshaderStorageImageReadWithoutFormat: %s\n", Indent(indent + 2), info.features.shaderStorageImageReadWithoutFormat ? "YES" : "NO");
-    printf("%sshaderStorageImageWriteWithoutFormat: %s\n", Indent(indent + 2), info.features.shaderStorageImageWriteWithoutFormat ? "YES" : "NO");
-    printf("%sshaderUniformBufferArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderUniformBufferArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderSampledImageArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderSampledImageArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderStorageBufferArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderStorageBufferArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderStorageImageArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderStorageImageArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderClipDistance: %s\n", Indent(indent + 2), info.features.shaderClipDistance ? "YES" : "NO");
-    printf("%sshaderCullDistance: %s\n", Indent(indent + 2), info.features.shaderCullDistance ? "YES" : "NO");
-    printf("%sshaderFloat64: %s\n", Indent(indent + 2), info.features.shaderFloat64 ? "YES" : "NO");
-    printf("%sshaderInt64: %s\n", Indent(indent + 2), info.features.shaderInt64 ? "YES" : "NO");
-    printf("%sshaderInt16: %s\n", Indent(indent + 2), info.features.shaderInt16 ? "YES" : "NO");
-    printf("%sshaderResourceResidency: %s\n", Indent(indent + 2), info.features.shaderResourceResidency ? "YES" : "NO");
-    printf("%sshaderResourceMinLod: %s\n", Indent(indent + 2), info.features.shaderResourceMinLod ? "YES" : "NO");
-    printf("%ssparseBinding: %s\n", Indent(indent + 2), info.features.sparseBinding ? "YES" : "NO");
-    printf("%ssparseResidencyBuffer: %s\n", Indent(indent + 2), info.features.sparseResidencyBuffer ? "YES" : "NO");
-    printf("%ssparseResidencyImage2D: %s\n", Indent(indent + 2), info.features.sparseResidencyImage2D ? "YES" : "NO");
-    printf("%ssparseResidencyImage3D: %s\n", Indent(indent + 2), info.features.sparseResidencyImage3D ? "YES" : "NO");
-    printf("%ssparseResidency2Samples: %s\n", Indent(indent + 2), info.features.sparseResidency2Samples ? "YES" : "NO");
-    printf("%ssparseResidency4Samples: %s\n", Indent(indent + 2), info.features.sparseResidency4Samples ? "YES" : "NO");
-    printf("%ssparseResidency8Samples: %s\n", Indent(indent + 2), info.features.sparseResidency8Samples ? "YES" : "NO");
-    printf("%ssparseResidency16Samples: %s\n", Indent(indent + 2), info.features.sparseResidency16Samples ? "YES" : "NO");
-    printf("%ssparseResidencyAliased: %s\n", Indent(indent + 2), info.features.sparseResidencyAliased ? "YES" : "NO");
-    printf("%svariableMultisampleRate: %s\n", Indent(indent + 2), info.features.variableMultisampleRate ? "YES" : "NO");
-    printf("%sinheritedQueries: %s\n", Indent(indent + 2), info.features.inheritedQueries ? "YES" : "NO");
-    // clang-format on
+    if (options.unsupported_features) {
+        PrintAllFeatures(Indent(indent + 2), info.features);
+    } else {
+        PrintSupportedFeatures(Indent(indent + 2), info.features);
+    }
 
     printf("%sExtensions [%zu]:\n", Indent(indent + 1), info.extensions.size());
     if (!info.extensions.empty())
@@ -508,6 +577,15 @@
         PrintGpuInfo(gpu, options, indent + 1);
 }
 
+const char kUsageString[] =
+    "usage: vkinfo [options]\n"
+    "  -v                       enable all the following verbose options\n"
+    "    -layer_description     print layer description strings\n"
+    "    -layer_extensions      print extensions supported by each layer\n"
+    "    -unsupported_features  print all physical device features\n"
+    "  -validate                enable validation layers if present\n"
+    "  -debug_pause             pause at start until resumed via debugger\n";
+
 }  // namespace
 
 // ----------------------------------------------------------------------------
@@ -516,16 +594,24 @@
     static volatile bool startup_pause = false;
     Options options = {
         .layer_description = false, .layer_extensions = false,
+        .unsupported_features = false,
         .validate = false,
     };
     for (int argi = 1; argi < argc; argi++) {
+        if (strcmp(argv[argi], "-h") == 0) {
+            fputs(kUsageString, stdout);
+            return 0;
+        }
         if (strcmp(argv[argi], "-v") == 0) {
             options.layer_description = true;
             options.layer_extensions = true;
+            options.unsupported_features = true;
         } else if (strcmp(argv[argi], "-layer_description") == 0) {
             options.layer_description = true;
         } else if (strcmp(argv[argi], "-layer_extensions") == 0) {
             options.layer_extensions = true;
+        } else if (strcmp(argv[argi], "-unsupported_features") == 0) {
+            options.unsupported_features = true;
         } else if (strcmp(argv[argi], "-validate") == 0) {
             options.validate = true;
         } else if (strcmp(argv[argi], "-debug_pause") == 0) {