Merge "Add libprocessgroup dependency and fix sched_policy include"
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 7dcc4a1..99f3739 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -171,7 +171,7 @@
     chmod 0666 /sys/kernel/tracing/per_cpu/cpu5/trace
     chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu6/trace
     chmod 0666 /sys/kernel/tracing/per_cpu/cpu6/trace
-    chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu6/trace
+    chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu7/trace
     chmod 0666 /sys/kernel/tracing/per_cpu/cpu7/trace
     chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu8/trace
     chmod 0666 /sys/kernel/tracing/per_cpu/cpu8/trace
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index cfd2b40..214559d 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -774,7 +774,8 @@
     AKEYCODE_THUMBS_UP = 286,
     /** Thumbs down key. Apps can use this to let user downvote content. */
     AKEYCODE_THUMBS_DOWN = 287,
-    /** Consumed by system to switch current viewer profile. */
+    /** Used to switch current account that is consuming content.
+     * May be consumed by system to switch current viewer profile. */
     AKEYCODE_PROFILE_SWITCH = 288
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 7357ba9..ad2dc14 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -66,7 +66,8 @@
     virtual void setTransactionState(const Vector<ComposerState>& state,
                                      const Vector<DisplayState>& displays, uint32_t flags,
                                      const sp<IBinder>& applyToken,
-                                     const InputWindowCommands& commands) {
+                                     const InputWindowCommands& commands,
+                                     int64_t desiredPresentTime) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
 
@@ -83,6 +84,7 @@
         data.writeUint32(flags);
         data.writeStrongBinder(applyToken);
         commands.write(data);
+        data.writeInt64(desiredPresentTime);
         remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
     }
 
@@ -693,6 +695,42 @@
         }
         return err;
     }
+
+    virtual status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
+                                 int32_t* outBufferId) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+
+        data.writeStrongBinder(token);
+        if (buffer) {
+            data.writeBool(true);
+            data.write(*buffer);
+        } else {
+            data.writeBool(false);
+        }
+
+        status_t result = remote()->transact(BnSurfaceComposer::CACHE_BUFFER, data, &reply);
+        if (result != NO_ERROR) {
+            return result;
+        }
+
+        int32_t id = -1;
+        result = reply.readInt32(&id);
+        if (result == NO_ERROR) {
+            *outBufferId = id;
+        }
+        return result;
+    }
+
+    virtual status_t uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+
+        data.writeStrongBinder(token);
+        data.writeInt32(bufferId);
+
+        return remote()->transact(BnSurfaceComposer::UNCACHE_BUFFER, data, &reply);
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -748,7 +786,10 @@
             sp<IBinder> applyToken = data.readStrongBinder();
             InputWindowCommands inputWindowCommands;
             inputWindowCommands.read(data);
-            setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands);
+
+            int64_t desiredPresentTime = data.readInt64();
+            setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
+                                desiredPresentTime);
             return NO_ERROR;
         }
         case BOOT_FINISHED: {
@@ -1131,6 +1172,48 @@
             }
             return error;
         }
+        case CACHE_BUFFER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> token;
+            status_t result = data.readStrongBinder(&token);
+            if (result != NO_ERROR) {
+                ALOGE("cache buffer failure in reading token: %d", result);
+                return result;
+            }
+
+            sp<GraphicBuffer> buffer = new GraphicBuffer();
+            if (data.readBool()) {
+                result = data.read(*buffer);
+                if (result != NO_ERROR) {
+                    ALOGE("cache buffer failure in reading buffer: %d", result);
+                    return result;
+                }
+            }
+            int32_t bufferId = -1;
+            status_t error = cacheBuffer(token, buffer, &bufferId);
+            if (error == NO_ERROR) {
+                reply->writeInt32(bufferId);
+            }
+            return error;
+        }
+        case UNCACHE_BUFFER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> token;
+            status_t result = data.readStrongBinder(&token);
+            if (result != NO_ERROR) {
+                ALOGE("uncache buffer failure in reading token: %d", result);
+                return result;
+            }
+
+            int32_t bufferId = -1;
+            result = data.readInt32(&bufferId);
+            if (result != NO_ERROR) {
+                ALOGE("uncache buffer failure in reading buffer id: %d", result);
+                return result;
+            }
+
+            return uncacheBuffer(token, bufferId);
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index e5170ab..40b55fa 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -94,6 +94,9 @@
         }
     }
 
+    output.writeStrongBinder(cachedBuffer.token);
+    output.writeInt32(cachedBuffer.bufferId);
+
     return NO_ERROR;
 }
 
@@ -164,6 +167,9 @@
         listenerCallbacks.emplace_back(listener, callbackIds);
     }
 
+    cachedBuffer.token = input.readStrongBinder();
+    cachedBuffer.bufferId = input.readInt32();
+
     return NO_ERROR;
 }
 
@@ -372,6 +378,10 @@
     }
 #endif
 
+    if (other.what & eCachedBufferChanged) {
+        what |= eCachedBufferChanged;
+        cachedBuffer = other.cachedBuffer;
+    }
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
               "other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index e3ee511..a82054b 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -159,11 +159,12 @@
 
 // ---------------------------------------------------------------------------
 
-SurfaceComposerClient::Transaction::Transaction(const Transaction& other) :
-    mForceSynchronous(other.mForceSynchronous),
-    mTransactionNestCount(other.mTransactionNestCount),
-    mAnimation(other.mAnimation),
-    mEarlyWakeup(other.mEarlyWakeup) {
+SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
+      : mForceSynchronous(other.mForceSynchronous),
+        mTransactionNestCount(other.mTransactionNestCount),
+        mAnimation(other.mAnimation),
+        mEarlyWakeup(other.mEarlyWakeup),
+        mDesiredPresentTime(other.mDesiredPresentTime) {
     mDisplayStates = other.mDisplayStates;
     mComposerStates = other.mComposerStates;
     mInputWindowCommands = other.mInputWindowCommands;
@@ -218,7 +219,7 @@
     s.state.parentHandleForChild = nullptr;
 
     composerStates.add(s);
-    sf->setTransactionState(composerStates, displayStates, 0, nullptr, {});
+    sf->setTransactionState(composerStates, displayStates, 0, nullptr, {}, -1);
 }
 
 status_t SurfaceComposerClient::Transaction::apply(bool synchronous) {
@@ -284,7 +285,8 @@
     mEarlyWakeup = false;
 
     sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands);
+    sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
+                            mDesiredPresentTime);
     mInputWindowCommands.clear();
     mStatus = NO_ERROR;
     return NO_ERROR;
@@ -658,6 +660,21 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCachedBuffer(
+        const sp<SurfaceControl>& sc, int32_t bufferId) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eCachedBufferChanged;
+    s->cachedBuffer.token = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    s->cachedBuffer.bufferId = bufferId;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence(
         const sp<SurfaceControl>& sc, const sp<Fence>& fence) {
     layer_state_t* s = getLayerState(sc);
@@ -742,6 +759,12 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime(
+        nsecs_t desiredPresentTime) {
+    mDesiredPresentTime = desiredPresentTime;
+    return *this;
+}
+
 SurfaceComposerClient::Transaction&
 SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
         TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
@@ -1049,6 +1072,26 @@
 
 // ----------------------------------------------------------------------------
 
+status_t SurfaceComposerClient::cacheBuffer(const sp<GraphicBuffer>& buffer, int32_t* outBufferId) {
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    if (buffer == nullptr || outBufferId == nullptr) {
+        return BAD_VALUE;
+    }
+    return sf->cacheBuffer(IInterface::asBinder(TransactionCompletedListener::getIInstance()),
+                           buffer, outBufferId);
+}
+
+status_t SurfaceComposerClient::uncacheBuffer(int32_t bufferId) {
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    if (bufferId < 0) {
+        return BAD_VALUE;
+    }
+    return sf->uncacheBuffer(IInterface::asBinder(TransactionCompletedListener::getIInstance()),
+                             bufferId);
+}
+
+// ----------------------------------------------------------------------------
+
 status_t SurfaceComposerClient::enableVSyncInjections(bool enable) {
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
     return sf->enableVSyncInjections(enable);
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 9812e1c..418d5fb 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -116,7 +116,8 @@
     virtual void setTransactionState(const Vector<ComposerState>& state,
                                      const Vector<DisplayState>& displays, uint32_t flags,
                                      const sp<IBinder>& applyToken,
-                                     const InputWindowCommands& inputWindowCommands) = 0;
+                                     const InputWindowCommands& inputWindowCommands,
+                                     int64_t desiredPresentTime) = 0;
 
     /* signal that we're done booting.
      * Requires ACCESS_SURFACE_FLINGER permission
@@ -315,6 +316,11 @@
      * Requires the ACCESS_SURFACE_FLINGER permission.
      */
     virtual status_t getProtectedContentSupport(bool* outSupported) const = 0;
+
+    virtual status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
+                                 int32_t* outBufferId) = 0;
+
+    virtual status_t uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -357,6 +363,8 @@
         SET_DISPLAY_CONTENT_SAMPLING_ENABLED,
         GET_DISPLAYED_CONTENT_SAMPLE,
         GET_PROTECTED_CONTENT_SUPPORT,
+        CACHE_BUFFER,
+        UNCACHE_BUFFER,
     };
 
     virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index e7564f5..af31420 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -84,6 +84,7 @@
         eInputInfoChanged = 0x40000000,
         eCornerRadiusChanged = 0x80000000,
         eFrameChanged = 0x1'00000000,
+        eCachedBufferChanged = 0x2'00000000,
     };
 
     layer_state_t()
@@ -125,6 +126,10 @@
         float dtdy{0};
         float dsdy{0};
     };
+    struct cached_buffer_t {
+        sp<IBinder> token = nullptr;
+        int32_t bufferId = -1;
+    };
     sp<IBinder> surface;
     uint64_t what;
     float x;
@@ -173,6 +178,8 @@
 #ifndef NO_INPUT
     InputWindowInfo inputInfo;
 #endif
+
+    cached_buffer_t cachedBuffer;
 };
 
 struct ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 9a4f7a0..db315c2 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -158,6 +158,12 @@
     static void doDropReferenceTransaction(const sp<IBinder>& handle,
             const sp<ISurfaceComposerClient>& client);
 
+    // Caches a buffer with the ISurfaceComposer so the buffer does not need to be resent across
+    // processes
+    static status_t cacheBuffer(const sp<GraphicBuffer>& buffer, int32_t* outBufferId);
+    // Uncaches a buffer set by cacheBuffer
+    static status_t uncacheBuffer(int32_t bufferId);
+
     // ------------------------------------------------------------------------
     // surface creation / destruction
 
@@ -243,6 +249,18 @@
         bool                        mAnimation = false;
         bool                        mEarlyWakeup = false;
 
+        // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
+        // to be presented. When it is not possible to present at exactly that time, it will be
+        // presented after the time has passed.
+        //
+        // Desired present times that are more than 1 second in the future may be ignored.
+        // When a desired present time has already passed, the transaction will be presented as soon
+        // as possible.
+        //
+        // Transactions from the same process are presented in the same order that they are applied.
+        // The desired present time does not affect this ordering.
+        int64_t mDesiredPresentTime = -1;
+
         InputWindowCommands mInputWindowCommands;
         int mStatus = NO_ERROR;
 
@@ -323,6 +341,7 @@
         Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
         Transaction& setFrame(const sp<SurfaceControl>& sc, const Rect& frame);
         Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer);
+        Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId);
         Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence);
         Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
         Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
@@ -331,6 +350,7 @@
         Transaction& setApi(const sp<SurfaceControl>& sc, int32_t api);
         Transaction& setSidebandStream(const sp<SurfaceControl>& sc,
                                        const sp<NativeHandle>& sidebandStream);
+        Transaction& setDesiredPresentTime(nsecs_t desiredPresentTime);
 
         Transaction& addTransactionCompletedCallback(
                 TransactionCompletedCallbackTakesContext callback, void* callbackContext);
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 7fc69ff..ac97733 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -489,5 +489,13 @@
     toSurface->expectMotionEvent(AMOTION_EVENT_ACTION_UP, 1, 1);
     fromSurface->expectNoMotionEvent(AMOTION_EVENT_ACTION_UP);
 }
+
+TEST_F(InputSurfacesTest, input_respects_outscreen) {
+    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+    surface->showAt(-1, -1);
+
+    injectTap(0, 0);
+    surface->expectTap(1, 1);
+}
 }
 }
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 2d773f2..259ef9f 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -556,7 +556,9 @@
     void setTransactionState(const Vector<ComposerState>& /*state*/,
                              const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
                              const sp<IBinder>& /*applyToken*/,
-                             const InputWindowCommands& /*inputWindowCommands*/) override {}
+                             const InputWindowCommands& /*inputWindowCommands*/,
+                             int64_t /*desiredPresentTime*/) override {}
+
     void bootFinished() override {}
     bool authenticateSurfaceTexture(
             const sp<IGraphicBufferProducer>& /*surface*/) const override {
@@ -658,6 +660,12 @@
     status_t getColorManagement(bool* /*outGetColorManagement*/) const override { return NO_ERROR; }
     status_t getProtectedContentSupport(bool* /*outSupported*/) const override { return NO_ERROR; }
 
+    status_t cacheBuffer(const sp<IBinder>& /*token*/, const sp<GraphicBuffer>& /*buffer*/,
+                         int32_t* /*outBufferId*/) {
+        return NO_ERROR;
+    }
+    status_t uncacheBuffer(const sp<IBinder>& /*token*/, int32_t /*bufferId*/) { return NO_ERROR; }
+
 protected:
     IBinder* onAsBinder() override { return nullptr; }
 
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 2915bb8..f57bf9c 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -1280,7 +1280,7 @@
             std::lock_guard<std::mutex> lock(mMutex);
 
             mCondition.wait(mMutex,
-                            [&]() REQUIRES(mMutex) { return !mQueue.empty() && !mRunning; });
+                            [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; });
 
             if (!mRunning) {
                 // if mRunning is false, then FlushTracer is being destroyed, so
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index d089bf6..74450d7 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -60,7 +60,9 @@
         "Fence.cpp",
         "FenceTime.cpp",
         "FrameStats.cpp",
+        "Gralloc.cpp",
         "Gralloc2.cpp",
+        "Gralloc3.cpp",
         "GraphicBuffer.cpp",
         "GraphicBufferAllocator.cpp",
         "GraphicBufferMapper.cpp",
@@ -68,6 +70,7 @@
         "PixelFormat.cpp",
         "Rect.cpp",
         "Region.cpp",
+        "Size.cpp",
         "Transform.cpp",
         "UiConfig.cpp",
     ],
@@ -79,9 +82,11 @@
     shared_libs: [
         "android.frameworks.bufferhub@1.0",
         "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@2.1",
+        "android.hardware.graphics.mapper@3.0",
         "android.hardware.configstore@1.0",
         "android.hardware.configstore-utils",
         "libbase",
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/ui/Gralloc.cpp
similarity index 67%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/ui/Gralloc.cpp
index e6ac6bf..8e09a13 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/ui/Gralloc.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2016 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.
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#define LOG_TAG "Gralloc"
+
+#include <ui/Gralloc.h>
 
 namespace android {
-namespace mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+GrallocMapper::~GrallocMapper() {}
 
-} // namespace mock
+GrallocAllocator::~GrallocAllocator() {}
+
 } // namespace android
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index 20f27c5..ea7321e 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -27,9 +27,15 @@
 #include <sync/sync.h>
 #pragma clang diagnostic pop
 
-namespace android {
+using android::hardware::graphics::allocator::V2_0::IAllocator;
+using android::hardware::graphics::common::V1_1::BufferUsage;
+using android::hardware::graphics::common::V1_1::PixelFormat;
+using android::hardware::graphics::mapper::V2_0::BufferDescriptor;
+using android::hardware::graphics::mapper::V2_0::Error;
+using android::hardware::graphics::mapper::V2_0::YCbCrLayout;
+using android::hardware::graphics::mapper::V2_1::IMapper;
 
-namespace Gralloc2 {
+namespace android {
 
 namespace {
 
@@ -59,17 +65,26 @@
     return valid11UsageBits;
 }
 
+static inline IMapper::Rect sGralloc2Rect(const Rect& rect) {
+    IMapper::Rect outRect{};
+    outRect.left = rect.left;
+    outRect.top = rect.top;
+    outRect.width = rect.width();
+    outRect.height = rect.height();
+    return outRect;
+}
+
 }  // anonymous namespace
 
-void Mapper::preload() {
+void Gralloc2Mapper::preload() {
     android::hardware::preloadPassthroughService<hardware::graphics::mapper::V2_0::IMapper>();
 }
 
-Mapper::Mapper()
-{
+Gralloc2Mapper::Gralloc2Mapper() {
     mMapper = hardware::graphics::mapper::V2_0::IMapper::getService();
     if (mMapper == nullptr) {
-        LOG_ALWAYS_FATAL("gralloc-mapper is missing");
+        ALOGW("mapper 2.x is not supported");
+        return;
     }
     if (mMapper->isRemote()) {
         LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
@@ -79,30 +94,37 @@
     mMapperV2_1 = IMapper::castFrom(mMapper);
 }
 
-Gralloc2::Error Mapper::validateBufferDescriptorInfo(
-        const IMapper::BufferDescriptorInfo& descriptorInfo) const {
+bool Gralloc2Mapper::isSupported() const {
+    return mMapper != nullptr;
+}
+
+status_t Gralloc2Mapper::validateBufferDescriptorInfo(
+        IMapper::BufferDescriptorInfo* descriptorInfo) const {
     uint64_t validUsageBits = getValid10UsageBits();
     if (mMapperV2_1 != nullptr) {
         validUsageBits = validUsageBits | getValid11UsageBits();
     }
 
-    if (descriptorInfo.usage & ~validUsageBits) {
+    if (descriptorInfo->usage & ~validUsageBits) {
         ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64,
-              descriptorInfo.usage & ~validUsageBits);
-        return Error::BAD_VALUE;
+              descriptorInfo->usage & ~validUsageBits);
+        return BAD_VALUE;
     }
-    return Error::NONE;
+    return NO_ERROR;
 }
 
-Error Mapper::createDescriptor(
-        const IMapper::BufferDescriptorInfo& descriptorInfo,
-        BufferDescriptor* outDescriptor) const
-{
-    Error error = validateBufferDescriptorInfo(descriptorInfo);
-    if (error != Error::NONE) {
-        return error;
+status_t Gralloc2Mapper::createDescriptor(void* bufferDescriptorInfo,
+                                          void* outBufferDescriptor) const {
+    IMapper::BufferDescriptorInfo* descriptorInfo =
+            static_cast<IMapper::BufferDescriptorInfo*>(bufferDescriptorInfo);
+    BufferDescriptor* outDescriptor = static_cast<BufferDescriptor*>(outBufferDescriptor);
+
+    status_t status = validateBufferDescriptorInfo(descriptorInfo);
+    if (status != NO_ERROR) {
+        return status;
     }
 
+    Error error;
     auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor)
                    {
                        error = tmpError;
@@ -115,24 +137,23 @@
 
     hardware::Return<void> ret;
     if (mMapperV2_1 != nullptr) {
-        ret = mMapperV2_1->createDescriptor_2_1(descriptorInfo, hidl_cb);
+        ret = mMapperV2_1->createDescriptor_2_1(*descriptorInfo, hidl_cb);
     } else {
         const hardware::graphics::mapper::V2_0::IMapper::BufferDescriptorInfo info = {
-            descriptorInfo.width,
-            descriptorInfo.height,
-            descriptorInfo.layerCount,
-            static_cast<hardware::graphics::common::V1_0::PixelFormat>(descriptorInfo.format),
-            descriptorInfo.usage,
+                descriptorInfo->width,
+                descriptorInfo->height,
+                descriptorInfo->layerCount,
+                static_cast<hardware::graphics::common::V1_0::PixelFormat>(descriptorInfo->format),
+                descriptorInfo->usage,
         };
         ret = mMapper->createDescriptor(info, hidl_cb);
     }
 
-    return (ret.isOk()) ? error : kTransactionError;
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
 }
 
-Error Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
-        buffer_handle_t* outBufferHandle) const
-{
+status_t Gralloc2Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
+                                      buffer_handle_t* outBufferHandle) const {
     Error error;
     auto ret = mMapper->importBuffer(rawHandle,
             [&](const auto& tmpError, const auto& tmpBuffer)
@@ -145,11 +166,10 @@
                 *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer);
             });
 
-    return (ret.isOk()) ? error : kTransactionError;
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
 }
 
-void Mapper::freeBuffer(buffer_handle_t bufferHandle) const
-{
+void Gralloc2Mapper::freeBuffer(buffer_handle_t bufferHandle) const {
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
     auto ret = mMapper->freeBuffer(buffer);
 
@@ -158,23 +178,29 @@
             buffer, error);
 }
 
-Error Mapper::validateBufferSize(buffer_handle_t bufferHandle,
-        const IMapper::BufferDescriptorInfo& descriptorInfo,
-        uint32_t stride) const
-{
+status_t Gralloc2Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+                                            uint32_t height, android::PixelFormat format,
+                                            uint32_t layerCount, uint64_t usage,
+                                            uint32_t stride) const {
     if (mMapperV2_1 == nullptr) {
-        return Error::NONE;
+        return NO_ERROR;
     }
 
+    IMapper::BufferDescriptorInfo descriptorInfo = {};
+    descriptorInfo.width = width;
+    descriptorInfo.height = height;
+    descriptorInfo.layerCount = layerCount;
+    descriptorInfo.format = static_cast<hardware::graphics::common::V1_1::PixelFormat>(format);
+    descriptorInfo.usage = usage;
+
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
     auto ret = mMapperV2_1->validateBufferSize(buffer, descriptorInfo, stride);
 
-    return (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError;
+    return static_cast<status_t>((ret.isOk()) ? static_cast<Error>(ret) : kTransactionError);
 }
 
-void Mapper::getTransportSize(buffer_handle_t bufferHandle,
-        uint32_t* outNumFds, uint32_t* outNumInts) const
-{
+void Gralloc2Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+                                      uint32_t* outNumInts) const {
     *outNumFds = uint32_t(bufferHandle->numFds);
     *outNumInts = uint32_t(bufferHandle->numInts);
 
@@ -195,19 +221,17 @@
                 *outNumInts = tmpNumInts;
             });
 
-    if (!ret.isOk()) {
-        error = kTransactionError;
-    }
-    ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d",
-            buffer, error);
+    error = (ret.isOk()) ? error : kTransactionError;
+
+    ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error);
 }
 
-Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage,
-        const IMapper::Rect& accessRegion,
-        int acquireFence, void** outData) const
-{
+status_t Gralloc2Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                              int acquireFence, void** outData) const {
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
 
+    IMapper::Rect accessRegion = sGralloc2Rect(bounds);
+
     // put acquireFence in a hidl_handle
     hardware::hidl_handle acquireFenceHandle;
     NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
@@ -234,15 +258,19 @@
         close(acquireFence);
     }
 
-    return (ret.isOk()) ? error : kTransactionError;
+    error = (ret.isOk()) ? error : kTransactionError;
+
+    ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error);
+
+    return static_cast<status_t>(error);
 }
 
-Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage,
-        const IMapper::Rect& accessRegion,
-        int acquireFence, YCbCrLayout* outLayout) const
-{
+status_t Gralloc2Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                              int acquireFence, android_ycbcr* ycbcr) const {
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
 
+    IMapper::Rect accessRegion = sGralloc2Rect(bounds);
+
     // put acquireFence in a hidl_handle
     hardware::hidl_handle acquireFenceHandle;
     NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
@@ -252,6 +280,7 @@
         acquireFenceHandle = h;
     }
 
+    YCbCrLayout layout;
     Error error;
     auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion,
             acquireFenceHandle,
@@ -262,19 +291,27 @@
                     return;
                 }
 
-                *outLayout = tmpLayout;
+                layout = tmpLayout;
             });
 
+    if (error == Error::NONE) {
+        ycbcr->y = layout.y;
+        ycbcr->cb = layout.cb;
+        ycbcr->cr = layout.cr;
+        ycbcr->ystride = static_cast<size_t>(layout.yStride);
+        ycbcr->cstride = static_cast<size_t>(layout.cStride);
+        ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
+    }
+
     // we own acquireFence even on errors
     if (acquireFence >= 0) {
         close(acquireFence);
     }
 
-    return (ret.isOk()) ? error : kTransactionError;
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
 }
 
-int Mapper::unlock(buffer_handle_t bufferHandle) const
-{
+int Gralloc2Mapper::unlock(buffer_handle_t bufferHandle) const {
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
 
     int releaseFence = -1;
@@ -299,10 +336,7 @@
                 }
             });
 
-    if (!ret.isOk()) {
-        error = kTransactionError;
-    }
-
+    error = (ret.isOk()) ? error : kTransactionError;
     if (error != Error::NONE) {
         ALOGE("unlock(%p) failed with %d", buffer, error);
     }
@@ -310,17 +344,19 @@
     return releaseFence;
 }
 
-Allocator::Allocator(const Mapper& mapper)
-    : mMapper(mapper)
-{
+Gralloc2Allocator::Gralloc2Allocator(const Gralloc2Mapper& mapper) : mMapper(mapper) {
     mAllocator = IAllocator::getService();
     if (mAllocator == nullptr) {
-        LOG_ALWAYS_FATAL("gralloc-alloc is missing");
+        ALOGW("allocator 2.x is not supported");
+        return;
     }
 }
 
-std::string Allocator::dumpDebugInfo() const
-{
+bool Gralloc2Allocator::isSupported() const {
+    return mAllocator != nullptr;
+}
+
+std::string Gralloc2Allocator::dumpDebugInfo() const {
     std::string debugInfo;
 
     mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) {
@@ -330,40 +366,51 @@
     return debugInfo;
 }
 
-Error Allocator::allocate(BufferDescriptor descriptor, uint32_t count,
-        uint32_t* outStride, buffer_handle_t* outBufferHandles) const
-{
-    Error error;
-    auto ret = mAllocator->allocate(descriptor, count,
-            [&](const auto& tmpError, const auto& tmpStride,
-                const auto& tmpBuffers) {
-                error = tmpError;
-                if (tmpError != Error::NONE) {
-                    return;
-                }
+status_t Gralloc2Allocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
+                                     uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+                                     uint32_t* outStride, buffer_handle_t* outBufferHandles) const {
+    IMapper::BufferDescriptorInfo descriptorInfo = {};
+    descriptorInfo.width = width;
+    descriptorInfo.height = height;
+    descriptorInfo.layerCount = layerCount;
+    descriptorInfo.format = static_cast<hardware::graphics::common::V1_1::PixelFormat>(format);
+    descriptorInfo.usage = usage;
 
-                // import buffers
-                for (uint32_t i = 0; i < count; i++) {
-                    error = mMapper.importBuffer(tmpBuffers[i],
-                            &outBufferHandles[i]);
-                    if (error != Error::NONE) {
-                        for (uint32_t j = 0; j < i; j++) {
-                            mMapper.freeBuffer(outBufferHandles[j]);
-                            outBufferHandles[j] = nullptr;
-                        }
-                        return;
-                    }
-                }
+    BufferDescriptor descriptor;
+    status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
+                                              static_cast<void*>(&descriptor));
+    if (error != NO_ERROR) {
+        return error;
+    }
 
-                *outStride = tmpStride;
-            });
+    auto ret = mAllocator->allocate(descriptor, bufferCount,
+                                    [&](const auto& tmpError, const auto& tmpStride,
+                                        const auto& tmpBuffers) {
+                                        error = static_cast<status_t>(tmpError);
+                                        if (tmpError != Error::NONE) {
+                                            return;
+                                        }
+
+                                        // import buffers
+                                        for (uint32_t i = 0; i < bufferCount; i++) {
+                                            error = mMapper.importBuffer(tmpBuffers[i],
+                                                                         &outBufferHandles[i]);
+                                            if (error != NO_ERROR) {
+                                                for (uint32_t j = 0; j < i; j++) {
+                                                    mMapper.freeBuffer(outBufferHandles[j]);
+                                                    outBufferHandles[j] = nullptr;
+                                                }
+                                                return;
+                                            }
+                                        }
+
+                                        *outStride = tmpStride;
+                                    });
 
     // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
     hardware::IPCThreadState::self()->flushCommands();
 
-    return (ret.isOk()) ? error : kTransactionError;
+    return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError);
 }
 
-} // namespace Gralloc2
-
 } // namespace android
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
new file mode 100644
index 0000000..128200e
--- /dev/null
+++ b/libs/ui/Gralloc3.cpp
@@ -0,0 +1,371 @@
+/*
+ * Copyright 2016 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_TAG "Gralloc3"
+
+#include <hidl/ServiceManagement.h>
+#include <hwbinder/IPCThreadState.h>
+#include <ui/Gralloc3.h>
+
+#include <inttypes.h>
+#include <log/log.h>
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wzero-length-array"
+#include <sync/sync.h>
+#pragma clang diagnostic pop
+
+using android::hardware::graphics::allocator::V3_0::IAllocator;
+using android::hardware::graphics::common::V1_1::BufferUsage;
+using android::hardware::graphics::mapper::V3_0::BufferDescriptor;
+using android::hardware::graphics::mapper::V3_0::Error;
+using android::hardware::graphics::mapper::V3_0::IMapper;
+using android::hardware::graphics::mapper::V3_0::YCbCrLayout;
+
+namespace android {
+
+namespace {
+
+static constexpr Error kTransactionError = Error::NO_RESOURCES;
+
+uint64_t getValidUsageBits() {
+    static const uint64_t validUsageBits = []() -> uint64_t {
+        uint64_t bits = 0;
+        for (const auto bit :
+             hardware::hidl_enum_range<hardware::graphics::common::V1_0::BufferUsage>()) {
+            bits = bits | bit;
+        }
+        for (const auto bit :
+             hardware::hidl_enum_range<hardware::graphics::common::V1_1::BufferUsage>()) {
+            bits = bits | bit;
+        }
+        return bits;
+    }();
+    return validUsageBits;
+}
+
+static inline IMapper::Rect sGralloc3Rect(const Rect& rect) {
+    IMapper::Rect outRect{};
+    outRect.left = rect.left;
+    outRect.top = rect.top;
+    outRect.width = rect.width();
+    outRect.height = rect.height();
+    return outRect;
+}
+static inline void sBufferDescriptorInfo(uint32_t width, uint32_t height,
+                                         android::PixelFormat format, uint32_t layerCount,
+                                         uint64_t usage,
+                                         IMapper::BufferDescriptorInfo* outDescriptorInfo) {
+    outDescriptorInfo->width = width;
+    outDescriptorInfo->height = height;
+    outDescriptorInfo->layerCount = layerCount;
+    outDescriptorInfo->format = static_cast<hardware::graphics::common::V1_1::PixelFormat>(format);
+    outDescriptorInfo->usage = usage;
+}
+
+} // anonymous namespace
+
+void Gralloc3Mapper::preload() {
+    android::hardware::preloadPassthroughService<IMapper>();
+}
+
+Gralloc3Mapper::Gralloc3Mapper() {
+    mMapper = IMapper::getService();
+    if (mMapper == nullptr) {
+        ALOGW("mapper 3.x is not supported");
+        return;
+    }
+    if (mMapper->isRemote()) {
+        LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
+    }
+}
+
+bool Gralloc3Mapper::isSupported() const {
+    return mMapper != nullptr;
+}
+
+status_t Gralloc3Mapper::validateBufferDescriptorInfo(
+        IMapper::BufferDescriptorInfo* descriptorInfo) const {
+    uint64_t validUsageBits = getValidUsageBits();
+
+    if (descriptorInfo->usage & ~validUsageBits) {
+        ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64,
+              descriptorInfo->usage & ~validUsageBits);
+        return BAD_VALUE;
+    }
+    return NO_ERROR;
+}
+
+status_t Gralloc3Mapper::createDescriptor(void* bufferDescriptorInfo,
+                                          void* outBufferDescriptor) const {
+    IMapper::BufferDescriptorInfo* descriptorInfo =
+            static_cast<IMapper::BufferDescriptorInfo*>(bufferDescriptorInfo);
+    BufferDescriptor* outDescriptor = static_cast<BufferDescriptor*>(outBufferDescriptor);
+
+    status_t status = validateBufferDescriptorInfo(descriptorInfo);
+    if (status != NO_ERROR) {
+        return status;
+    }
+
+    Error error;
+    auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+        *outDescriptor = tmpDescriptor;
+    };
+
+    hardware::Return<void> ret = mMapper->createDescriptor(*descriptorInfo, hidl_cb);
+
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+}
+
+status_t Gralloc3Mapper::importBuffer(const hardware::hidl_handle& rawHandle,
+                                      buffer_handle_t* outBufferHandle) const {
+    Error error;
+    auto ret = mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+        *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer);
+    });
+
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+}
+
+void Gralloc3Mapper::freeBuffer(buffer_handle_t bufferHandle) const {
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+    auto ret = mMapper->freeBuffer(buffer);
+
+    auto error = (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError;
+    ALOGE_IF(error != Error::NONE, "freeBuffer(%p) failed with %d", buffer, error);
+}
+
+status_t Gralloc3Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+                                            uint32_t height, android::PixelFormat format,
+                                            uint32_t layerCount, uint64_t usage,
+                                            uint32_t stride) const {
+    IMapper::BufferDescriptorInfo descriptorInfo;
+    sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+    auto ret = mMapper->validateBufferSize(buffer, descriptorInfo, stride);
+
+    return static_cast<status_t>((ret.isOk()) ? static_cast<Error>(ret) : kTransactionError);
+}
+
+void Gralloc3Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+                                      uint32_t* outNumInts) const {
+    *outNumFds = uint32_t(bufferHandle->numFds);
+    *outNumInts = uint32_t(bufferHandle->numInts);
+
+    Error error;
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+    auto ret = mMapper->getTransportSize(buffer,
+                                         [&](const auto& tmpError, const auto& tmpNumFds,
+                                             const auto& tmpNumInts) {
+                                             error = tmpError;
+                                             if (error != Error::NONE) {
+                                                 return;
+                                             }
+                                             *outNumFds = tmpNumFds;
+                                             *outNumInts = tmpNumInts;
+                                         });
+
+    error = (ret.isOk()) ? error : kTransactionError;
+
+    ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error);
+}
+
+status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                              int acquireFence, void** outData) const {
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+    IMapper::Rect accessRegion = sGralloc3Rect(bounds);
+
+    // put acquireFence in a hidl_handle
+    hardware::hidl_handle acquireFenceHandle;
+    NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
+    if (acquireFence >= 0) {
+        auto h = native_handle_init(acquireFenceStorage, 1, 0);
+        h->data[0] = acquireFence;
+        acquireFenceHandle = h;
+    }
+
+    Error error;
+    auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle,
+                             [&](const auto& tmpError, const auto& tmpData) {
+                                 error = tmpError;
+                                 if (error != Error::NONE) {
+                                     return;
+                                 }
+                                 *outData = tmpData;
+                             });
+
+    // we own acquireFence even on errors
+    if (acquireFence >= 0) {
+        close(acquireFence);
+    }
+
+    error = (ret.isOk()) ? error : kTransactionError;
+
+    ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error);
+
+    return static_cast<status_t>(error);
+}
+
+status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                              int acquireFence, android_ycbcr* ycbcr) const {
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+    IMapper::Rect accessRegion = sGralloc3Rect(bounds);
+
+    // put acquireFence in a hidl_handle
+    hardware::hidl_handle acquireFenceHandle;
+    NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
+    if (acquireFence >= 0) {
+        auto h = native_handle_init(acquireFenceStorage, 1, 0);
+        h->data[0] = acquireFence;
+        acquireFenceHandle = h;
+    }
+
+    YCbCrLayout layout;
+    Error error;
+    auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion, acquireFenceHandle,
+                                  [&](const auto& tmpError, const auto& tmpLayout) {
+                                      error = tmpError;
+                                      if (error != Error::NONE) {
+                                          return;
+                                      }
+
+                                      layout = tmpLayout;
+                                  });
+
+    if (error == Error::NONE) {
+        ycbcr->y = layout.y;
+        ycbcr->cb = layout.cb;
+        ycbcr->cr = layout.cr;
+        ycbcr->ystride = static_cast<size_t>(layout.yStride);
+        ycbcr->cstride = static_cast<size_t>(layout.cStride);
+        ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
+    }
+
+    // we own acquireFence even on errors
+    if (acquireFence >= 0) {
+        close(acquireFence);
+    }
+
+    return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+}
+
+int Gralloc3Mapper::unlock(buffer_handle_t bufferHandle) const {
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+    int releaseFence = -1;
+    Error error;
+    auto ret = mMapper->unlock(buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+
+        auto fenceHandle = tmpReleaseFence.getNativeHandle();
+        if (fenceHandle && fenceHandle->numFds == 1) {
+            int fd = dup(fenceHandle->data[0]);
+            if (fd >= 0) {
+                releaseFence = fd;
+            } else {
+                ALOGD("failed to dup unlock release fence");
+                sync_wait(fenceHandle->data[0], -1);
+            }
+        }
+    });
+
+    if (!ret.isOk()) {
+        error = kTransactionError;
+    }
+
+    if (error != Error::NONE) {
+        ALOGE("unlock(%p) failed with %d", buffer, error);
+    }
+
+    return releaseFence;
+}
+
+Gralloc3Allocator::Gralloc3Allocator(const Gralloc3Mapper& mapper) : mMapper(mapper) {
+    mAllocator = IAllocator::getService();
+    if (mAllocator == nullptr) {
+        ALOGW("allocator 3.x is not supported");
+        return;
+    }
+}
+
+bool Gralloc3Allocator::isSupported() const {
+    return mAllocator != nullptr;
+}
+
+std::string Gralloc3Allocator::dumpDebugInfo() const {
+    std::string debugInfo;
+
+    mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); });
+
+    return debugInfo;
+}
+
+status_t Gralloc3Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format,
+                                     uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+                                     uint32_t* outStride, buffer_handle_t* outBufferHandles) const {
+    IMapper::BufferDescriptorInfo descriptorInfo;
+    sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+
+    BufferDescriptor descriptor;
+    status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
+                                              static_cast<void*>(&descriptor));
+    if (error != NO_ERROR) {
+        return error;
+    }
+
+    auto ret = mAllocator->allocate(descriptor, bufferCount,
+                                    [&](const auto& tmpError, const auto& tmpStride,
+                                        const auto& tmpBuffers) {
+                                        error = static_cast<status_t>(tmpError);
+                                        if (tmpError != Error::NONE) {
+                                            return;
+                                        }
+
+                                        // import buffers
+                                        for (uint32_t i = 0; i < bufferCount; i++) {
+                                            error = mMapper.importBuffer(tmpBuffers[i],
+                                                                         &outBufferHandles[i]);
+                                            if (error != NO_ERROR) {
+                                                for (uint32_t j = 0; j < i; j++) {
+                                                    mMapper.freeBuffer(outBufferHandles[j]);
+                                                    outBufferHandles[j] = nullptr;
+                                                }
+                                                return;
+                                            }
+                                        }
+                                        *outStride = tmpStride;
+                                    });
+
+    // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now
+    hardware::IPCThreadState::self()->flushCommands();
+
+    return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError);
+}
+
+} // namespace android
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index f56e6b9..efb5798 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -29,7 +29,9 @@
 #include <utils/Singleton.h>
 #include <utils/Trace.h>
 
+#include <ui/Gralloc.h>
 #include <ui/Gralloc2.h>
+#include <ui/Gralloc3.h>
 #include <ui/GraphicBufferMapper.h>
 
 namespace android {
@@ -43,11 +45,17 @@
 KeyedVector<buffer_handle_t,
     GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList;
 
-GraphicBufferAllocator::GraphicBufferAllocator()
-  : mMapper(GraphicBufferMapper::getInstance()),
-    mAllocator(std::make_unique<Gralloc2::Allocator>(
-                mMapper.getGrallocMapper()))
-{
+GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()) {
+    mAllocator = std::make_unique<const Gralloc3Allocator>(
+            reinterpret_cast<const Gralloc3Mapper&>(mMapper.getGrallocMapper()));
+    if (!mAllocator->isSupported()) {
+        mAllocator = std::make_unique<const Gralloc2Allocator>(
+                reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper()));
+    }
+
+    if (!mAllocator->isSupported()) {
+        LOG_ALWAYS_FATAL("gralloc-allocator is missing");
+    }
 }
 
 GraphicBufferAllocator::~GraphicBufferAllocator() {}
@@ -104,15 +112,9 @@
     // TODO(b/72323293, b/72703005): Remove these invalid bits from callers
     usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
 
-    Gralloc2::IMapper::BufferDescriptorInfo info = {};
-    info.width = width;
-    info.height = height;
-    info.layerCount = layerCount;
-    info.format = static_cast<Gralloc2::PixelFormat>(format);
-    info.usage = usage;
-
-    Gralloc2::Error error = mAllocator->allocate(info, stride, handle);
-    if (error == Gralloc2::Error::NONE) {
+    status_t error =
+            mAllocator->allocate(width, height, format, layerCount, usage, 1, stride, handle);
+    if (error == NO_ERROR) {
         Mutex::Autolock _l(sLock);
         KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
         uint32_t bpp = bytesPerPixel(format);
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 2d8e582..b049329 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -32,7 +32,9 @@
 #include <utils/Log.h>
 #include <utils/Trace.h>
 
+#include <ui/Gralloc.h>
 #include <ui/Gralloc2.h>
+#include <ui/Gralloc3.h>
 #include <ui/GraphicBuffer.h>
 
 #include <system/graphics.h>
@@ -43,12 +45,19 @@
 ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper )
 
 void GraphicBufferMapper::preloadHal() {
-    Gralloc2::Mapper::preload();
+    Gralloc2Mapper::preload();
+    Gralloc3Mapper::preload();
 }
 
-GraphicBufferMapper::GraphicBufferMapper()
-  : mMapper(std::make_unique<const Gralloc2::Mapper>())
-{
+GraphicBufferMapper::GraphicBufferMapper() {
+    mMapper = std::make_unique<const Gralloc3Mapper>();
+    if (!mMapper->isSupported()) {
+        mMapper = std::make_unique<const Gralloc2Mapper>();
+    }
+
+    if (!mMapper->isSupported()) {
+        LOG_ALWAYS_FATAL("gralloc-mapper is missing");
+    }
 }
 
 status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle,
@@ -59,22 +68,15 @@
     ATRACE_CALL();
 
     buffer_handle_t bufferHandle;
-    Gralloc2::Error error = mMapper->importBuffer(
-            hardware::hidl_handle(rawHandle), &bufferHandle);
-    if (error != Gralloc2::Error::NONE) {
+    status_t error = mMapper->importBuffer(hardware::hidl_handle(rawHandle), &bufferHandle);
+    if (error != NO_ERROR) {
         ALOGW("importBuffer(%p) failed: %d", rawHandle, error);
-        return static_cast<status_t>(error);
+        return error;
     }
 
-    Gralloc2::IMapper::BufferDescriptorInfo info = {};
-    info.width = width;
-    info.height = height;
-    info.layerCount = layerCount;
-    info.format = static_cast<Gralloc2::PixelFormat>(format);
-    info.usage = usage;
-
-    error = mMapper->validateBufferSize(bufferHandle, info, stride);
-    if (error != Gralloc2::Error::NONE) {
+    error = mMapper->validateBufferSize(bufferHandle, width, height, format, layerCount, usage,
+                                        stride);
+    if (error != NO_ERROR) {
         ALOGE("validateBufferSize(%p) failed: %d", rawHandle, error);
         freeBuffer(bufferHandle);
         return static_cast<status_t>(error);
@@ -100,15 +102,6 @@
     return NO_ERROR;
 }
 
-static inline Gralloc2::IMapper::Rect asGralloc2Rect(const Rect& rect) {
-    Gralloc2::IMapper::Rect outRect{};
-    outRect.left = rect.left;
-    outRect.top = rect.top;
-    outRect.width = rect.width();
-    outRect.height = rect.height();
-    return outRect;
-}
-
 status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage,
         const Rect& bounds, void** vaddr)
 {
@@ -146,13 +139,7 @@
 
     const uint64_t usage = static_cast<uint64_t>(
             android_convertGralloc1To0Usage(producerUsage, consumerUsage));
-    Gralloc2::Error error = mMapper->lock(handle, usage,
-            asGralloc2Rect(bounds), fenceFd, vaddr);
-
-    ALOGW_IF(error != Gralloc2::Error::NONE, "lock(%p, ...) failed: %d",
-            handle, error);
-
-    return static_cast<status_t>(error);
+    return mMapper->lock(handle, usage, bounds, fenceFd, vaddr);
 }
 
 status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle,
@@ -160,19 +147,7 @@
 {
     ATRACE_CALL();
 
-    Gralloc2::YCbCrLayout layout;
-    Gralloc2::Error error = mMapper->lock(handle, usage,
-            asGralloc2Rect(bounds), fenceFd, &layout);
-    if (error == Gralloc2::Error::NONE) {
-        ycbcr->y = layout.y;
-        ycbcr->cb = layout.cb;
-        ycbcr->cr = layout.cr;
-        ycbcr->ystride = static_cast<size_t>(layout.yStride);
-        ycbcr->cstride = static_cast<size_t>(layout.cStride);
-        ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
-    }
-
-    return static_cast<status_t>(error);
+    return mMapper->lock(handle, usage, bounds, fenceFd, ycbcr);
 }
 
 status_t GraphicBufferMapper::unlockAsync(buffer_handle_t handle, int *fenceFd)
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/libs/ui/Size.cpp
similarity index 63%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to libs/ui/Size.cpp
index e6ac6bf..d2996d1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/libs/ui/Size.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,11 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <ui/Size.h>
 
-namespace android {
-namespace mock {
+namespace android::ui {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+const Size Size::INVALID{-1, -1};
+const Size Size::EMPTY{0, 0};
 
-} // namespace mock
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 25128ef..d13942d 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -16,6 +16,7 @@
 
 #include <math.h>
 
+#include <android-base/stringprintf.h>
 #include <cutils/compiler.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
@@ -380,44 +381,47 @@
     return (getOrientation() & ROT_INVALID) ? false : true;
 }
 
-void Transform::dump(const char* name) const
-{
-    type(); // updates the type
+void Transform::dump(std::string& out, const char* name) const {
+    using android::base::StringAppendF;
 
-    String8 flags, type;
-    const mat33& m(mMatrix);
-    uint32_t orient = mType >> 8;
+    type(); // Ensure the information in mType is up to date
 
-    if (orient&ROT_INVALID) {
-        flags.append("ROT_INVALID ");
+    const uint32_t type = mType;
+    const uint32_t orient = type >> 8;
+
+    StringAppendF(&out, "%s 0x%08x (", name, orient);
+
+    if (orient & ROT_INVALID) {
+        out.append("ROT_INVALID ");
     } else {
-        if (orient&ROT_90) {
-            flags.append("ROT_90 ");
+        if (orient & ROT_90) {
+            out.append("ROT_90 ");
         } else {
-            flags.append("ROT_0 ");
+            out.append("ROT_0 ");
         }
-        if (orient&FLIP_V)
-            flags.append("FLIP_V ");
-        if (orient&FLIP_H)
-            flags.append("FLIP_H ");
+        if (orient & FLIP_V) out.append("FLIP_V ");
+        if (orient & FLIP_H) out.append("FLIP_H ");
     }
 
-    if (!(mType&(SCALE|ROTATE|TRANSLATE)))
-        type.append("IDENTITY ");
-    if (mType&SCALE)
-        type.append("SCALE ");
-    if (mType&ROTATE)
-        type.append("ROTATE ");
-    if (mType&TRANSLATE)
-        type.append("TRANSLATE ");
+    StringAppendF(&out, ") 0x%02x (", type);
 
-    ALOGD("%s 0x%08x (%s, %s)", name, mType, flags.string(), type.string());
-    ALOGD("%.4f  %.4f  %.4f", static_cast<double>(m[0][0]), static_cast<double>(m[1][0]),
-          static_cast<double>(m[2][0]));
-    ALOGD("%.4f  %.4f  %.4f", static_cast<double>(m[0][1]), static_cast<double>(m[1][1]),
-          static_cast<double>(m[2][1]));
-    ALOGD("%.4f  %.4f  %.4f", static_cast<double>(m[0][2]), static_cast<double>(m[1][2]),
-          static_cast<double>(m[2][2]));
+    if (!(type & (SCALE | ROTATE | TRANSLATE))) out.append("IDENTITY ");
+    if (type & SCALE) out.append("SCALE ");
+    if (type & ROTATE) out.append("ROTATE ");
+    if (type & TRANSLATE) out.append("TRANSLATE ");
+
+    out.append(")\n");
+
+    for (size_t i = 0; i < 3; i++) {
+        StringAppendF(&out, "    %.4f  %.4f  %.4f\n", static_cast<double>(mMatrix[0][i]),
+                      static_cast<double>(mMatrix[1][i]), static_cast<double>(mMatrix[2][i]));
+    }
+}
+
+void Transform::dump(const char* name) const {
+    std::string out;
+    dump(out, name);
+    ALOGD("%s", out.c_str());
 }
 
 }  // namespace ui
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
new file mode 100644
index 0000000..92bf043
--- /dev/null
+++ b/libs/ui/include/ui/Gralloc.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2016 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_UI_GRALLOC_H
+#define ANDROID_UI_GRALLOC_H
+
+#include <string>
+
+#include <hidl/HidlSupport.h>
+#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+// A wrapper to IMapper
+class GrallocMapper {
+public:
+    virtual ~GrallocMapper();
+
+    virtual bool isSupported() const = 0;
+
+    virtual status_t createDescriptor(void* bufferDescriptorInfo,
+                                      void* outBufferDescriptor) const = 0;
+
+    // Import a buffer that is from another HAL, another process, or is
+    // cloned.
+    //
+    // The returned handle must be freed with freeBuffer.
+    virtual status_t importBuffer(const hardware::hidl_handle& rawHandle,
+                                  buffer_handle_t* outBufferHandle) const = 0;
+
+    virtual void freeBuffer(buffer_handle_t bufferHandle) const = 0;
+
+    virtual status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+                                        uint32_t height, android::PixelFormat format,
+                                        uint32_t layerCount, uint64_t usage,
+                                        uint32_t stride) const = 0;
+
+    virtual void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+                                  uint32_t* outNumInts) const = 0;
+
+    // The ownership of acquireFence is always transferred to the callee, even
+    // on errors.
+    virtual status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                          int acquireFence, void** outData) const = 0;
+
+    // The ownership of acquireFence is always transferred to the callee, even
+    // on errors.
+    virtual status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                          int acquireFence, android_ycbcr* ycbcr) const = 0;
+
+    // unlock returns a fence sync object (or -1) and the fence sync object is
+    // owned by the caller
+    virtual int unlock(buffer_handle_t bufferHandle) const = 0;
+};
+
+// A wrapper to IAllocator
+class GrallocAllocator {
+public:
+    virtual ~GrallocAllocator();
+
+    virtual bool isSupported() const = 0;
+
+    virtual std::string dumpDebugInfo() const = 0;
+
+    /*
+     * The returned buffers are already imported and must not be imported
+     * again.  outBufferHandles must point to a space that can contain at
+     * least "bufferCount" buffer_handle_t.
+     */
+    virtual status_t allocate(uint32_t width, uint32_t height, PixelFormat format,
+                              uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+                              uint32_t* outStride, buffer_handle_t* outBufferHandles) const = 0;
+};
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC_H
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index 5a8dbda..e03cb43 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -23,119 +23,71 @@
 #include <android/hardware/graphics/common/1.1/types.h>
 #include <android/hardware/graphics/mapper/2.0/IMapper.h>
 #include <android/hardware/graphics/mapper/2.1/IMapper.h>
+#include <ui/Gralloc.h>
+#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
 #include <utils/StrongPointer.h>
 
 namespace android {
 
-namespace Gralloc2 {
-
-using hardware::graphics::allocator::V2_0::IAllocator;
-using hardware::graphics::common::V1_1::BufferUsage;
-using hardware::graphics::common::V1_1::PixelFormat;
-using hardware::graphics::mapper::V2_1::IMapper;
-using hardware::graphics::mapper::V2_0::BufferDescriptor;
-using hardware::graphics::mapper::V2_0::Error;
-using hardware::graphics::mapper::V2_0::YCbCrLayout;
-
-// A wrapper to IMapper
-class Mapper {
+class Gralloc2Mapper : public GrallocMapper {
 public:
     static void preload();
 
-    Mapper();
+    Gralloc2Mapper();
 
-    Error createDescriptor(
-            const IMapper::BufferDescriptorInfo& descriptorInfo,
-            BufferDescriptor* outDescriptor) const;
+    bool isSupported() const override;
 
-    // Import a buffer that is from another HAL, another process, or is
-    // cloned.
-    //
-    // The returned handle must be freed with freeBuffer.
-    Error importBuffer(const hardware::hidl_handle& rawHandle,
-            buffer_handle_t* outBufferHandle) const;
+    status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
 
-    void freeBuffer(buffer_handle_t bufferHandle) const;
+    status_t importBuffer(const hardware::hidl_handle& rawHandle,
+                          buffer_handle_t* outBufferHandle) const override;
 
-    Error validateBufferSize(buffer_handle_t bufferHandle,
-            const IMapper::BufferDescriptorInfo& descriptorInfo,
-            uint32_t stride) const;
+    void freeBuffer(buffer_handle_t bufferHandle) const override;
 
-    void getTransportSize(buffer_handle_t bufferHandle,
-            uint32_t* outNumFds, uint32_t* outNumInts) const;
+    status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height,
+                                android::PixelFormat format, uint32_t layerCount, uint64_t usage,
+                                uint32_t stride) const override;
 
-    // The ownership of acquireFence is always transferred to the callee, even
-    // on errors.
-    Error lock(buffer_handle_t bufferHandle, uint64_t usage,
-            const IMapper::Rect& accessRegion,
-            int acquireFence, void** outData) const;
+    void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+                          uint32_t* outNumInts) const override;
 
-    // The ownership of acquireFence is always transferred to the callee, even
-    // on errors.
-    Error lock(buffer_handle_t bufferHandle, uint64_t usage,
-            const IMapper::Rect& accessRegion,
-            int acquireFence, YCbCrLayout* outLayout) const;
+    status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                  int acquireFence, void** outData) const override;
 
-    // unlock returns a fence sync object (or -1) and the fence sync object is
-    // owned by the caller
-    int unlock(buffer_handle_t bufferHandle) const;
+    status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                  int acquireFence, android_ycbcr* ycbcr) const override;
+
+    int unlock(buffer_handle_t bufferHandle) const override;
 
 private:
     // Determines whether the passed info is compatible with the mapper.
-    Error validateBufferDescriptorInfo(
-            const IMapper::BufferDescriptorInfo& descriptorInfo) const;
+    status_t validateBufferDescriptorInfo(
+            hardware::graphics::mapper::V2_1::IMapper::BufferDescriptorInfo* descriptorInfo) const;
 
     sp<hardware::graphics::mapper::V2_0::IMapper> mMapper;
-    sp<IMapper> mMapperV2_1;
+    sp<hardware::graphics::mapper::V2_1::IMapper> mMapperV2_1;
 };
 
-// A wrapper to IAllocator
-class Allocator {
+class Gralloc2Allocator : public GrallocAllocator {
 public:
     // An allocator relies on a mapper, and that mapper must be alive at all
     // time.
-    Allocator(const Mapper& mapper);
+    Gralloc2Allocator(const Gralloc2Mapper& mapper);
 
-    std::string dumpDebugInfo() const;
+    bool isSupported() const override;
 
-    /*
-     * The returned buffers are already imported and must not be imported
-     * again.  outBufferHandles must point to a space that can contain at
-     * least "count" buffer_handle_t.
-     */
-    Error allocate(BufferDescriptor descriptor, uint32_t count,
-            uint32_t* outStride, buffer_handle_t* outBufferHandles) const;
+    std::string dumpDebugInfo() const override;
 
-    Error allocate(BufferDescriptor descriptor,
-            uint32_t* outStride, buffer_handle_t* outBufferHandle) const
-    {
-        return allocate(descriptor, 1, outStride, outBufferHandle);
-    }
-
-    Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo, uint32_t count,
-            uint32_t* outStride, buffer_handle_t* outBufferHandles) const
-    {
-        BufferDescriptor descriptor;
-        Error error = mMapper.createDescriptor(descriptorInfo, &descriptor);
-        if (error == Error::NONE) {
-            error = allocate(descriptor, count, outStride, outBufferHandles);
-        }
-        return error;
-    }
-
-    Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
-            uint32_t* outStride, buffer_handle_t* outBufferHandle) const
-    {
-        return allocate(descriptorInfo, 1, outStride, outBufferHandle);
-    }
+    status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+                      uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+                      buffer_handle_t* outBufferHandles) const override;
 
 private:
-    const Mapper& mMapper;
-    sp<IAllocator> mAllocator;
+    const Gralloc2Mapper& mMapper;
+    sp<hardware::graphics::allocator::V2_0::IAllocator> mAllocator;
 };
 
-} // namespace Gralloc2
-
 } // namespace android
 
 #endif // ANDROID_UI_GRALLOC2_H
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
new file mode 100644
index 0000000..510ce4a
--- /dev/null
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016 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_UI_GRALLOC3_H
+#define ANDROID_UI_GRALLOC3_H
+
+#include <string>
+
+#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
+#include <android/hardware/graphics/common/1.1/types.h>
+#include <android/hardware/graphics/mapper/3.0/IMapper.h>
+#include <ui/Gralloc.h>
+#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class Gralloc3Mapper : public GrallocMapper {
+public:
+    static void preload();
+
+    Gralloc3Mapper();
+
+    bool isSupported() const override;
+
+    status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
+
+    status_t importBuffer(const hardware::hidl_handle& rawHandle,
+                          buffer_handle_t* outBufferHandle) const override;
+
+    void freeBuffer(buffer_handle_t bufferHandle) const override;
+
+    status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height,
+                                android::PixelFormat format, uint32_t layerCount, uint64_t usage,
+                                uint32_t stride) const override;
+
+    void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
+                          uint32_t* outNumInts) const override;
+
+    status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                  int acquireFence, void** outData) const override;
+
+    status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                  int acquireFence, android_ycbcr* ycbcr) const override;
+
+    int unlock(buffer_handle_t bufferHandle) const override;
+
+private:
+    // Determines whether the passed info is compatible with the mapper.
+    status_t validateBufferDescriptorInfo(
+            hardware::graphics::mapper::V3_0::IMapper::BufferDescriptorInfo* descriptorInfo) const;
+
+    sp<hardware::graphics::mapper::V3_0::IMapper> mMapper;
+};
+
+class Gralloc3Allocator : public GrallocAllocator {
+public:
+    // An allocator relies on a mapper, and that mapper must be alive at all
+    // time.
+    Gralloc3Allocator(const Gralloc3Mapper& mapper);
+
+    bool isSupported() const override;
+
+    std::string dumpDebugInfo() const override;
+
+    status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+                      uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+                      buffer_handle_t* outBufferHandles) const override;
+
+private:
+    const Gralloc3Mapper& mMapper;
+    sp<hardware::graphics::allocator::V3_0::IAllocator> mAllocator;
+};
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC3_H
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index 7e2b230..3a547b6 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -34,10 +34,7 @@
 
 namespace android {
 
-namespace Gralloc2 {
-class Allocator;
-}
-
+class GrallocAllocator;
 class GraphicBufferMapper;
 
 class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator>
@@ -75,7 +72,7 @@
     ~GraphicBufferAllocator();
 
     GraphicBufferMapper& mMapper;
-    const std::unique_ptr<const Gralloc2::Allocator> mAllocator;
+    std::unique_ptr<const GrallocAllocator> mAllocator;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 7cf003d..156bd7a 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -35,10 +35,7 @@
 
 // ---------------------------------------------------------------------------
 
-namespace Gralloc2 {
-class Mapper;
-}
-
+class GrallocMapper;
 class Rect;
 
 class GraphicBufferMapper : public Singleton<GraphicBufferMapper>
@@ -80,9 +77,8 @@
 
     status_t unlockAsync(buffer_handle_t handle, int *fenceFd);
 
-    const Gralloc2::Mapper& getGrallocMapper() const
-    {
-        return *mMapper;
+    const GrallocMapper& getGrallocMapper() const {
+        return reinterpret_cast<const GrallocMapper&>(*mMapper);
     }
 
 private:
@@ -90,7 +86,7 @@
 
     GraphicBufferMapper();
 
-    const std::unique_ptr<const Gralloc2::Mapper> mMapper;
+    std::unique_ptr<const GrallocMapper> mMapper;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index e9da087..1768805 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -24,6 +24,7 @@
 
 #include <ui/FloatRect.h>
 #include <ui/Point.h>
+#include <ui/Size.h>
 
 #include <android/rect.h>
 
@@ -78,6 +79,13 @@
         bottom = static_cast<int32_t>(floatRect.bottom + 0.5f);
     }
 
+    inline explicit Rect(const ui::Size& size) {
+        left = 0;
+        top = 0;
+        right = size.width;
+        bottom = size.height;
+    }
+
     void makeInvalid();
 
     inline void clear() {
@@ -106,6 +114,8 @@
         return bottom - top;
     }
 
+    ui::Size getSize() const { return ui::Size(getWidth(), getHeight()); }
+
     __attribute__((no_sanitize("signed-integer-overflow")))
     inline Rect getBounds() const {
         return Rect(right - left, bottom - top);
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h
new file mode 100644
index 0000000..c39d8af
--- /dev/null
+++ b/libs/ui/include/ui/Size.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+namespace android {
+namespace ui {
+
+// Forward declare a few things.
+struct Size;
+bool operator==(const Size& lhs, const Size& rhs);
+
+/**
+ * A simple value type representing a two-dimensional size
+ */
+struct Size {
+    int32_t width;
+    int32_t height;
+
+    // Special values
+    static const Size INVALID;
+    static const Size EMPTY;
+
+    // ------------------------------------------------------------------------
+    // Construction
+    // ------------------------------------------------------------------------
+
+    Size() : Size(INVALID) {}
+    template <typename T>
+    Size(T&& w, T&& h)
+          : width(Size::clamp<int32_t, T>(std::forward<T>(w))),
+            height(Size::clamp<int32_t, T>(std::forward<T>(h))) {}
+
+    // ------------------------------------------------------------------------
+    // Accessors
+    // ------------------------------------------------------------------------
+
+    int32_t getWidth() const { return width; }
+    int32_t getHeight() const { return height; }
+
+    template <typename T>
+    void setWidth(T&& v) {
+        width = Size::clamp<int32_t, T>(std::forward<T>(v));
+    }
+    template <typename T>
+    void setHeight(T&& v) {
+        height = Size::clamp<int32_t, T>(std::forward<T>(v));
+    }
+
+    // ------------------------------------------------------------------------
+    // Assignment
+    // ------------------------------------------------------------------------
+
+    void set(const Size& size) { *this = size; }
+    template <typename T>
+    void set(T&& w, T&& h) {
+        set(Size(std::forward<T>(w), std::forward<T>(h)));
+    }
+
+    // Sets the value to INVALID
+    void makeInvalid() { set(INVALID); }
+
+    // Sets the value to EMPTY
+    void clear() { set(EMPTY); }
+
+    // ------------------------------------------------------------------------
+    // Semantic checks
+    // ------------------------------------------------------------------------
+
+    // Valid means non-negative width and height
+    bool isValid() const { return width >= 0 && height >= 0; }
+
+    // Empty means zero width and height
+    bool isEmpty() const { return *this == EMPTY; }
+
+    // ------------------------------------------------------------------------
+    // Clamp Helpers
+    // ------------------------------------------------------------------------
+
+    // Note: We use only features available in C++11 here for compatibility with
+    // external targets which include this file directly or indirectly and which
+    // themselves use C++11.
+
+    // C++11 compatible replacement for std::remove_cv_reference_t [C++20]
+    template <typename T>
+    using remove_cv_reference_t =
+            typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+    // Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
+    // clamping the input value to the output range if necessary.
+    template <typename ToType, typename FromType>
+    static Size::remove_cv_reference_t<ToType> clamp(
+            typename std::enable_if<
+                    std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_bounded &&
+                            std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_bounded,
+                    FromType&&>::type v) {
+        static constexpr auto toHighest = std::numeric_limits<remove_cv_reference_t<ToType>>::max();
+        static constexpr auto toLowest =
+                std::numeric_limits<remove_cv_reference_t<ToType>>::lowest();
+        static constexpr auto fromHighest =
+                std::numeric_limits<remove_cv_reference_t<FromType>>::max();
+        static constexpr auto fromLowest =
+                std::numeric_limits<remove_cv_reference_t<FromType>>::lowest();
+
+        // A clamp is needed if the range of FromType is not a subset of the range of ToType
+        static constexpr bool isClampNeeded = (toLowest > fromLowest) || (toHighest < fromHighest);
+
+        // If a clamp is not needed, the conversion is just a trivial cast.
+        if (!isClampNeeded) {
+            return static_cast<ToType>(v);
+        }
+
+        // Otherwise we leverage implicit conversion to safely compare values of
+        // different types, to ensure we return a value clamped to the range of
+        // ToType.
+        return v < toLowest ? toLowest : (v > toHighest ? toHighest : static_cast<ToType>(v));
+    }
+};
+
+// ------------------------------------------------------------------------
+// Comparisons
+// ------------------------------------------------------------------------
+
+inline bool operator==(const Size& lhs, const Size& rhs) {
+    return lhs.width == rhs.width && lhs.height == rhs.height;
+}
+
+inline bool operator!=(const Size& lhs, const Size& rhs) {
+    return !operator==(lhs, rhs);
+}
+
+inline bool operator<(const Size& lhs, const Size& rhs) {
+    // Orders by increasing width, then height.
+    if (lhs.width != rhs.width) return lhs.width < rhs.width;
+    return lhs.height < rhs.height;
+}
+
+} // namespace ui
+} // namespace android
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index 900a5c4..dcb26cf 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -19,6 +19,7 @@
 
 #include <stdint.h>
 #include <sys/types.h>
+#include <string>
 
 #include <hardware/hardware.h>
 #include <math/vec2.h>
@@ -90,6 +91,7 @@
     Transform inverse() const;
 
     // for debugging
+    void dump(std::string& result, const char* name) const;
     void dump(const char* name) const;
 
 private:
diff --git a/libs/ui/include_vndk/ui/Size.h b/libs/ui/include_vndk/ui/Size.h
new file mode 120000
index 0000000..fd2b21b
--- /dev/null
+++ b/libs/ui/include_vndk/ui/Size.h
@@ -0,0 +1 @@
+../../include/ui/Size.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 665469e..373fa4f 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -71,3 +71,10 @@
     ],
     cflags: ["-Wall", "-Werror"],
 }
+
+cc_test {
+    name: "Size_test",
+    shared_libs: ["libui"],
+    srcs: ["Size_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+}
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
new file mode 100644
index 0000000..69e1ac8
--- /dev/null
+++ b/libs/ui/tests/Size_test.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SizeTest"
+
+#include <cmath>
+#include <cstdlib>
+
+#include <ui/Size.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace ui {
+
+TEST(SizeTest, BasicConstructionAndEqualityComparison) {
+    Size s(123, 456);
+
+    EXPECT_EQ(123, s.width);
+    EXPECT_EQ(123, s.getWidth());
+
+    EXPECT_EQ(456, s.height);
+    EXPECT_EQ(456, s.getHeight());
+
+    EXPECT_EQ(Size(123, 456), s);
+    EXPECT_NE(Size(456, 123), s);
+}
+
+TEST(SizeTest, BasicLessThanComparison) {
+    EXPECT_TRUE(Size(0, 1) < Size(2, 3));
+    EXPECT_FALSE(Size(2, 3) < Size(0, 1));
+
+    EXPECT_TRUE(Size(0, 3) < Size(2, 1));
+    EXPECT_FALSE(Size(2, 1) < Size(0, 3));
+
+    EXPECT_TRUE(Size(0, 1) < Size(0, 3));
+    EXPECT_FALSE(Size(0, 3) < Size(0, 1));
+
+    EXPECT_FALSE(Size(1, 1) < Size(1, 1));
+}
+
+TEST(SizeTest, ValidAndEmpty) {
+    {
+        Size s;
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(-1, -1);
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(1, -1000);
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(-1000, 1);
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(-1000, -1000);
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        const auto& s = Size::INVALID;
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(123, 456);
+        s.makeInvalid();
+        EXPECT_FALSE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+
+    {
+        Size s(0, 0);
+        EXPECT_TRUE(s.isValid());
+        EXPECT_TRUE(s.isEmpty());
+    }
+
+    {
+        const auto& s = Size::EMPTY;
+        EXPECT_TRUE(s.isValid());
+        EXPECT_TRUE(s.isEmpty());
+    }
+
+    {
+        Size s(123, 456);
+        s.clear();
+        EXPECT_TRUE(s.isValid());
+        EXPECT_TRUE(s.isEmpty());
+    }
+
+    {
+        Size s(123, 456);
+        EXPECT_TRUE(s.isValid());
+        EXPECT_FALSE(s.isEmpty());
+    }
+}
+
+TEST(SizeTest, Set) {
+    {
+        Size s;
+        s.setWidth(0);
+        EXPECT_EQ(Size(0, -1), s);
+    }
+
+    {
+        Size s;
+        s.setHeight(0);
+        EXPECT_EQ(Size(-1, 0), s);
+    }
+
+    {
+        Size s;
+        s.set(123, 456);
+        EXPECT_EQ(Size(123, 456), s);
+    }
+}
+
+template <typename T, typename U>
+void ClampTest(T input, U expected) {
+    // The constructor, set(), setWidth() and setHeight() all allow arbitrary
+    // conversions from other numeric types, and implement clamping if necessary.
+
+    EXPECT_EQ(Size(expected, expected), Size(input, input));
+
+    {
+        Size s;
+        s.set(input, input);
+        EXPECT_EQ(Size(expected, expected), s);
+    }
+
+    {
+        Size s;
+        s.setWidth(input);
+        EXPECT_EQ(expected, s.width);
+    }
+
+    {
+        Size s;
+        s.setHeight(input);
+        EXPECT_EQ(expected, s.height);
+    }
+}
+
+TEST(SizeTest, Int8RangeIsNotClamped) {
+    ClampTest(std::numeric_limits<int8_t>::max(), std::numeric_limits<int8_t>::max());
+    ClampTest(int8_t(0), int8_t(0));
+    ClampTest(std::numeric_limits<int8_t>::lowest(), std::numeric_limits<int8_t>::lowest());
+}
+
+TEST(SizeTest, FloatRangeIsClamped) {
+    ClampTest(std::numeric_limits<float>::max(), std::numeric_limits<int32_t>::max());
+    ClampTest(float(0), int32_t(0));
+    ClampTest(std::numeric_limits<float>::lowest(), std::numeric_limits<int32_t>::lowest());
+}
+
+} // namespace ui
+} // namespace android
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index b37d543..01c60d8 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -25,6 +25,7 @@
         "android.hardware.configstore@1.1",
         "android.hardware.configstore@1.2",
         "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
@@ -78,6 +79,7 @@
     ],
     export_shared_lib_headers: [
         "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
@@ -115,6 +117,7 @@
         "BufferLayerConsumer.cpp",
         "BufferQueueLayer.cpp",
         "BufferStateLayer.cpp",
+        "BufferStateLayerCache.cpp",
         "Client.cpp",
         "ColorLayer.cpp",
         "ContainerLayer.cpp",
@@ -187,6 +190,7 @@
         "android.hardware.configstore@1.0",
         "android.hardware.configstore@1.2",
         "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.allocator@3.0",
         "libbinder",
         "libcutils",
         "libdisplayservicehidl",
diff --git a/services/surfaceflinger/BufferStateLayerCache.cpp b/services/surfaceflinger/BufferStateLayerCache.cpp
new file mode 100644
index 0000000..c82ad7b
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayerCache.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "BufferStateLayerCache"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "BufferStateLayerCache.h"
+
+#define MAX_CACHE_SIZE 64
+
+namespace android {
+
+int32_t BufferStateLayerCache::add(const sp<IBinder>& processToken,
+                                   const sp<GraphicBuffer>& buffer) {
+    std::lock_guard lock(mMutex);
+
+    auto& processCache = getProccessCache(processToken);
+
+    int32_t slot = findSlot(processCache);
+    if (slot < 0) {
+        return slot;
+    }
+
+    processCache[slot] = buffer;
+
+    return slot;
+}
+
+void BufferStateLayerCache::release(const sp<IBinder>& processToken, int32_t id) {
+    if (id < 0) {
+        ALOGE("invalid buffer id");
+        return;
+    }
+
+    std::lock_guard lock(mMutex);
+    auto& processCache = getProccessCache(processToken);
+
+    if (id >= processCache.size()) {
+        ALOGE("invalid buffer id");
+        return;
+    }
+    processCache[id] = nullptr;
+}
+
+sp<GraphicBuffer> BufferStateLayerCache::get(const sp<IBinder>& processToken, int32_t id) {
+    if (id < 0) {
+        ALOGE("invalid buffer id");
+        return nullptr;
+    }
+
+    std::lock_guard lock(mMutex);
+    auto& processCache = getProccessCache(processToken);
+
+    if (id >= processCache.size()) {
+        ALOGE("invalid buffer id");
+        return nullptr;
+    }
+    return processCache[id];
+}
+
+std::vector<sp<GraphicBuffer>>& BufferStateLayerCache::getProccessCache(
+        const sp<IBinder>& processToken) {
+    return mBuffers[processToken];
+}
+
+int32_t BufferStateLayerCache::findSlot(std::vector<sp<GraphicBuffer>>& processCache) {
+    int32_t slot = 0;
+
+    for (const sp<GraphicBuffer> buffer : processCache) {
+        if (!buffer) {
+            return slot;
+        }
+        slot++;
+    }
+
+    if (processCache.size() < MAX_CACHE_SIZE) {
+        processCache.push_back(nullptr);
+        return slot;
+    }
+
+    return -1;
+}
+
+}; // namespace android
diff --git a/services/surfaceflinger/BufferStateLayerCache.h b/services/surfaceflinger/BufferStateLayerCache.h
new file mode 100644
index 0000000..623f0c6
--- /dev/null
+++ b/services/surfaceflinger/BufferStateLayerCache.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <binder/IBinder.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/RefBase.h>
+
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+
+class BufferStateLayerCache {
+public:
+    int32_t add(const sp<IBinder>& processToken, const sp<GraphicBuffer>& buffer);
+    void release(const sp<IBinder>& processToken, int32_t id);
+
+    sp<GraphicBuffer> get(const sp<IBinder>& processToken, int32_t id);
+
+private:
+    std::mutex mMutex;
+
+    std::vector<sp<GraphicBuffer>>& getProccessCache(const sp<IBinder>& processToken)
+            REQUIRES(mMutex);
+
+    int32_t findSlot(std::vector<sp<GraphicBuffer>>& proccessCache) REQUIRES(mMutex);
+
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& strongPointer) const {
+            return std::hash<IBinder*>{}(strongPointer.get());
+        }
+    };
+
+    std::unordered_map<sp<IBinder> /*caching process*/, std::vector<sp<GraphicBuffer>>, IBinderHash>
+            mBuffers GUARDED_BY(mMutex);
+};
+
+}; // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index ec5e131..49fa84a 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -16,6 +16,8 @@
         "libgui",
         "liblayers_proto",
         "liblog",
+        "libnativewindow",
+        "libsync",
         "libtimestats_proto",
         "libui",
         "libutils",
@@ -35,6 +37,13 @@
     defaults: ["libcompositionengine_defaults"],
     srcs: [
         "src/CompositionEngine.cpp",
+        "src/Display.cpp",
+        "src/DisplayColorProfile.cpp",
+        "src/DisplaySurface.cpp",
+        "src/DumpHelpers.cpp",
+        "src/Output.cpp",
+        "src/OutputCompositionState.cpp",
+        "src/RenderSurface.cpp",
     ],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
@@ -45,6 +54,11 @@
     defaults: ["libcompositionengine_defaults"],
     srcs: [
         "mock/CompositionEngine.cpp",
+        "mock/Display.cpp",
+        "mock/DisplayColorProfile.cpp",
+        "mock/DisplaySurface.cpp",
+        "mock/Output.cpp",
+        "mock/RenderSurface.cpp",
     ],
     static_libs: [
         "libgtest",
@@ -61,7 +75,11 @@
     defaults: ["libcompositionengine_defaults"],
     srcs: [
         "tests/CompositionEngineTest.cpp",
+        "tests/DisplayColorProfileTest.cpp",
+        "tests/DisplayTest.cpp",
         "tests/MockHWComposer.cpp",
+        "tests/OutputTest.cpp",
+        "tests/RenderSurfaceTest.cpp",
     ],
     static_libs: [
         "libcompositionengine",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index af8515f..30cdb49 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -28,6 +28,10 @@
 
 namespace compositionengine {
 
+class Display;
+
+struct DisplayCreationArgs;
+
 /**
  * Encapsulates all the interfaces and implementation details for performing
  * display output composition.
@@ -36,6 +40,9 @@
 public:
     virtual ~CompositionEngine();
 
+    // Create a composition Display
+    virtual std::shared_ptr<Display> createDisplay(DisplayCreationArgs&&) = 0;
+
     virtual HWComposer& getHwComposer() const = 0;
     virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
new file mode 100644
index 0000000..dbcd3bd
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <optional>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+#include <compositionengine/Output.h>
+
+namespace android::compositionengine {
+
+struct RenderSurfaceCreationArgs;
+struct DisplayColorProfileCreationArgs;
+
+/**
+ * A display is a composition target which may be backed by a hardware composer
+ * display device
+ */
+class Display : public virtual Output {
+public:
+    // Gets the HWC DisplayId for the display if there is one
+    virtual const std::optional<DisplayId>& getId() const = 0;
+
+    // True if the display is secure
+    virtual bool isSecure() const = 0;
+
+    // True if the display is virtual
+    virtual bool isVirtual() const = 0;
+
+    // Releases the use of the HWC display, if any
+    virtual void disconnect() = 0;
+
+    // Creates a render color mode for the display
+    virtual void createDisplayColorProfile(DisplayColorProfileCreationArgs&&) = 0;
+
+    // Creates a render surface for the display
+    virtual void createRenderSurface(RenderSurfaceCreationArgs&&) = 0;
+
+protected:
+    ~Display() = default;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
new file mode 100644
index 0000000..e2a0d42
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <ui/GraphicTypes.h>
+
+namespace android {
+
+class HdrCapabilities;
+
+namespace compositionengine {
+
+/**
+ * Encapsulates all the state and functionality for how colors should be
+ * transformed for a display
+ */
+class DisplayColorProfile {
+public:
+    constexpr static float sDefaultMinLumiance = 0.0;
+    constexpr static float sDefaultMaxLumiance = 500.0;
+
+    virtual ~DisplayColorProfile();
+
+    // Returns true if the profile is valid. This is meant to be checked post-
+    // construction and prior to use, as not everything is set up by the
+    // constructor.
+    virtual bool isValid() const = 0;
+
+    // Returns true if the profile supports the indicated render intent
+    virtual bool hasRenderIntent(ui::RenderIntent) const = 0;
+
+    // Returns true if the profile supports the indicated dataspace
+    virtual bool hasLegacyHdrSupport(ui::Dataspace) const = 0;
+
+    // Obtains the best combination of color mode and render intent for the
+    // input values
+    virtual void getBestColorMode(ui::Dataspace dataspace, ui::RenderIntent intent,
+                                  ui::Dataspace* outDataspace, ui::ColorMode* outMode,
+                                  ui::RenderIntent* outIntent) const = 0;
+
+    // Returns true if the profile supports a wide color gamut
+    virtual bool hasWideColorGamut() const = 0;
+
+    // Returns the per-frame metadata value for this profile
+    virtual int32_t getSupportedPerFrameMetadata() const = 0;
+
+    // Returns true if HWC for this profile supports HDR10Plus
+    virtual bool hasHDR10PlusSupport() const = 0;
+
+    // Returns true if HWC for this profile supports HDR10
+    virtual bool hasHDR10Support() const = 0;
+
+    // Returns true if HWC for this profile supports HLG
+    virtual bool hasHLGSupport() const = 0;
+
+    // Returns true if HWC for this profile supports DolbyVision
+    virtual bool hasDolbyVisionSupport() const = 0;
+
+    // Gets the supported HDR capabilities for the profile
+    virtual const HdrCapabilities& getHdrCapabilities() const = 0;
+
+    // Debugging
+    virtual void dump(std::string&) const = 0;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
new file mode 100644
index 0000000..ef0f925
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <unordered_map>
+#include <vector>
+
+#include <ui/GraphicTypes.h>
+#include <ui/HdrCapabilities.h>
+
+namespace android::compositionengine {
+
+/**
+ * A parameter object for creating DisplayColorProfile instances
+ */
+struct DisplayColorProfileCreationArgs {
+    using HwcColorModes = std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>;
+
+    // True if this display supports a wide color gamut
+    bool hasWideColorGamut;
+
+    // The HDR capabilities supported by the HWC
+    HdrCapabilities hdrCapabilities;
+
+    // The per-frame metadata supported by the HWC
+    int32_t supportedPerFrameMetadata;
+
+    // The mapping of color modes and render intents supported by the HWC
+    HwcColorModes hwcColorModes;
+};
+
+/**
+ * A helper for setting up a DisplayColorProfileCreationArgs value in-line.
+ *
+ * Prefer this builder over raw structure initialization.
+ *
+ * Instead of:
+ *
+ *   DisplayColorProfileCreationArgs{false, HdrCapabilities(), 0,
+ *                                   HwcColorModes()}
+ *
+ * Prefer:
+ *
+ *  DisplayColorProfileCreationArgsBuilder().setHasWideColorGamut(false)
+ *      .setIsVirtual(false).setDisplayId(displayId).Build();
+ */
+class DisplayColorProfileCreationArgsBuilder {
+public:
+    DisplayColorProfileCreationArgs Build() { return std::move(mArgs); }
+
+    DisplayColorProfileCreationArgsBuilder& setHasWideColorGamut(bool hasWideColorGamut) {
+        mArgs.hasWideColorGamut = hasWideColorGamut;
+        return *this;
+    }
+    DisplayColorProfileCreationArgsBuilder& setHdrCapabilities(HdrCapabilities&& hdrCapabilities) {
+        mArgs.hdrCapabilities = std::move(hdrCapabilities);
+        return *this;
+    }
+    DisplayColorProfileCreationArgsBuilder& setSupportedPerFrameMetadata(
+            int32_t supportedPerFrameMetadata) {
+        mArgs.supportedPerFrameMetadata = supportedPerFrameMetadata;
+        return *this;
+    }
+    DisplayColorProfileCreationArgsBuilder& setHwcColorModes(
+            DisplayColorProfileCreationArgs::HwcColorModes&& hwcColorModes) {
+        mArgs.hwcColorModes = std::move(hwcColorModes);
+        return *this;
+    }
+
+private:
+    DisplayColorProfileCreationArgs mArgs;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
new file mode 100644
index 0000000..0b6b4e4
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <optional>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+
+/**
+ * A parameter object for creating Display instances
+ */
+struct DisplayCreationArgs {
+    // True if this display is secure
+    bool isSecure = false;
+
+    // True if this display is a virtual display
+    bool isVirtual = false;
+
+    // Identifies the display to the HWC, if composition is supported by it
+    std::optional<DisplayId> displayId;
+};
+
+/**
+ * A helper for setting up a DisplayCreationArgs value in-line.
+ * Prefer this builder over raw structure initialization.
+ *
+ * Instead of:
+ *
+ *   DisplayCreationArgs{false, false, displayId}
+ *
+ * Prefer:
+ *
+ *  DisplayCreationArgsBuilder().setIsSecure(false).setIsVirtual(false)
+ *      .setDisplayId(displayId).build();
+ */
+class DisplayCreationArgsBuilder {
+public:
+    DisplayCreationArgs build() { return std::move(mArgs); }
+
+    DisplayCreationArgsBuilder& setIsSecure(bool isSecure) {
+        mArgs.isSecure = isSecure;
+        return *this;
+    }
+    DisplayCreationArgsBuilder& setIsVirtual(bool isVirtual) {
+        mArgs.isVirtual = isVirtual;
+        return *this;
+    }
+    DisplayCreationArgsBuilder& setDisplayId(std::optional<DisplayId> displayId) {
+        mArgs.displayId = displayId;
+        return *this;
+    }
+
+private:
+    DisplayCreationArgs mArgs;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/DisplayHardware/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
similarity index 80%
rename from services/surfaceflinger/DisplayHardware/DisplaySurface.h
rename to services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index f744f5c..0e67acf 100644
--- a/services/surfaceflinger/DisplayHardware/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -14,23 +14,27 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_SF_DISPLAY_SURFACE_H
-#define ANDROID_SF_DISPLAY_SURFACE_H
+#pragma once
 
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 #include <utils/StrongPointer.h>
 
-// ---------------------------------------------------------------------------
 namespace android {
-// ---------------------------------------------------------------------------
 
 class Fence;
 class IGraphicBufferProducer;
 class String8;
 
+namespace compositionengine {
+
+/**
+ * An abstraction for working with a display surface (buffer queue)
+ */
 class DisplaySurface : public virtual RefBase {
 public:
+    virtual ~DisplaySurface();
+
     // beginFrame is called at the beginning of the composition loop, before
     // the configuration is known. The DisplaySurface should do anything it
     // needs to do to enable HWComposer to decide how to compose the frame.
@@ -44,9 +48,9 @@
     // GLES and HWC for this frame.
     enum CompositionType {
         COMPOSITION_UNKNOWN = 0,
-        COMPOSITION_GLES    = 1,
-        COMPOSITION_HWC     = 2,
-        COMPOSITION_MIXED   = COMPOSITION_GLES | COMPOSITION_HWC
+        COMPOSITION_GLES = 1,
+        COMPOSITION_HWC = 2,
+        COMPOSITION_MIXED = COMPOSITION_GLES | COMPOSITION_HWC
     };
     virtual status_t prepareFrame(CompositionType compositionType) = 0;
 
@@ -70,15 +74,7 @@
     virtual void resizeBuffers(const uint32_t w, const uint32_t h) = 0;
 
     virtual const sp<Fence>& getClientTargetAcquireFence() const = 0;
-
-protected:
-    DisplaySurface() {}
-    virtual ~DisplaySurface() {}
 };
 
-// ---------------------------------------------------------------------------
+} // namespace compositionengine
 } // namespace android
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_SF_DISPLAY_SURFACE_H
-
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
new file mode 100644
index 0000000..2d00f0c
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <string>
+
+#include <math/mat4.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android::compositionengine {
+
+class DisplayColorProfile;
+class RenderSurface;
+
+namespace impl {
+struct OutputCompositionState;
+} // namespace impl
+
+/**
+ * Encapsulates all the state involved with composing layers for an output
+ */
+class Output {
+public:
+    // Returns true if the output is valid. This is meant to be checked post-
+    // construction and prior to use, as not everything is set up by the
+    // constructor.
+    virtual bool isValid() const = 0;
+
+    // Enables (or disables) composition on this output
+    virtual void setCompositionEnabled(bool) = 0;
+
+    // Sets the projection state to use
+    virtual void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
+                               const Rect& viewport, const Rect& scissor, bool needsFiltering) = 0;
+    // Sets the bounds to use
+    virtual void setBounds(const ui::Size&) = 0;
+
+    // Sets the layer stack filter for this output. If singleLayerStack is true,
+    // this output displays just the single layer stack specified by
+    // singleLayerStackId. Otherwise all layer stacks will be visible on this
+    // output.
+    virtual void setLayerStackFilter(bool singleLayerStack, uint32_t singleLayerStackId) = 0;
+
+    // Sets the color transform matrix to use
+    virtual void setColorTransform(const mat4&) = 0;
+
+    // Sets the output color mode
+    virtual void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) = 0;
+
+    // Outputs a string with a state dump
+    virtual void dump(std::string&) const = 0;
+
+    // Gets the debug name for the output
+    virtual const std::string& getName() const = 0;
+
+    // Sets a debug name for the output
+    virtual void setName(const std::string&) = 0;
+
+    // Gets the current render color mode for the output
+    virtual DisplayColorProfile* getDisplayColorProfile() const = 0;
+
+    // Gets the current render surface for the output
+    virtual RenderSurface* getRenderSurface() const = 0;
+
+    using OutputCompositionState = compositionengine::impl::OutputCompositionState;
+
+    // Gets the raw composition state data for the output
+    // TODO(lpique): Make this protected once it is only internally called.
+    virtual const OutputCompositionState& getState() const = 0;
+
+    // Allows mutable access to the raw composition state data for the output.
+    // This is meant to be used by the various functions that are part of the
+    // composition process.
+    // TODO(lpique): Make this protected once it is only internally called.
+    virtual OutputCompositionState& editState() = 0;
+
+    // Gets the physical space dirty region. If repaintEverything is true, this
+    // will be the full display bounds. Internally the dirty region is stored in
+    // logical (aka layer stack) space.
+    virtual Region getPhysicalSpaceDirtyRegion(bool repaintEverything) const = 0;
+
+    // Tests whether a given layerStackId belongs in this output
+    virtual bool belongsInOutput(uint32_t layerStackId) const = 0;
+
+protected:
+    ~Output() = default;
+
+    virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
+    virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
new file mode 100644
index 0000000..dd01b05
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+#include <ui/Fence.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Size.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class GraphicBuffer;
+
+struct CompositionInfo;
+
+namespace compositionengine {
+
+/**
+ * Encapsulates everything for composing to a render surface with RenderEngine
+ */
+class RenderSurface {
+public:
+    virtual ~RenderSurface();
+
+    // Returns true if the render surface is valid. This is meant to be checked
+    // post-construction and prior to use, as not everything is set up by the
+    // constructor.
+    virtual bool isValid() const = 0;
+
+    // Performs one-time initialization of the render surface. This is meant
+    // to be called after the validation check.
+    virtual void initialize() = 0;
+
+    // Returns the bounds of the surface
+    virtual const ui::Size& getSize() const = 0;
+
+    // Gets the latest fence to pass to the HWC to signal that the surface
+    // buffer is done rendering
+    virtual const sp<Fence>& getClientTargetAcquireFence() const = 0;
+
+    // Sets the size of the surface
+    virtual void setDisplaySize(const ui::Size&) = 0;
+
+    // Sets the dataspace used for rendering the surface
+    virtual void setBufferDataspace(ui::Dataspace) = 0;
+
+    // Configures the protected rendering on the surface
+    virtual void setProtected(bool useProtected) = 0;
+
+    // Called to signal that rendering has started. 'mustRecompose' should be
+    // true if the entire frame must be recomposed.
+    virtual status_t beginFrame(bool mustRecompose) = 0;
+
+    // Prepares the frame for rendering
+    virtual status_t prepareFrame(std::vector<CompositionInfo>& compositionData) = 0;
+
+    // Allocates a buffer as scratch space for GPU composition
+    virtual sp<GraphicBuffer> dequeueBuffer() = 0;
+
+    // Queues the drawn buffer for consumption by HWC
+    virtual void queueBuffer() = 0;
+
+    // Called after the HWC calls are made to present the display
+    virtual void onPresentDisplayCompleted() = 0;
+
+    // Marks the current buffer has finished, so that it can be presented and
+    // swapped out
+    virtual void finishBuffer() = 0;
+
+    // Called to set the viewport and projection state for rendering into this
+    // surface
+    virtual void setViewportAndProjection() = 0;
+
+    // Called after the surface has been rendering to signal the surface should
+    // be made ready for displaying
+    virtual void flip() = 0;
+
+    // Debugging - Dumps the state of the RenderSurface to a string
+    virtual void dump(std::string& result) const = 0;
+
+    // Debugging - gets the page flip count for the RenderSurface
+    virtual std::uint32_t getPageFlipCount() const = 0;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
new file mode 100644
index 0000000..a1230b3
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurfaceCreationArgs.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <memory>
+
+#include <compositionengine/DisplaySurface.h>
+#include <utils/StrongPointer.h>
+
+struct ANativeWindow;
+
+namespace android {
+
+namespace compositionengine {
+
+class Display;
+
+/**
+ * A parameter object for creating RenderSurface instances
+ */
+struct RenderSurfaceCreationArgs {
+    // The initial width of the surface
+    int32_t displayWidth;
+
+    // The initial height of the surface
+    int32_t displayHeight;
+
+    // The ANativeWindow for the buffer queue for this surface
+    sp<ANativeWindow> nativeWindow;
+
+    // The DisplaySurface for this surface
+    sp<DisplaySurface> displaySurface;
+};
+
+/**
+ * A helper for setting up a RenderSurfaceCreationArgs value in-line.
+ * Prefer this builder over raw structure initialization.
+ *
+ * Instead of:
+ *
+ *   RenderSurfaceCreationArgs{1000, 1000, nativeWindow, displaySurface}
+ *
+ * Prefer:
+ *
+ *  RenderSurfaceCreationArgsBuilder().setDisplayWidth(1000).setDisplayHeight(1000)
+ *      .setNativeWindow(nativeWindow).setDisplaySurface(displaySurface).Build();
+ */
+class RenderSurfaceCreationArgsBuilder {
+public:
+    RenderSurfaceCreationArgs build() { return std::move(mArgs); }
+
+    RenderSurfaceCreationArgsBuilder& setDisplayWidth(int32_t displayWidth) {
+        mArgs.displayWidth = displayWidth;
+        return *this;
+    }
+    RenderSurfaceCreationArgsBuilder& setDisplayHeight(int32_t displayHeight) {
+        mArgs.displayHeight = displayHeight;
+        return *this;
+    }
+    RenderSurfaceCreationArgsBuilder& setNativeWindow(sp<ANativeWindow> nativeWindow) {
+        mArgs.nativeWindow = nativeWindow;
+        return *this;
+    }
+    RenderSurfaceCreationArgsBuilder& setDisplaySurface(sp<DisplaySurface> displaySurface) {
+        mArgs.displaySurface = displaySurface;
+        return *this;
+    }
+
+private:
+    RenderSurfaceCreationArgs mArgs;
+};
+
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index 86d1774..e23c431 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -25,6 +25,9 @@
     CompositionEngine();
     ~CompositionEngine() override;
 
+    std::shared_ptr<compositionengine::Display> createDisplay(
+            compositionengine::DisplayCreationArgs&&) override;
+
     HWComposer& getHwComposer() const override;
     void setHwComposer(std::unique_ptr<HWComposer>) override;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
new file mode 100644
index 0000000..0e20c43
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <compositionengine/Display.h>
+#include <compositionengine/impl/Output.h>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+
+struct DisplayCreationArgs;
+
+namespace impl {
+
+class Display : public compositionengine::impl::Output, public compositionengine::Display {
+public:
+    Display(const CompositionEngine&, compositionengine::DisplayCreationArgs&&);
+    virtual ~Display();
+
+    // compositionengine::Output overrides
+    void dump(std::string&) const override;
+    void setColorTransform(const mat4&) override;
+    void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
+
+    // compositionengine::Display overrides
+    const std::optional<DisplayId>& getId() const override;
+    bool isSecure() const override;
+    bool isVirtual() const override;
+    void disconnect() override;
+    void createDisplayColorProfile(compositionengine::DisplayColorProfileCreationArgs&&) override;
+    void createRenderSurface(compositionengine::RenderSurfaceCreationArgs&&) override;
+
+private:
+    const bool mIsVirtual;
+    std::optional<DisplayId> mId;
+};
+
+std::shared_ptr<compositionengine::Display> createDisplay(
+        const compositionengine::CompositionEngine&, compositionengine::DisplayCreationArgs&&);
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
new file mode 100644
index 0000000..49c2d2c
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <unordered_map>
+#include <vector>
+
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <ui/HdrCapabilities.h>
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+class Display;
+
+namespace impl {
+
+class DisplayColorProfile : public compositionengine::DisplayColorProfile {
+public:
+    DisplayColorProfile(DisplayColorProfileCreationArgs&&);
+    ~DisplayColorProfile() override;
+
+    bool isValid() const override;
+
+    bool hasRenderIntent(ui::RenderIntent intent) const override;
+    bool hasLegacyHdrSupport(ui::Dataspace dataspace) const override;
+    void getBestColorMode(ui::Dataspace dataspace, ui::RenderIntent intent,
+                          ui::Dataspace* outDataspace, ui::ColorMode* outMode,
+                          ui::RenderIntent* outIntent) const override;
+
+    bool hasWideColorGamut() const override;
+    int32_t getSupportedPerFrameMetadata() const override;
+
+    // Whether h/w composer has native support for specific HDR type.
+    bool hasHDR10PlusSupport() const override;
+    bool hasHDR10Support() const override;
+    bool hasHLGSupport() const override;
+    bool hasDolbyVisionSupport() const override;
+
+    const HdrCapabilities& getHdrCapabilities() const override;
+
+    void dump(std::string&) const override;
+
+private:
+    void populateColorModes(const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes);
+    void addColorMode(const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes,
+                      const ui::ColorMode mode, const ui::RenderIntent intent);
+
+    // Mappings from desired Dataspace/RenderIntent to the supported
+    // Dataspace/ColorMode/RenderIntent.
+    using ColorModeKey = uint64_t;
+    struct ColorModeValue {
+        ui::Dataspace dataspace;
+        ui::ColorMode colorMode;
+        ui::RenderIntent renderIntent;
+    };
+
+    static ColorModeKey getColorModeKey(ui::Dataspace dataspace, ui::RenderIntent intent) {
+        return (static_cast<uint64_t>(dataspace) << 32) | static_cast<uint32_t>(intent);
+    }
+
+    // Need to know if display is wide-color capable or not.
+    // Initialized by SurfaceFlinger when the DisplayDevice is created.
+    // Fed to RenderEngine during composition.
+    bool mHasWideColorGamut{false};
+    int32_t mSupportedPerFrameMetadata{0};
+    bool mHasHdr10Plus{false};
+    bool mHasHdr10{false};
+    bool mHasHLG{false};
+    bool mHasDolbyVision{false};
+    HdrCapabilities mHdrCapabilities;
+    std::unordered_map<ColorModeKey, ColorModeValue> mColorModes;
+};
+
+std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile(
+        DisplayColorProfileCreationArgs&&);
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
new file mode 100644
index 0000000..782c8d7
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <string>
+#include <type_traits>
+
+#include <math/mat4.h>
+#include <ui/FloatRect.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android::compositionengine::impl {
+
+void dumpVal(std::string& out, const char* name, bool);
+void dumpVal(std::string& out, const char* name, const void*);
+void dumpVal(std::string& out, const char* name, int);
+void dumpVal(std::string& out, const char* name, float);
+void dumpVal(std::string& out, const char* name, uint32_t);
+void dumpHex(std::string& out, const char* name, uint64_t);
+void dumpVal(std::string& out, const char* name, const char* value);
+void dumpVal(std::string& out, const char* name, const std::string& value);
+
+// For enums with named values
+void dumpVal(std::string& out, const char* name, const char*, int);
+void dumpVal(std::string& out, const char* name, const std::string&, int);
+
+template <typename EnumType>
+void dumpVal(std::string& out, const char* name, const char* valueName, EnumType value) {
+    dumpVal(out, name, valueName, static_cast<std::underlying_type_t<EnumType>>(value));
+}
+
+template <typename EnumType>
+void dumpVal(std::string& out, const char* name, const std::string& valueName, EnumType value) {
+    dumpVal(out, name, valueName, static_cast<std::underlying_type_t<EnumType>>(value));
+}
+
+void dumpVal(std::string& out, const char* name, const FloatRect& rect);
+void dumpVal(std::string& out, const char* name, const Rect& rect);
+void dumpVal(std::string& out, const char* name, const Region& region);
+void dumpVal(std::string& out, const char* name, const ui::Transform&);
+void dumpVal(std::string& out, const char* name, const ui::Size&);
+
+void dumpVal(std::string& out, const char* name, const mat4& tr);
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
new file mode 100644
index 0000000..521e1d7
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <compositionengine/Output.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+
+namespace impl {
+
+class Output : public virtual compositionengine::Output {
+public:
+    Output(const CompositionEngine&);
+    virtual ~Output();
+
+    bool isValid() const override;
+
+    void setCompositionEnabled(bool) override;
+    void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
+                       const Rect& viewport, const Rect& scissor, bool needsFiltering) override;
+    void setBounds(const ui::Size&) override;
+    void setLayerStackFilter(bool singleLayerStack, uint32_t singleLayerStackId) override;
+
+    void setColorTransform(const mat4&) override;
+    void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
+
+    void dump(std::string&) const override;
+
+    const std::string& getName() const override;
+    void setName(const std::string&) override;
+
+    compositionengine::DisplayColorProfile* getDisplayColorProfile() const override;
+    void setDisplayColorProfile(std::unique_ptr<compositionengine::DisplayColorProfile>) override;
+
+    compositionengine::RenderSurface* getRenderSurface() const override;
+    void setRenderSurface(std::unique_ptr<compositionengine::RenderSurface>) override;
+
+    const OutputCompositionState& getState() const override;
+    OutputCompositionState& editState() override;
+
+    Region getPhysicalSpaceDirtyRegion(bool repaintEverything) const override;
+    bool belongsInOutput(uint32_t) const override;
+
+    // Testing
+    void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
+    void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
+
+protected:
+    const CompositionEngine& getCompositionEngine() const;
+    void dumpBase(std::string&) const;
+
+private:
+    void dirtyEntireOutput();
+
+    const CompositionEngine& mCompositionEngine;
+
+    std::string mName;
+
+    OutputCompositionState mState;
+
+    std::unique_ptr<compositionengine::DisplayColorProfile> mDisplayColorProfile;
+    std::unique_ptr<compositionengine::RenderSurface> mRenderSurface;
+};
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
new file mode 100644
index 0000000..96a0e0f
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android {
+
+namespace compositionengine::impl {
+
+struct OutputCompositionState {
+    // If false, composition will not per performed for this display
+    bool isEnabled{false};
+
+    // If false, this output is not considered secure
+    bool isSecure{false};
+
+    // If true, only layer stacks with a matching layerStack value will be
+    // displayed. If false, all layer stacks will be displayed.
+    bool singleLayerStack{true};
+
+    // The layer stack to display on this display
+    uint32_t singleLayerStackId{~0u};
+
+    // The physical space screen bounds
+    Rect bounds;
+
+    // The logical to physical transformation to use
+    ui::Transform transform;
+
+    // The physical orientation of the display, expressed as ui::Transform
+    // orientation flags.
+    uint32_t orientation{0};
+
+    // The logical space user visible bounds
+    Rect frame;
+
+    // The logical space user viewport rectangle
+    Rect viewport;
+
+    // The physical space scissor rectangle
+    Rect scissor;
+
+    // If true, RenderEngine filtering should be enabled
+    bool needsFiltering{false};
+
+    // The logical coordinates for the dirty region for the display.
+    // dirtyRegion is semi-persistent state. Dirty rectangles are added to it
+    // by the FE until composition happens, at which point it is cleared.
+    Region dirtyRegion;
+
+    // The logical coordinates for the undefined region for the display.
+    // The undefined region is internal to the composition engine. It is
+    // updated every time the geometry changes.
+    Region undefinedRegion;
+
+    // True if the last composition frame had visible layers
+    bool lastCompositionHadVisibleLayers{false};
+
+    // The color transform to apply
+    android_color_transform_t colorTransform{HAL_COLOR_TRANSFORM_IDENTITY};
+
+    // Current active color mode
+    ui::ColorMode colorMode{ui::ColorMode::NATIVE};
+
+    // Current active render intent
+    ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC};
+
+    // Current active dstaspace
+    ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+
+    // Debugging
+    void dump(std::string& result) const;
+};
+
+} // namespace compositionengine::impl
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
new file mode 100644
index 0000000..0489310
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <android-base/unique_fd.h>
+#include <compositionengine/RenderSurface.h>
+#include <utils/StrongPointer.h>
+
+struct ANativeWindow;
+
+namespace android {
+
+namespace compositionengine {
+
+class CompositionEngine;
+class Display;
+class DisplaySurface;
+
+struct RenderSurfaceCreationArgs;
+
+namespace impl {
+
+class RenderSurface : public compositionengine::RenderSurface {
+public:
+    RenderSurface(const CompositionEngine&, compositionengine::Display&,
+                  compositionengine::RenderSurfaceCreationArgs&&);
+    ~RenderSurface() override;
+
+    bool isValid() const override;
+    void initialize() override;
+    const ui::Size& getSize() const override;
+
+    const sp<Fence>& getClientTargetAcquireFence() const override;
+    void setBufferDataspace(ui::Dataspace) override;
+    void setDisplaySize(const ui::Size&) override;
+    void setProtected(bool useProtected) override;
+    status_t beginFrame(bool mustRecompose) override;
+    status_t prepareFrame(std::vector<CompositionInfo>& compositionData) override;
+    sp<GraphicBuffer> dequeueBuffer() override;
+    void queueBuffer() override;
+    void onPresentDisplayCompleted() override;
+    void finishBuffer() override;
+    void setViewportAndProjection() override;
+    void flip() override;
+
+    // Debugging
+    void dump(std::string& result) const override;
+    std::uint32_t getPageFlipCount() const override;
+
+    // Testing
+    void setPageFlipCountForTest(std::uint32_t);
+    void setSizeForTest(const ui::Size&);
+    sp<GraphicBuffer>& mutableGraphicBufferForTest();
+    base::unique_fd& mutableBufferReadyForTest();
+
+private:
+    const compositionengine::CompositionEngine& mCompositionEngine;
+    const compositionengine::Display& mDisplay;
+
+    // ANativeWindow being rendered into
+    const sp<ANativeWindow> mNativeWindow;
+    // Current buffer being rendered into
+    sp<GraphicBuffer> mGraphicBuffer;
+    // File descriptor indicating that mGraphicBuffer is ready for display, i.e.
+    // that drawing to the buffer is now complete.
+    base::unique_fd mBufferReady;
+    const sp<DisplaySurface> mDisplaySurface;
+    ui::Size mSize;
+    std::uint32_t mPageFlipCount{0};
+};
+
+std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
+        const compositionengine::CompositionEngine&, compositionengine::Display&,
+        compositionengine::RenderSurfaceCreationArgs&&);
+
+} // namespace impl
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index 9ba213e..1967666 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/DisplayCreationArgs.h>
 #include <gmock/gmock.h>
 #include <renderengine/RenderEngine.h>
 
@@ -29,6 +30,8 @@
     CompositionEngine();
     ~CompositionEngine() override;
 
+    MOCK_METHOD1(createDisplay, std::shared_ptr<Display>(DisplayCreationArgs&&));
+
     MOCK_CONST_METHOD0(getHwComposer, HWComposer&());
     MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>));
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
new file mode 100644
index 0000000..d763aa6
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/mock/Output.h>
+#include <gmock/gmock.h>
+#include <system/window.h>
+
+#include "DisplayHardware/DisplayIdentification.h"
+
+namespace android::compositionengine::mock {
+
+class Display : public compositionengine::mock::Output, public compositionengine::Display {
+public:
+    Display();
+    virtual ~Display();
+
+    MOCK_CONST_METHOD0(getId, const std::optional<DisplayId>&());
+    MOCK_CONST_METHOD0(isSecure, bool());
+    MOCK_CONST_METHOD0(isVirtual, bool());
+
+    MOCK_METHOD0(disconnect, void());
+
+    MOCK_METHOD1(createDisplayColorProfile, void(DisplayColorProfileCreationArgs&&));
+    MOCK_METHOD1(createRenderSurface, void(RenderSurfaceCreationArgs&&));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
new file mode 100644
index 0000000..8056c9d
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <compositionengine/DisplayColorProfile.h>
+#include <gmock/gmock.h>
+#include <ui/HdrCapabilities.h>
+
+namespace android::compositionengine::mock {
+
+class DisplayColorProfile : public compositionengine::DisplayColorProfile {
+public:
+    DisplayColorProfile();
+    ~DisplayColorProfile() override;
+
+    MOCK_CONST_METHOD0(isValid, bool());
+
+    MOCK_CONST_METHOD1(hasRenderIntent, bool(ui::RenderIntent));
+    MOCK_CONST_METHOD1(hasLegacyHdrSupport, bool(ui::Dataspace));
+    MOCK_CONST_METHOD5(getBestColorMode,
+                       void(ui::Dataspace, ui::RenderIntent, ui::Dataspace*, ui::ColorMode*,
+                            ui::RenderIntent*));
+    MOCK_CONST_METHOD0(hasWideColorGamut, bool());
+    MOCK_CONST_METHOD0(getSupportedPerFrameMetadata, int32_t());
+    MOCK_CONST_METHOD0(hasHDR10PlusSupport, bool());
+    MOCK_CONST_METHOD0(hasHDR10Support, bool());
+    MOCK_CONST_METHOD0(hasHLGSupport, bool());
+    MOCK_CONST_METHOD0(hasDolbyVisionSupport, bool());
+
+    MOCK_CONST_METHOD0(getHdrCapabilities, const HdrCapabilities&());
+
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
similarity index 81%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.h
rename to services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
index d6c9aa4..31b5f95 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplaySurface.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,16 +16,13 @@
 
 #pragma once
 
+#include <compositionengine/DisplaySurface.h>
 #include <gmock/gmock.h>
-
 #include <utils/String8.h>
 
-#include "DisplayHardware/DisplaySurface.h"
+namespace android::compositionengine::mock {
 
-namespace android {
-namespace mock {
-
-class DisplaySurface : public android::DisplaySurface {
+class DisplaySurface : public compositionengine::DisplaySurface {
 public:
     DisplaySurface();
     ~DisplaySurface() override;
@@ -39,5 +36,4 @@
     MOCK_CONST_METHOD0(getClientTargetAcquireFence, const sp<Fence>&());
 };
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
new file mode 100644
index 0000000..37f7007
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/Output.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <gmock/gmock.h>
+
+namespace android::compositionengine::mock {
+
+class Output : public virtual compositionengine::Output {
+public:
+    Output();
+    virtual ~Output();
+
+    MOCK_CONST_METHOD0(isValid, bool());
+
+    MOCK_METHOD1(setCompositionEnabled, void(bool));
+    MOCK_METHOD6(setProjection,
+                 void(const ui::Transform&, int32_t, const Rect&, const Rect&, const Rect&, bool));
+    MOCK_METHOD1(setBounds, void(const ui::Size&));
+    MOCK_METHOD2(setLayerStackFilter, void(bool, uint32_t));
+
+    MOCK_METHOD1(setColorTransform, void(const mat4&));
+    MOCK_METHOD3(setColorMode, void(ui::ColorMode, ui::Dataspace, ui::RenderIntent));
+
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+    MOCK_CONST_METHOD0(getName, const std::string&());
+    MOCK_METHOD1(setName, void(const std::string&));
+
+    MOCK_CONST_METHOD0(getDisplayColorProfile, DisplayColorProfile*());
+    MOCK_METHOD1(setDisplayColorProfile, void(std::unique_ptr<DisplayColorProfile>));
+
+    MOCK_CONST_METHOD0(getRenderSurface, RenderSurface*());
+    MOCK_METHOD1(setRenderSurface, void(std::unique_ptr<RenderSurface>));
+
+    MOCK_CONST_METHOD0(getState, const OutputCompositionState&());
+    MOCK_METHOD0(editState, OutputCompositionState&());
+
+    MOCK_CONST_METHOD1(getPhysicalSpaceDirtyRegion, Region(bool));
+    MOCK_CONST_METHOD1(belongsInOutput, bool(uint32_t));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
new file mode 100644
index 0000000..2269e57
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <compositionengine/RenderSurface.h>
+#include <gmock/gmock.h>
+#include <ui/GraphicBuffer.h>
+
+#include "LayerBE.h"
+
+namespace android::compositionengine::mock {
+
+class RenderSurface : public compositionengine::RenderSurface {
+public:
+    RenderSurface();
+    ~RenderSurface() override;
+
+    MOCK_CONST_METHOD0(isValid, bool());
+    MOCK_METHOD0(initialize, void());
+    MOCK_CONST_METHOD0(getSize, const ui::Size&());
+    MOCK_CONST_METHOD0(getClientTargetAcquireFence, const sp<Fence>&());
+    MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
+    MOCK_METHOD1(setProtected, void(bool));
+    MOCK_METHOD1(setBufferDataspace, void(ui::Dataspace));
+    MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
+    MOCK_METHOD1(prepareFrame, status_t(std::vector<CompositionInfo>& compositionData));
+    MOCK_METHOD0(dequeueBuffer, sp<GraphicBuffer>());
+    MOCK_METHOD0(queueBuffer, void());
+    MOCK_METHOD0(onPresentDisplayCompleted, void());
+    MOCK_METHOD0(finishBuffer, void());
+    MOCK_METHOD0(setViewportAndProjection, void());
+    MOCK_METHOD0(flip, void());
+    MOCK_CONST_METHOD1(dump, void(std::string& result));
+    MOCK_CONST_METHOD0(getPageFlipCount, std::uint32_t());
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/Display.cpp
similarity index 60%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/Display.cpp
index e6ac6bf..01cf112 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/Display.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/Display.h>
 
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Display::Display() = default;
+Display::~Display() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/DisplayColorProfile.cpp
similarity index 66%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/DisplayColorProfile.cpp
index e6ac6bf..581d1f7 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/DisplayColorProfile.cpp
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
- *
+ * Copyright 2019 The Android Open Source Project
+
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/DisplayColorProfile.h>
 
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
 
 // Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+DisplayColorProfile::DisplayColorProfile() = default;
+DisplayColorProfile::~DisplayColorProfile() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/DisplaySurface.cpp
similarity index 67%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/DisplaySurface.cpp
index e6ac6bf..bbbd7c1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/DisplaySurface.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include "compositionengine/mock/DisplaySurface.h"
 
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
 
-// Explicit default instantiation is recommended.
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
 DisplaySurface::DisplaySurface() = default;
 DisplaySurface::~DisplaySurface() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/Output.cpp
similarity index 60%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/Output.cpp
index e6ac6bf..44df4c3 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/Output.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/Output.h>
 
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Output::Output() = default;
+Output::~Output() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/mock/RenderSurface.cpp
similarity index 68%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
copy to services/surfaceflinger/CompositionEngine/mock/RenderSurface.cpp
index e6ac6bf..fe718d6 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/mock/RenderSurface.cpp
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
- *
+ * Copyright 2019 The Android Open Source Project
+
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/mock/RenderSurface.h>
 
-namespace android {
-namespace mock {
+namespace android::compositionengine::mock {
 
 // Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
-DisplaySurface::~DisplaySurface() = default;
+RenderSurface::RenderSurface() = default;
+RenderSurface::~RenderSurface() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index fbf71b5..127d729 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <compositionengine/impl/CompositionEngine.h>
+#include <compositionengine/impl/Display.h>
 #include <renderengine/RenderEngine.h>
 
 #include "DisplayHardware/HWComposer.h"
@@ -32,6 +33,11 @@
 CompositionEngine::CompositionEngine() = default;
 CompositionEngine::~CompositionEngine() = default;
 
+std::shared_ptr<compositionengine::Display> CompositionEngine::createDisplay(
+        DisplayCreationArgs&& args) {
+    return compositionengine::impl::createDisplay(*this, std::move(args));
+}
+
 HWComposer& CompositionEngine::getHwComposer() const {
     return *mHwComposer.get();
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
new file mode 100644
index 0000000..f9d70e3
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/stringprintf.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
+#include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/RenderSurface.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine::impl {
+
+std::shared_ptr<compositionengine::Display> createDisplay(
+        const compositionengine::CompositionEngine& compositionEngine,
+        compositionengine::DisplayCreationArgs&& args) {
+    return std::make_shared<Display>(compositionEngine, std::move(args));
+}
+
+Display::Display(const CompositionEngine& compositionEngine, DisplayCreationArgs&& args)
+      : compositionengine::impl::Output(compositionEngine),
+        mIsVirtual(args.isVirtual),
+        mId(args.displayId) {
+    editState().isSecure = args.isSecure;
+}
+
+Display::~Display() = default;
+
+const std::optional<DisplayId>& Display::getId() const {
+    return mId;
+}
+
+bool Display::isSecure() const {
+    return getState().isSecure;
+}
+
+bool Display::isVirtual() const {
+    return mIsVirtual;
+}
+
+void Display::disconnect() {
+    if (!mId) {
+        return;
+    }
+
+    auto& hwc = getCompositionEngine().getHwComposer();
+    hwc.disconnectDisplay(*mId);
+    mId.reset();
+}
+
+void Display::setColorTransform(const mat4& transform) {
+    Output::setColorTransform(transform);
+
+    auto& hwc = getCompositionEngine().getHwComposer();
+    status_t result = hwc.setColorTransform(*mId, transform);
+    ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d",
+             mId ? to_string(*mId).c_str() : "", result);
+}
+
+void Display::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
+                           ui::RenderIntent renderIntent) {
+    if (mode == getState().colorMode && dataspace == getState().dataspace &&
+        renderIntent == getState().renderIntent) {
+        return;
+    }
+
+    if (mIsVirtual) {
+        ALOGW("%s: Invalid operation on virtual display", __FUNCTION__);
+        return;
+    }
+
+    Output::setColorMode(mode, dataspace, renderIntent);
+
+    auto& hwc = getCompositionEngine().getHwComposer();
+    hwc.setActiveColorMode(*mId, mode, renderIntent);
+}
+
+void Display::dump(std::string& out) const {
+    using android::base::StringAppendF;
+
+    StringAppendF(&out, "   Composition Display State: [\"%s\"]", getName().c_str());
+
+    out.append("\n   ");
+
+    dumpVal(out, "isVirtual", mIsVirtual);
+    if (mId) {
+        dumpVal(out, "hwcId", to_string(*mId));
+    } else {
+        StringAppendF(&out, "no hwcId, ");
+    }
+
+    out.append("\n");
+
+    Output::dumpBase(out);
+}
+
+void Display::createDisplayColorProfile(DisplayColorProfileCreationArgs&& args) {
+    setDisplayColorProfile(compositionengine::impl::createDisplayColorProfile(std::move(args)));
+}
+
+void Display::createRenderSurface(RenderSurfaceCreationArgs&& args) {
+    setRenderSurface(compositionengine::impl::createRenderSurface(getCompositionEngine(), *this,
+                                                                  std::move(args)));
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
new file mode 100644
index 0000000..6e6f3c0
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <array>
+#include <unordered_set>
+
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
+#include <compositionengine/impl/DumpHelpers.h>
+#include <log/log.h>
+#include <ui/DebugUtils.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine {
+
+DisplayColorProfile::~DisplayColorProfile() = default;
+
+namespace impl {
+namespace {
+
+using ui::ColorMode;
+using ui::Dataspace;
+using ui::RenderIntent;
+
+// ordered list of known SDR color modes
+const std::array<ColorMode, 3> sSdrColorModes = {
+        ColorMode::DISPLAY_BT2020,
+        ColorMode::DISPLAY_P3,
+        ColorMode::SRGB,
+};
+
+// ordered list of known HDR color modes
+const std::array<ColorMode, 2> sHdrColorModes = {
+        ColorMode::BT2100_PQ,
+        ColorMode::BT2100_HLG,
+};
+
+// ordered list of known SDR render intents
+const std::array<RenderIntent, 2> sSdrRenderIntents = {
+        RenderIntent::ENHANCE,
+        RenderIntent::COLORIMETRIC,
+};
+
+// ordered list of known HDR render intents
+const std::array<RenderIntent, 2> sHdrRenderIntents = {
+        RenderIntent::TONE_MAP_ENHANCE,
+        RenderIntent::TONE_MAP_COLORIMETRIC,
+};
+
+// map known color mode to dataspace
+Dataspace colorModeToDataspace(ColorMode mode) {
+    switch (mode) {
+        case ColorMode::SRGB:
+            return Dataspace::V0_SRGB;
+        case ColorMode::DISPLAY_P3:
+            return Dataspace::DISPLAY_P3;
+        case ColorMode::DISPLAY_BT2020:
+            return Dataspace::DISPLAY_BT2020;
+        case ColorMode::BT2100_HLG:
+            return Dataspace::BT2020_HLG;
+        case ColorMode::BT2100_PQ:
+            return Dataspace::BT2020_PQ;
+        default:
+            return Dataspace::UNKNOWN;
+    }
+}
+
+// Return a list of candidate color modes.
+std::vector<ColorMode> getColorModeCandidates(ColorMode mode) {
+    std::vector<ColorMode> candidates;
+
+    // add mode itself
+    candidates.push_back(mode);
+
+    // check if mode is HDR
+    bool isHdr = false;
+    for (auto hdrMode : sHdrColorModes) {
+        if (hdrMode == mode) {
+            isHdr = true;
+            break;
+        }
+    }
+
+    // add other HDR candidates when mode is HDR
+    if (isHdr) {
+        for (auto hdrMode : sHdrColorModes) {
+            if (hdrMode != mode) {
+                candidates.push_back(hdrMode);
+            }
+        }
+    }
+
+    // add other SDR candidates
+    for (auto sdrMode : sSdrColorModes) {
+        if (sdrMode != mode) {
+            candidates.push_back(sdrMode);
+        }
+    }
+
+    return candidates;
+}
+
+// Return a list of candidate render intents.
+std::vector<RenderIntent> getRenderIntentCandidates(RenderIntent intent) {
+    std::vector<RenderIntent> candidates;
+
+    // add intent itself
+    candidates.push_back(intent);
+
+    // check if intent is HDR
+    bool isHdr = false;
+    for (auto hdrIntent : sHdrRenderIntents) {
+        if (hdrIntent == intent) {
+            isHdr = true;
+            break;
+        }
+    }
+
+    if (isHdr) {
+        // add other HDR candidates when intent is HDR
+        for (auto hdrIntent : sHdrRenderIntents) {
+            if (hdrIntent != intent) {
+                candidates.push_back(hdrIntent);
+            }
+        }
+    } else {
+        // add other SDR candidates when intent is SDR
+        for (auto sdrIntent : sSdrRenderIntents) {
+            if (sdrIntent != intent) {
+                candidates.push_back(sdrIntent);
+            }
+        }
+    }
+
+    return candidates;
+}
+
+// Return the best color mode supported by HWC.
+ColorMode getHwcColorMode(
+        const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
+        ColorMode mode) {
+    std::vector<ColorMode> candidates = getColorModeCandidates(mode);
+    for (auto candidate : candidates) {
+        auto iter = hwcColorModes.find(candidate);
+        if (iter != hwcColorModes.end()) {
+            return candidate;
+        }
+    }
+
+    return ColorMode::NATIVE;
+}
+
+// Return the best render intent supported by HWC.
+RenderIntent getHwcRenderIntent(const std::vector<RenderIntent>& hwcIntents, RenderIntent intent) {
+    std::vector<RenderIntent> candidates = getRenderIntentCandidates(intent);
+    for (auto candidate : candidates) {
+        for (auto hwcIntent : hwcIntents) {
+            if (candidate == hwcIntent) {
+                return candidate;
+            }
+        }
+    }
+
+    return RenderIntent::COLORIMETRIC;
+}
+
+} // anonymous namespace
+
+std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile(
+        DisplayColorProfileCreationArgs&& args) {
+    return std::make_unique<DisplayColorProfile>(std::move(args));
+}
+
+DisplayColorProfile::DisplayColorProfile(DisplayColorProfileCreationArgs&& args)
+      : mHasWideColorGamut(args.hasWideColorGamut),
+        mSupportedPerFrameMetadata(args.supportedPerFrameMetadata) {
+    populateColorModes(args.hwcColorModes);
+
+    std::vector<ui::Hdr> types = args.hdrCapabilities.getSupportedHdrTypes();
+    for (ui::Hdr hdrType : types) {
+        switch (hdrType) {
+            case ui::Hdr::HDR10_PLUS:
+                mHasHdr10Plus = true;
+                break;
+            case ui::Hdr::HDR10:
+                mHasHdr10 = true;
+                break;
+            case ui::Hdr::HLG:
+                mHasHLG = true;
+                break;
+            case ui::Hdr::DOLBY_VISION:
+                mHasDolbyVision = true;
+                break;
+            default:
+                ALOGE("UNKNOWN HDR capability: %d", static_cast<int32_t>(hdrType));
+        }
+    }
+
+    float minLuminance = args.hdrCapabilities.getDesiredMinLuminance();
+    float maxLuminance = args.hdrCapabilities.getDesiredMaxLuminance();
+    float maxAverageLuminance = args.hdrCapabilities.getDesiredMaxAverageLuminance();
+
+    minLuminance = minLuminance <= 0.0 ? sDefaultMinLumiance : minLuminance;
+    maxLuminance = maxLuminance <= 0.0 ? sDefaultMaxLumiance : maxLuminance;
+    maxAverageLuminance = maxAverageLuminance <= 0.0 ? sDefaultMaxLumiance : maxAverageLuminance;
+    if (args.hasWideColorGamut) {
+        // insert HDR10/HLG as we will force client composition for HDR10/HLG
+        // layers
+        if (!hasHDR10Support()) {
+            types.push_back(ui::Hdr::HDR10);
+        }
+
+        if (!hasHLGSupport()) {
+            types.push_back(ui::Hdr::HLG);
+        }
+    }
+
+    mHdrCapabilities = HdrCapabilities(types, maxLuminance, maxAverageLuminance, minLuminance);
+}
+
+DisplayColorProfile::~DisplayColorProfile() = default;
+
+bool DisplayColorProfile::isValid() const {
+    return true;
+}
+
+bool DisplayColorProfile::hasWideColorGamut() const {
+    return mHasWideColorGamut;
+}
+
+int32_t DisplayColorProfile::getSupportedPerFrameMetadata() const {
+    return mSupportedPerFrameMetadata;
+}
+
+bool DisplayColorProfile::hasHDR10PlusSupport() const {
+    return mHasHdr10Plus;
+}
+
+bool DisplayColorProfile::hasHDR10Support() const {
+    return mHasHdr10;
+}
+
+bool DisplayColorProfile::hasHLGSupport() const {
+    return mHasHLG;
+}
+
+bool DisplayColorProfile::hasDolbyVisionSupport() const {
+    return mHasDolbyVision;
+}
+
+const HdrCapabilities& DisplayColorProfile::getHdrCapabilities() const {
+    return mHdrCapabilities;
+}
+
+void DisplayColorProfile::populateColorModes(
+        const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes) {
+    if (!hasWideColorGamut()) {
+        return;
+    }
+
+    // collect all known SDR render intents
+    std::unordered_set<RenderIntent> sdrRenderIntents(sSdrRenderIntents.begin(),
+                                                      sSdrRenderIntents.end());
+    auto iter = hwcColorModes.find(ColorMode::SRGB);
+    if (iter != hwcColorModes.end()) {
+        for (auto intent : iter->second) {
+            sdrRenderIntents.insert(intent);
+        }
+    }
+
+    // add all known SDR combinations
+    for (auto intent : sdrRenderIntents) {
+        for (auto mode : sSdrColorModes) {
+            addColorMode(hwcColorModes, mode, intent);
+        }
+    }
+
+    // collect all known HDR render intents
+    std::unordered_set<RenderIntent> hdrRenderIntents(sHdrRenderIntents.begin(),
+                                                      sHdrRenderIntents.end());
+    iter = hwcColorModes.find(ColorMode::BT2100_PQ);
+    if (iter != hwcColorModes.end()) {
+        for (auto intent : iter->second) {
+            hdrRenderIntents.insert(intent);
+        }
+    }
+
+    // add all known HDR combinations
+    for (auto intent : sHdrRenderIntents) {
+        for (auto mode : sHdrColorModes) {
+            addColorMode(hwcColorModes, mode, intent);
+        }
+    }
+}
+
+// Map dataspace/intent to the best matched dataspace/colorMode/renderIntent
+// supported by HWC.
+void DisplayColorProfile::addColorMode(
+        const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes, const ColorMode mode,
+        const RenderIntent intent) {
+    // find the best color mode
+    const ColorMode hwcColorMode = getHwcColorMode(hwcColorModes, mode);
+
+    // find the best render intent
+    auto iter = hwcColorModes.find(hwcColorMode);
+    const auto& hwcIntents =
+            iter != hwcColorModes.end() ? iter->second : std::vector<RenderIntent>();
+    const RenderIntent hwcIntent = getHwcRenderIntent(hwcIntents, intent);
+
+    const Dataspace dataspace = colorModeToDataspace(mode);
+    const Dataspace hwcDataspace = colorModeToDataspace(hwcColorMode);
+
+    ALOGV("DisplayColorProfile: map (%s, %s) to (%s, %s, %s)",
+          dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
+          decodeRenderIntent(intent).c_str(),
+          dataspaceDetails(static_cast<android_dataspace_t>(hwcDataspace)).c_str(),
+          decodeColorMode(hwcColorMode).c_str(), decodeRenderIntent(hwcIntent).c_str());
+
+    mColorModes[getColorModeKey(dataspace, intent)] = {hwcDataspace, hwcColorMode, hwcIntent};
+}
+
+bool DisplayColorProfile::hasRenderIntent(RenderIntent intent) const {
+    // assume a render intent is supported when SRGB supports it; we should
+    // get rid of that assumption.
+    auto iter = mColorModes.find(getColorModeKey(Dataspace::V0_SRGB, intent));
+    return iter != mColorModes.end() && iter->second.renderIntent == intent;
+}
+
+bool DisplayColorProfile::hasLegacyHdrSupport(Dataspace dataspace) const {
+    if ((dataspace == Dataspace::BT2020_PQ && hasHDR10Support()) ||
+        (dataspace == Dataspace::BT2020_HLG && hasHLGSupport())) {
+        auto iter =
+                mColorModes.find(getColorModeKey(dataspace, RenderIntent::TONE_MAP_COLORIMETRIC));
+        return iter == mColorModes.end() || iter->second.dataspace != dataspace;
+    }
+
+    return false;
+}
+
+void DisplayColorProfile::getBestColorMode(Dataspace dataspace, RenderIntent intent,
+                                           Dataspace* outDataspace, ColorMode* outMode,
+                                           RenderIntent* outIntent) const {
+    auto iter = mColorModes.find(getColorModeKey(dataspace, intent));
+    if (iter != mColorModes.end()) {
+        *outDataspace = iter->second.dataspace;
+        *outMode = iter->second.colorMode;
+        *outIntent = iter->second.renderIntent;
+    } else {
+        // this is unexpected on a WCG display
+        if (hasWideColorGamut()) {
+            ALOGE("map unknown (%s)/(%s) to default color mode",
+                  dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
+                  decodeRenderIntent(intent).c_str());
+        }
+
+        *outDataspace = Dataspace::UNKNOWN;
+        *outMode = ColorMode::NATIVE;
+        *outIntent = RenderIntent::COLORIMETRIC;
+    }
+}
+
+void DisplayColorProfile::dump(std::string& out) const {
+    out.append("   Composition Display Color State:");
+
+    out.append("\n   HWC Support: ");
+
+    dumpVal(out, "wideColorGamut", hasWideColorGamut());
+    dumpVal(out, "hdr10plus", hasHDR10PlusSupport());
+    dumpVal(out, "hdr10", hasHDR10Support());
+    dumpVal(out, "hlg", hasHLGSupport());
+    dumpVal(out, "dv", hasDolbyVisionSupport());
+    dumpVal(out, "metadata", getSupportedPerFrameMetadata());
+
+    out.append("\n");
+}
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
similarity index 68%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
rename to services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
index e6ac6bf..db6d4f2 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplaySurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplaySurface.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,10 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockDisplaySurface.h"
+#include <compositionengine/DisplaySurface.h>
 
-namespace android {
-namespace mock {
+namespace android::compositionengine {
 
-// Explicit default instantiation is recommended.
-DisplaySurface::DisplaySurface() = default;
 DisplaySurface::~DisplaySurface() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
new file mode 100644
index 0000000..c497013
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cinttypes>
+
+#include <android-base/stringprintf.h>
+#include <compositionengine/impl/DumpHelpers.h>
+
+namespace android::compositionengine::impl {
+
+using android::base::StringAppendF;
+
+void dumpVal(std::string& out, const char* name, bool value) {
+    StringAppendF(&out, "%s=%c ", name, value ? 'T' : 'F');
+}
+
+void dumpVal(std::string& out, const char* name, const void* value) {
+    StringAppendF(&out, "%s=%p ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, int value) {
+    StringAppendF(&out, "%s=%d ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, float value) {
+    StringAppendF(&out, "%s=%f ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, uint32_t value) {
+    StringAppendF(&out, "%s=%u ", name, value);
+}
+
+void dumpHex(std::string& out, const char* name, uint64_t value) {
+    StringAppendF(&out, "%s=0x08%" PRIx64 " ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, const char* value) {
+    StringAppendF(&out, "%s=%s ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, const std::string& value) {
+    dumpVal(out, name, value.c_str());
+}
+
+void dumpVal(std::string& out, const char* name, const char* valueName, int value) {
+    StringAppendF(&out, "%s=%s (%d)", name, valueName, value);
+}
+
+void dumpVal(std::string& out, const char* name, const std::string& valueName, int value) {
+    dumpVal(out, name, valueName.c_str(), value);
+}
+
+void dumpVal(std::string& out, const char* name, const FloatRect& rect) {
+    StringAppendF(&out, "%s=[%f %f %f %f] ", name, rect.left, rect.top, rect.right, rect.bottom);
+}
+
+void dumpVal(std::string& out, const char* name, const Rect& rect) {
+    StringAppendF(&out, "%s=[%d %d %d %d] ", name, rect.left, rect.top, rect.right, rect.bottom);
+}
+
+void dumpVal(std::string& out, const char* name, const Region& region) {
+    region.dump(out, name, 0);
+}
+
+void dumpVal(std::string& out, const char* name, const ui::Transform& transform) {
+    transform.dump(out, name);
+}
+
+void dumpVal(std::string& out, const char* name, const ui::Size& size) {
+    StringAppendF(&out, "%s=[%d %d] ", name, size.width, size.height);
+}
+
+void dumpVal(std::string& out, const char* name, const mat4& tr) {
+    StringAppendF(&out,
+                  "%s=["
+                  /* clang-format off */
+                  "[%0.3f,%0.3f,%0.3f,%0.3f]"
+                  "[%0.3f,%0.3f,%0.3f,%0.3f]"
+                  "[%0.3f,%0.3f,%0.3f,%0.3f]"
+                  "[%0.3f,%0.3f,%0.3f,%0.3f]]",
+                  name,
+                  tr[0][0], tr[1][0], tr[2][0], tr[3][0],
+                  tr[0][1], tr[1][1], tr[2][1], tr[3][1],
+                  tr[0][2], tr[1][2], tr[2][2], tr[3][2],
+                  tr[0][3], tr[1][3], tr[2][3], tr[3][3]
+                  ); /* clang-format on */
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
new file mode 100644
index 0000000..dbf77b0
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/stringprintf.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/Output.h>
+#include <ui/DebugUtils.h>
+
+namespace android::compositionengine::impl {
+
+Output::Output(const CompositionEngine& compositionEngine)
+      : mCompositionEngine(compositionEngine) {}
+
+Output::~Output() = default;
+
+const CompositionEngine& Output::getCompositionEngine() const {
+    return mCompositionEngine;
+}
+
+bool Output::isValid() const {
+    return mDisplayColorProfile && mDisplayColorProfile->isValid() && mRenderSurface &&
+            mRenderSurface->isValid();
+}
+
+const std::string& Output::getName() const {
+    return mName;
+}
+
+void Output::setName(const std::string& name) {
+    mName = name;
+}
+
+void Output::setCompositionEnabled(bool enabled) {
+    if (mState.isEnabled == enabled) {
+        return;
+    }
+
+    mState.isEnabled = enabled;
+    dirtyEntireOutput();
+}
+
+void Output::setProjection(const ui::Transform& transform, int32_t orientation, const Rect& frame,
+                           const Rect& viewport, const Rect& scissor, bool needsFiltering) {
+    mState.transform = transform;
+    mState.orientation = orientation;
+    mState.scissor = scissor;
+    mState.frame = frame;
+    mState.viewport = viewport;
+    mState.needsFiltering = needsFiltering;
+
+    dirtyEntireOutput();
+}
+
+// TODO(lpique): Rename setSize() once more is moved.
+void Output::setBounds(const ui::Size& size) {
+    mRenderSurface->setDisplaySize(size);
+    // TODO(lpique): Rename mState.size once more is moved.
+    mState.bounds = Rect(mRenderSurface->getSize());
+
+    dirtyEntireOutput();
+}
+
+void Output::setLayerStackFilter(bool singleLayerStack, uint32_t singleLayerStackId) {
+    mState.singleLayerStack = singleLayerStack;
+    mState.singleLayerStackId = singleLayerStackId;
+
+    dirtyEntireOutput();
+}
+
+void Output::setColorTransform(const mat4& transform) {
+    const bool isIdentity = (transform == mat4());
+
+    mState.colorTransform =
+            isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
+}
+
+void Output::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
+                          ui::RenderIntent renderIntent) {
+    mState.colorMode = mode;
+    mState.dataspace = dataspace;
+    mState.renderIntent = renderIntent;
+
+    mRenderSurface->setBufferDataspace(dataspace);
+
+    ALOGV("Set active color mode: %s (%d), active render intent: %s (%d)",
+          decodeColorMode(mode).c_str(), mode, decodeRenderIntent(renderIntent).c_str(),
+          renderIntent);
+}
+
+void Output::dump(std::string& out) const {
+    using android::base::StringAppendF;
+
+    StringAppendF(&out, "   Composition Output State: [\"%s\"]", mName.c_str());
+
+    out.append("\n   ");
+
+    dumpBase(out);
+}
+
+void Output::dumpBase(std::string& out) const {
+    mState.dump(out);
+
+    if (mDisplayColorProfile) {
+        mDisplayColorProfile->dump(out);
+    } else {
+        out.append("    No display color profile!\n");
+    }
+
+    if (mRenderSurface) {
+        mRenderSurface->dump(out);
+    } else {
+        out.append("    No render surface!\n");
+    }
+}
+
+compositionengine::DisplayColorProfile* Output::getDisplayColorProfile() const {
+    return mDisplayColorProfile.get();
+}
+
+void Output::setDisplayColorProfile(std::unique_ptr<compositionengine::DisplayColorProfile> mode) {
+    mDisplayColorProfile = std::move(mode);
+}
+
+void Output::setDisplayColorProfileForTest(
+        std::unique_ptr<compositionengine::DisplayColorProfile> mode) {
+    mDisplayColorProfile = std::move(mode);
+}
+
+compositionengine::RenderSurface* Output::getRenderSurface() const {
+    return mRenderSurface.get();
+}
+
+void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
+    mRenderSurface = std::move(surface);
+    mState.bounds = Rect(mRenderSurface->getSize());
+
+    dirtyEntireOutput();
+}
+
+void Output::setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface> surface) {
+    mRenderSurface = std::move(surface);
+}
+
+const OutputCompositionState& Output::getState() const {
+    return mState;
+}
+
+OutputCompositionState& Output::editState() {
+    return mState;
+}
+
+Region Output::getPhysicalSpaceDirtyRegion(bool repaintEverything) const {
+    Region dirty;
+    if (repaintEverything) {
+        dirty.set(mState.bounds);
+    } else {
+        dirty = mState.transform.transform(mState.dirtyRegion);
+        dirty.andSelf(mState.bounds);
+    }
+    return dirty;
+}
+
+bool Output::belongsInOutput(uint32_t layerStackId) const {
+    return !mState.singleLayerStack || (layerStackId == mState.singleLayerStackId);
+}
+
+void Output::dirtyEntireOutput() {
+    mState.dirtyRegion.set(mState.bounds);
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
new file mode 100644
index 0000000..7d8765f
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+
+namespace android::compositionengine::impl {
+
+void OutputCompositionState::dump(std::string& out) const {
+    dumpVal(out, "isEnabled", isEnabled);
+    dumpVal(out, "isSecure", isSecure);
+    if (singleLayerStack) {
+        out.append("layerStack=<any>");
+    } else {
+        dumpVal(out, "layerStack", singleLayerStackId);
+    }
+
+    out.append("\n   ");
+
+    dumpVal(out, "transform", transform);
+
+    out.append("\n   ");
+
+    dumpVal(out, "frame", frame);
+    dumpVal(out, "viewport", viewport);
+    dumpVal(out, "scissor", scissor);
+    dumpVal(out, "needsFiltering", needsFiltering);
+
+    out.append("\n");
+
+    dumpVal(out, "colorMode", toString(colorMode), colorMode);
+    dumpVal(out, "renderIntent", toString(renderIntent), renderIntent);
+    dumpVal(out, "dataspace", toString(dataspace), dataspace);
+    dumpVal(out, "colorTransform", colorTransform);
+
+    out.append("\n");
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
new file mode 100644
index 0000000..d546fc8
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/stringprintf.h>
+#include <android/native_window.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/RenderSurface.h>
+#include <log/log.h>
+#include <renderengine/RenderEngine.h>
+#include <sync/sync.h>
+#include <system/window.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::compositionengine {
+
+RenderSurface::~RenderSurface() = default;
+
+namespace impl {
+
+std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
+        const compositionengine::CompositionEngine& compositionEngine,
+        compositionengine::Display& display, compositionengine::RenderSurfaceCreationArgs&& args) {
+    return std::make_unique<RenderSurface>(compositionEngine, display, std::move(args));
+}
+
+RenderSurface::RenderSurface(const CompositionEngine& compositionEngine, Display& display,
+                             RenderSurfaceCreationArgs&& args)
+      : mCompositionEngine(compositionEngine),
+        mDisplay(display),
+        mNativeWindow(args.nativeWindow),
+        mDisplaySurface(args.displaySurface),
+        mSize(args.displayWidth, args.displayHeight) {}
+
+RenderSurface::~RenderSurface() = default;
+
+bool RenderSurface::isValid() const {
+    return mSize.isValid();
+}
+
+void RenderSurface::initialize() {
+    ANativeWindow* const window = mNativeWindow.get();
+
+    int status = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+    ALOGE_IF(status != NO_ERROR, "Unable to connect BQ producer: %d", status);
+    status = native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
+    ALOGE_IF(status != NO_ERROR, "Unable to set BQ format to RGBA888: %d", status);
+    status = native_window_set_usage(window, GRALLOC_USAGE_HW_RENDER);
+    ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for GPU rendering: %d", status);
+}
+
+const ui::Size& RenderSurface::getSize() const {
+    return mSize;
+}
+
+const sp<Fence>& RenderSurface::getClientTargetAcquireFence() const {
+    return mDisplaySurface->getClientTargetAcquireFence();
+}
+
+void RenderSurface::setDisplaySize(const ui::Size& size) {
+    mDisplaySurface->resizeBuffers(size.width, size.height);
+    mSize = size;
+}
+
+void RenderSurface::setBufferDataspace(ui::Dataspace dataspace) {
+    native_window_set_buffers_data_space(mNativeWindow.get(),
+                                         static_cast<android_dataspace>(dataspace));
+}
+
+void RenderSurface::setProtected(bool useProtected) {
+    uint64_t usageFlags = GRALLOC_USAGE_HW_RENDER;
+    if (useProtected) {
+        usageFlags |= GRALLOC_USAGE_PROTECTED;
+    }
+    const int status = native_window_set_usage(mNativeWindow.get(), usageFlags);
+    ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for protected content: %d", status);
+}
+
+status_t RenderSurface::beginFrame(bool mustRecompose) {
+    return mDisplaySurface->beginFrame(mustRecompose);
+}
+
+status_t RenderSurface::prepareFrame(std::vector<CompositionInfo>& compositionData) {
+    auto& hwc = mCompositionEngine.getHwComposer();
+    const auto id = mDisplay.getId();
+    if (id) {
+        status_t error = hwc.prepare(*id, compositionData);
+        if (error != NO_ERROR) {
+            return error;
+        }
+    }
+
+    DisplaySurface::CompositionType compositionType;
+    const bool hasClient = hwc.hasClientComposition(id);
+    const bool hasDevice = hwc.hasDeviceComposition(id);
+    if (hasClient && hasDevice) {
+        compositionType = DisplaySurface::COMPOSITION_MIXED;
+    } else if (hasClient) {
+        compositionType = DisplaySurface::COMPOSITION_GLES;
+    } else if (hasDevice) {
+        compositionType = DisplaySurface::COMPOSITION_HWC;
+    } else {
+        // Nothing to do -- when turning the screen off we get a frame like
+        // this. Call it a HWC frame since we won't be doing any GLES work but
+        // will do a prepare/set cycle.
+        compositionType = DisplaySurface::COMPOSITION_HWC;
+    }
+    return mDisplaySurface->prepareFrame(compositionType);
+}
+
+sp<GraphicBuffer> RenderSurface::dequeueBuffer() {
+    int fd = -1;
+    ANativeWindowBuffer* buffer = nullptr;
+
+    status_t result = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fd);
+
+    if (result != NO_ERROR) {
+        ALOGE("ANativeWindow::dequeueBuffer failed for display [%s] with error: %d",
+              mDisplay.getName().c_str(), result);
+        // Return fast here as we can't do much more - any rendering we do
+        // now will just be wrong.
+        return mGraphicBuffer;
+    }
+
+    ALOGW_IF(mGraphicBuffer != nullptr, "Clobbering a non-null pointer to a buffer [%p].",
+             mGraphicBuffer->getNativeBuffer()->handle);
+    mGraphicBuffer = GraphicBuffer::from(buffer);
+
+    // Block until the buffer is ready
+    // TODO(alecmouri): it's perhaps more appropriate to block renderengine so
+    // that the gl driver can block instead.
+    if (fd >= 0) {
+        sync_wait(fd, -1);
+        close(fd);
+    }
+
+    return mGraphicBuffer;
+}
+
+void RenderSurface::queueBuffer() {
+    auto& hwc = mCompositionEngine.getHwComposer();
+    const auto id = mDisplay.getId();
+
+    if (hwc.hasClientComposition(id) || hwc.hasFlipClientTargetRequest(id)) {
+        // hasFlipClientTargetRequest could return true even if we haven't
+        // dequeued a buffer before. Try dequeueing one if we don't have a
+        // buffer ready.
+        if (mGraphicBuffer == nullptr) {
+            ALOGI("Attempting to queue a client composited buffer without one "
+                  "previously dequeued for display [%s]. Attempting to dequeue "
+                  "a scratch buffer now",
+                  mDisplay.getName().c_str());
+            // We shouldn't deadlock here, since mGraphicBuffer == nullptr only
+            // after a successful call to queueBuffer, or if dequeueBuffer has
+            // never been called.
+            dequeueBuffer();
+        }
+
+        if (mGraphicBuffer == nullptr) {
+            ALOGE("No buffer is ready for display [%s]", mDisplay.getName().c_str());
+        } else {
+            status_t result = mNativeWindow->queueBuffer(mNativeWindow.get(),
+                                                         mGraphicBuffer->getNativeBuffer(),
+                                                         dup(mBufferReady));
+            if (result != NO_ERROR) {
+                ALOGE("Error when queueing buffer for display [%s]: %d", mDisplay.getName().c_str(),
+                      result);
+                // We risk blocking on dequeueBuffer if the primary display failed
+                // to queue up its buffer, so crash here.
+                if (!mDisplay.isVirtual()) {
+                    LOG_ALWAYS_FATAL("ANativeWindow::queueBuffer failed with error: %d", result);
+                } else {
+                    mNativeWindow->cancelBuffer(mNativeWindow.get(),
+                                                mGraphicBuffer->getNativeBuffer(),
+                                                dup(mBufferReady));
+                }
+            }
+
+            mBufferReady.reset();
+            mGraphicBuffer = nullptr;
+        }
+    }
+
+    status_t result = mDisplaySurface->advanceFrame();
+    if (result != NO_ERROR) {
+        ALOGE("[%s] failed pushing new frame to HWC: %d", mDisplay.getName().c_str(), result);
+    }
+}
+
+void RenderSurface::onPresentDisplayCompleted() {
+    mDisplaySurface->onFrameCommitted();
+}
+
+void RenderSurface::setViewportAndProjection() {
+    auto& renderEngine = mCompositionEngine.getRenderEngine();
+    Rect sourceCrop = Rect(mSize);
+    renderEngine.setViewportAndProjection(mSize.width, mSize.height, sourceCrop,
+                                          ui::Transform::ROT_0);
+}
+
+void RenderSurface::finishBuffer() {
+    auto& renderEngine = mCompositionEngine.getRenderEngine();
+    mBufferReady = renderEngine.flush();
+    if (mBufferReady.get() < 0) {
+        renderEngine.finish();
+    }
+}
+
+void RenderSurface::flip() {
+    mPageFlipCount++;
+}
+
+void RenderSurface::dump(std::string& out) const {
+    using android::base::StringAppendF;
+
+    out.append("   Composition RenderSurface State:");
+
+    out.append("\n   ");
+
+    dumpVal(out, "size", mSize);
+    StringAppendF(&out, "ANativeWindow=%p (format %d) ", mNativeWindow.get(),
+                  ANativeWindow_getFormat(mNativeWindow.get()));
+    dumpVal(out, "flips", mPageFlipCount);
+    out.append("\n");
+
+    String8 surfaceDump;
+    mDisplaySurface->dumpAsString(surfaceDump);
+    out.append(surfaceDump);
+}
+
+std::uint32_t RenderSurface::getPageFlipCount() const {
+    return mPageFlipCount;
+}
+
+void RenderSurface::setPageFlipCountForTest(std::uint32_t count) {
+    mPageFlipCount = count;
+}
+
+void RenderSurface::setSizeForTest(const ui::Size& size) {
+    mSize = size;
+}
+
+sp<GraphicBuffer>& RenderSurface::mutableGraphicBufferForTest() {
+    return mGraphicBuffer;
+}
+
+base::unique_fd& RenderSurface::mutableBufferReadyForTest() {
+    return mBufferReady;
+}
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
new file mode 100644
index 0000000..d20fdda
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
@@ -0,0 +1,644 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <gtest/gtest.h>
+
+namespace android::hardware::graphics::common::V1_1 {
+
+// Note: These operator overloads need to be defined in the same namespace as
+// the values they print.
+
+std::ostream& operator<<(std::ostream& os, const RenderIntent& value) {
+    return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
+              << ")";
+}
+
+} // namespace android::hardware::graphics::common::V1_1
+
+namespace android::hardware::graphics::common::V1_2 {
+
+// Note: These operator overloads need to be defined in the same namespace as
+// the values they print.
+
+std::ostream& operator<<(std::ostream& os, const Dataspace& value) {
+    return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
+              << ")";
+}
+
+std::ostream& operator<<(std::ostream& os, const ColorMode& value) {
+    return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
+              << ")";
+}
+
+} // namespace android::hardware::graphics::common::V1_2
+
+namespace android::compositionengine {
+namespace {
+
+using testing::_;
+using testing::Contains;
+using testing::IsEmpty;
+using testing::Ref;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SizeIs;
+using testing::StrictMock;
+
+using ui::ColorMode;
+using ui::Dataspace;
+using ui::Hdr;
+using ui::RenderIntent;
+
+// This allows us to simulate a vendor-specified intent being used.
+constexpr RenderIntent VendorRenderIntent = static_cast<RenderIntent>(0x100);
+
+class DisplayColorProfileTest : public testing::Test {
+public:
+    ~DisplayColorProfileTest() override = default;
+
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+};
+
+class ProfileFactory {
+public:
+    impl::DisplayColorProfile build() const {
+        return impl::DisplayColorProfile{DisplayColorProfileCreationArgs{
+                mHasWideColorGamut,
+                HdrCapabilities(mSupportedHdrTypes, mMaxLuminance, mMaxAverageLuminance,
+                                mMinLuminance),
+                mSupportedPerFrameMetadata,
+                mSupportedColorModes,
+        }};
+    }
+
+    ProfileFactory& setHasWideColorGamut(bool value) {
+        mHasWideColorGamut = value;
+        return *this;
+    }
+
+    ProfileFactory& setPerFrameMetadata(int32_t value) {
+        mSupportedPerFrameMetadata = value;
+        return *this;
+    }
+
+    ProfileFactory& addHdrType(Hdr value) {
+        mSupportedHdrTypes.emplace_back(value);
+        return *this;
+    }
+
+    ProfileFactory& addHdrTypes(std::initializer_list<Hdr> values) {
+        for (auto value : values) {
+            mSupportedHdrTypes.emplace_back(value);
+        }
+        return *this;
+    }
+
+    ProfileFactory& setMaxLuminance(float value) {
+        mMaxLuminance = value;
+        return *this;
+    }
+
+    ProfileFactory& setMaxAverageLuminance(float value) {
+        mMaxAverageLuminance = value;
+        return *this;
+    }
+
+    ProfileFactory& setMinLuminance(float value) {
+        mMinLuminance = value;
+        return *this;
+    }
+
+    ProfileFactory& addColorModeRenderIntent(ColorMode colorMode, RenderIntent renderIntent) {
+        mSupportedColorModes[colorMode].emplace_back(renderIntent);
+        return *this;
+    }
+
+    ProfileFactory& addColorModeRenderIntents(ColorMode colorMode,
+                                              std::initializer_list<RenderIntent> renderIntents) {
+        auto& profileedRenderIntents = mSupportedColorModes[colorMode];
+        for (auto renderIntent : renderIntents) {
+            profileedRenderIntents.emplace_back(renderIntent);
+        }
+        return *this;
+    }
+
+    static impl::DisplayColorProfile createProfileWithNoColorModeSupport() {
+        return ProfileFactory().build();
+    }
+
+    static impl::DisplayColorProfile createProfileWithBT2020ColorModeSupport() {
+        return ProfileFactory()
+                .setHasWideColorGamut(true)
+                .addHdrType(Hdr::HDR10)
+                .addColorModeRenderIntent(ColorMode::DISPLAY_BT2020, VendorRenderIntent)
+                .build();
+    }
+
+    static impl::DisplayColorProfile createProfileWithSRGBColorModeSupport() {
+        return ProfileFactory()
+                .setHasWideColorGamut(true)
+                .addHdrType(Hdr::HDR10)
+                .addColorModeRenderIntent(ColorMode::SRGB, VendorRenderIntent)
+                .build();
+    }
+
+    static impl::DisplayColorProfile createProfileWithBT2100PQSupport() {
+        return ProfileFactory()
+                .setHasWideColorGamut(true)
+                .addHdrType(Hdr::HLG)
+                .addColorModeRenderIntent(ColorMode::BT2100_PQ, VendorRenderIntent)
+                .build();
+    }
+
+private:
+    bool mHasWideColorGamut = false;
+    std::vector<Hdr> mSupportedHdrTypes;
+    float mMaxLuminance = -1.f;
+    float mMaxAverageLuminance = -1.f;
+    float mMinLuminance = -1.f;
+    int32_t mSupportedPerFrameMetadata = 0;
+    std::unordered_map<ColorMode, std::vector<RenderIntent>> mSupportedColorModes;
+};
+
+/* ------------------------------------------------------------------------
+ * RenderSurface Construction
+ */
+
+TEST_F(DisplayColorProfileTest, ctorSetsHasWideColorGamutFromInputArgs) {
+    {
+        auto profile = ProfileFactory().setHasWideColorGamut(false).build();
+
+        EXPECT_FALSE(profile.hasWideColorGamut());
+    }
+
+    {
+        auto profile = ProfileFactory().setHasWideColorGamut(true).build();
+
+        EXPECT_TRUE(profile.hasWideColorGamut());
+    }
+}
+
+TEST_F(DisplayColorProfileTest, ctorSetsSupportedPerFrameMetadataFromInputArgs) {
+    {
+        auto profile = ProfileFactory().setPerFrameMetadata(0).build();
+
+        EXPECT_EQ(0, profile.getSupportedPerFrameMetadata());
+    }
+
+    {
+        impl::DisplayColorProfile profile = ProfileFactory().setPerFrameMetadata(123).build();
+
+        EXPECT_EQ(123, profile.getSupportedPerFrameMetadata());
+    }
+}
+
+TEST_F(DisplayColorProfileTest, ctorDetectsSupportedHdrTypesFromInputArgs) {
+    {
+        // The constructor will set the internal state to not indicate any
+        // profile for HDR modes if none are profileed.
+        auto profile = ProfileFactory().build();
+
+        EXPECT_FALSE(profile.hasHDR10PlusSupport());
+        EXPECT_FALSE(profile.hasHDR10Support());
+        EXPECT_FALSE(profile.hasHLGSupport());
+        EXPECT_FALSE(profile.hasDolbyVisionSupport());
+    }
+
+    {
+        // The constructor will set the intenral state to indicate HDR10Plus
+        // profile if the input arguments indicate it is profileed.
+        auto profile = ProfileFactory().addHdrType(Hdr::HDR10_PLUS).build();
+
+        EXPECT_TRUE(profile.hasHDR10PlusSupport());
+        EXPECT_FALSE(profile.hasHDR10Support());
+        EXPECT_FALSE(profile.hasHLGSupport());
+        EXPECT_FALSE(profile.hasDolbyVisionSupport());
+    }
+
+    {
+        // The constructor will set the intenral state to indicate HDR10 profile
+        // if the input arguments indicate it is profileed.
+        auto profile = ProfileFactory().addHdrType(Hdr::HDR10).build();
+
+        EXPECT_FALSE(profile.hasHDR10PlusSupport());
+        EXPECT_TRUE(profile.hasHDR10Support());
+        EXPECT_FALSE(profile.hasHLGSupport());
+        EXPECT_FALSE(profile.hasDolbyVisionSupport());
+    }
+
+    {
+        // The constructor will set the intenral state to indicate HLG profile
+        // if the input arguments indicate it is profileed.
+        auto profile = ProfileFactory().addHdrType(Hdr::HLG).build();
+
+        EXPECT_FALSE(profile.hasHDR10PlusSupport());
+        EXPECT_FALSE(profile.hasHDR10Support());
+        EXPECT_TRUE(profile.hasHLGSupport());
+        EXPECT_FALSE(profile.hasDolbyVisionSupport());
+    }
+
+    {
+        // The constructor will set the intenral state to indicate Dolbyvision profile
+        // if the input arguments indicate it is profileed.
+        auto profile = ProfileFactory().addHdrType(Hdr::DOLBY_VISION).build();
+
+        EXPECT_FALSE(profile.hasHDR10Support());
+        EXPECT_FALSE(profile.hasHLGSupport());
+        EXPECT_TRUE(profile.hasDolbyVisionSupport());
+    }
+}
+
+TEST_F(DisplayColorProfileTest, ctorUsesOrDefaultsLuminanceValuesFromInputArgs) {
+    {
+        // The constructor will use a default value for each luminance setting
+        // that is negative.
+        auto profile = ProfileFactory()
+                               .setMaxLuminance(-1.f)
+                               .setMaxAverageLuminance(-1.f)
+                               .setMinLuminance(-1.f)
+                               .build();
+
+        EXPECT_EQ(DisplayColorProfile::sDefaultMaxLumiance,
+                  profile.getHdrCapabilities().getDesiredMaxLuminance());
+        EXPECT_EQ(DisplayColorProfile::sDefaultMaxLumiance,
+                  profile.getHdrCapabilities().getDesiredMaxAverageLuminance());
+        EXPECT_EQ(DisplayColorProfile::sDefaultMinLumiance,
+                  profile.getHdrCapabilities().getDesiredMinLuminance());
+    }
+
+    {
+        // The constructor will otherwise take and use a positive value for each
+        // of the luminance settings.
+        auto profile = ProfileFactory()
+                               .setMaxLuminance(1001.f)
+                               .setMaxAverageLuminance(1002.f)
+                               .setMinLuminance(1003.f)
+                               .build();
+
+        EXPECT_EQ(1001.f, profile.getHdrCapabilities().getDesiredMaxLuminance());
+        EXPECT_EQ(1002.f, profile.getHdrCapabilities().getDesiredMaxAverageLuminance());
+        EXPECT_EQ(1003.f, profile.getHdrCapabilities().getDesiredMinLuminance());
+    }
+}
+
+TEST_F(DisplayColorProfileTest, ctorSignalsHdrSupportForAnyWideColorGamutDevice) {
+    {
+        // If the output does not profile wide color gamut, then no HDR modes
+        // will be profileed in the generated HDR capabilities.
+        auto profile = ProfileFactory().setHasWideColorGamut(false).build();
+
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), IsEmpty());
+    }
+
+    {
+        // If the HWC does not show profile for certain HDR modes, then the
+        // generated HDR capabilities will indicate profile anyway.
+        auto profile = ProfileFactory().setHasWideColorGamut(true).build();
+
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), SizeIs(2));
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HDR10));
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HLG));
+    }
+
+    {
+        // If the HWC profiles the HDR modes, then the generated capabilities
+        // still has one entry for each HDR type.
+        auto profile = ProfileFactory()
+                               .setHasWideColorGamut(true)
+                               .addHdrTypes({Hdr::HLG, Hdr::HDR10})
+                               .build();
+
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), SizeIs(2));
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HDR10));
+        EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HLG));
+    }
+}
+
+/* ------------------------------------------------------------------------
+ * DisplayColorProfile::hasRenderIntent
+ */
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasNoSupport) {
+    auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasBT2020upport) {
+    auto profile = ProfileFactory::createProfileWithBT2020ColorModeSupport();
+
+    EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasSRGBSupport) {
+    auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
+
+    EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+    EXPECT_TRUE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasBTG2100PQSupport) {
+    auto profile = ProfileFactory::createProfileWithBT2100PQSupport();
+
+    EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
+/* ------------------------------------------------------------------------
+ * DisplayColorProfile::hasLegacyHdrSupport
+ */
+
+TEST_F(DisplayColorProfileTest, hasLegacyHdrSupport) {
+    {
+        auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+        EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+        EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+    }
+
+    {
+        auto profile = ProfileFactory::createProfileWithBT2020ColorModeSupport();
+
+        EXPECT_TRUE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+        EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+    }
+
+    {
+        auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
+
+        EXPECT_TRUE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+        EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+    }
+
+    {
+        auto profile = ProfileFactory::createProfileWithBT2100PQSupport();
+
+        EXPECT_FALSE(profile.hasLegacyHdrSupport(Dataspace::BT2020_PQ));
+        EXPECT_TRUE(profile.hasLegacyHdrSupport(Dataspace::BT2020_HLG));
+    }
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::getBestColorMode()
+ */
+
+void checkGetBestColorMode(
+        DisplayColorProfile& profile,
+        const std::array<std::tuple<Dataspace, ColorMode, RenderIntent>, 25>& expected) {
+    using ArgsType = std::tuple<Dataspace, RenderIntent>;
+
+    // These are the combinations of dataspaces and render intents that could be
+    // passed to RenderSurface::getBestColorMode()
+    const std::array<std::tuple<Dataspace, RenderIntent>, 25> kArgs = {
+            /* clang-format off */
+
+            // Non-HDR combinations
+
+            /*  0 */ ArgsType{Dataspace::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /*  1 */ ArgsType{Dataspace::DISPLAY_BT2020, RenderIntent::ENHANCE},
+            /*  2 */ ArgsType{Dataspace::DISPLAY_BT2020, RenderIntent::TONE_MAP_COLORIMETRIC}, // Vendor explicit setting
+            /*  3 */ ArgsType{Dataspace::DISPLAY_BT2020, RenderIntent::TONE_MAP_ENHANCE},      // Vendor explicit setting
+            /*  4 */ ArgsType{Dataspace::DISPLAY_BT2020, VendorRenderIntent},                  // Vendor explicit setting
+
+            /*  5 */ ArgsType{Dataspace::DISPLAY_P3, RenderIntent::COLORIMETRIC},
+            /*  6 */ ArgsType{Dataspace::DISPLAY_P3, RenderIntent::ENHANCE},
+            /*  7 */ ArgsType{Dataspace::DISPLAY_P3, RenderIntent::TONE_MAP_COLORIMETRIC}, // Vendor explicit setting
+            /*  8 */ ArgsType{Dataspace::DISPLAY_P3, RenderIntent::TONE_MAP_ENHANCE},      // Vendor explicit setting
+            /*  9 */ ArgsType{Dataspace::DISPLAY_P3, VendorRenderIntent},                  // Vendor explicit setting
+
+            /* 10 */ ArgsType{Dataspace::V0_SRGB, RenderIntent::COLORIMETRIC},
+            /* 11 */ ArgsType{Dataspace::V0_SRGB, RenderIntent::ENHANCE},
+            /* 12 */ ArgsType{Dataspace::V0_SRGB, RenderIntent::TONE_MAP_COLORIMETRIC}, // Vendor explicit setting
+            /* 13 */ ArgsType{Dataspace::V0_SRGB, RenderIntent::TONE_MAP_ENHANCE},      // Vendor explicit setting
+            /* 14 */ ArgsType{Dataspace::V0_SRGB, VendorRenderIntent},                  // Vendor explicit setting
+
+            // HDR combinations
+
+            /* 15 */ ArgsType{Dataspace::BT2020_PQ, RenderIntent::TONE_MAP_COLORIMETRIC},
+            /* 16 */ ArgsType{Dataspace::BT2020_PQ, RenderIntent::TONE_MAP_ENHANCE},
+            /* 17 */ ArgsType{Dataspace::BT2020_PQ, RenderIntent::COLORIMETRIC},       // Vendor explicit setting
+            /* 18 */ ArgsType{Dataspace::BT2020_PQ, RenderIntent::ENHANCE},            // Vendor explicit setting
+            /* 19 */ ArgsType{Dataspace::BT2020_PQ, VendorRenderIntent},               // Vendor explicit setting
+
+            /* 20 */ ArgsType{Dataspace::BT2020_HLG, RenderIntent::TONE_MAP_COLORIMETRIC},
+            /* 21 */ ArgsType{Dataspace::BT2020_HLG, RenderIntent::TONE_MAP_ENHANCE},
+            /* 22 */ ArgsType{Dataspace::BT2020_HLG, RenderIntent::COLORIMETRIC},       // Vendor explicit setting
+            /* 23 */ ArgsType{Dataspace::BT2020_HLG, RenderIntent::ENHANCE},            // Vendor explicit setting
+            /* 24 */ ArgsType{Dataspace::BT2020_HLG, VendorRenderIntent},               // Vendor explicit setting
+            /* clang-format on */
+    };
+
+    for (size_t i = 0; i < kArgs.size(); i++) {
+        std::tuple<Dataspace, ColorMode, RenderIntent> actual;
+        profile.getBestColorMode(std::get<0>(kArgs[i]), std::get<1>(kArgs[i]), &std::get<0>(actual),
+                                 &std::get<1>(actual), &std::get<2>(actual));
+
+        EXPECT_EQ(expected[i], actual) << " for index " << i;
+    }
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasNoSupport) {
+    auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+    // Note: This table of expected values goes with the table of arguments
+    // used in checkGetBestColorMode.
+    using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+    std::array<Result, 25> expectedResults = {
+            /* clang-format off */
+            /*  0 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  1 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  3 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  4 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /*  5 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  6 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  7 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  9 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 10 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 11 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 12 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 13 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 14 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 15 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 16 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 17 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 18 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 19 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 20 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 21 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 22 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 23 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 24 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* clang-format on */
+    };
+
+    checkGetBestColorMode(profile, expectedResults);
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasBT2020Support) {
+    auto profile = ProfileFactory::createProfileWithBT2020ColorModeSupport();
+
+    // Note: This table of expected values goes with the table of arguments
+    // used in checkGetBestColorMode.
+    using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+    std::array<Result, 25> expectedResults = {
+            /* clang-format off */
+            /*  0 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /*  1 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /*  2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  3 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  4 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /*  5 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /*  6 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /*  7 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  9 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 10 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 11 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 12 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 13 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 14 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 15 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 16 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 17 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 18 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 19 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 20 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 21 */ Result{Dataspace::DISPLAY_BT2020, ColorMode::DISPLAY_BT2020, RenderIntent::COLORIMETRIC},
+            /* 22 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 23 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 24 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* clang-format on */
+    };
+
+    checkGetBestColorMode(profile, expectedResults);
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasSRGBSupport) {
+    auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
+
+    // Note: This table of expected values goes with the table of arguments
+    // used in checkGetBestColorMode.
+    using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+    std::array<Result, 25> expectedResults = {
+            /* clang-format off */
+            /*  0 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /*  1 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /*  2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  3 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  4 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+            /*  5 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /*  6 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /*  7 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  9 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+            /* 10 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 11 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 12 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 13 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 14 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+            /* 15 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 16 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 17 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 18 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 19 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 20 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 21 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 22 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 23 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 24 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* clang-format on */
+    };
+
+    checkGetBestColorMode(profile, expectedResults);
+}
+
+TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasBT2100PQSupport) {
+    auto profile = ProfileFactory::createProfileWithBT2100PQSupport();
+
+    // Note: This table of expected values goes with the table of arguments
+    // used in checkGetBestColorMode.
+    using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+    std::array<Result, 25> expectedResults = {
+            /* clang-format off */
+            /*  0 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  1 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  2 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  3 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  4 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /*  5 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  6 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  7 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  8 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /*  9 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 19 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 11 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 12 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 13 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 14 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 15 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+            /* 16 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+            /* 17 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 18 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 19 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 20 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+            /* 21 */ Result{Dataspace::BT2020_PQ, ColorMode::BT2100_PQ, RenderIntent::COLORIMETRIC},
+            /* 22 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 23 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* 24 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* clang-format on */
+    };
+
+    checkGetBestColorMode(profile, expectedResults);
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
new file mode 100644
index 0000000..8cb6936
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cmath>
+
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/Display.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/RenderSurface.h>
+#include <gtest/gtest.h>
+#include <system/window.h>
+
+#include "MockHWComposer.h"
+
+namespace android::compositionengine {
+namespace {
+
+using testing::Return;
+using testing::ReturnRef;
+using testing::StrictMock;
+
+constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+
+class DisplayTest : public testing::Test {
+public:
+    ~DisplayTest() override = default;
+
+    StrictMock<android::mock::HWComposer> mHwComposer;
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    impl::Display mDisplay{mCompositionEngine,
+                           DisplayCreationArgsBuilder().setDisplayId(DEFAULT_DISPLAY_ID).build()};
+};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(DisplayTest, canInstantiateDisplay) {
+    {
+        constexpr DisplayId display1 = DisplayId{123u};
+        auto display =
+                impl::createDisplay(mCompositionEngine,
+                                    DisplayCreationArgsBuilder().setDisplayId(display1).build());
+        EXPECT_FALSE(display->isSecure());
+        EXPECT_FALSE(display->isVirtual());
+        EXPECT_EQ(display1, display->getId());
+    }
+
+    {
+        constexpr DisplayId display2 = DisplayId{546u};
+        auto display = impl::createDisplay(mCompositionEngine,
+                                           DisplayCreationArgsBuilder()
+                                                   .setIsSecure(true)
+                                                   .setDisplayId(display2)
+                                                   .build());
+        EXPECT_TRUE(display->isSecure());
+        EXPECT_FALSE(display->isVirtual());
+        EXPECT_EQ(display2, display->getId());
+    }
+
+    {
+        constexpr DisplayId display3 = DisplayId{789u};
+        auto display = impl::createDisplay(mCompositionEngine,
+                                           DisplayCreationArgsBuilder()
+                                                   .setIsVirtual(true)
+                                                   .setDisplayId(display3)
+                                                   .build());
+        EXPECT_FALSE(display->isSecure());
+        EXPECT_TRUE(display->isVirtual());
+        EXPECT_EQ(display3, display->getId());
+    }
+}
+
+/* ------------------------------------------------------------------------
+ * Display::disconnect()
+ */
+
+TEST_F(DisplayTest, disconnectDisconnectsDisplay) {
+    EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+    // The first call to disconnect will disconnect the display with the HWC and
+    // set mHwcId to -1.
+    EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(1);
+    mDisplay.disconnect();
+    EXPECT_FALSE(mDisplay.getId());
+
+    // Subsequent calls will do nothing,
+    EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(0);
+    mDisplay.disconnect();
+    EXPECT_FALSE(mDisplay.getId());
+}
+
+/* ------------------------------------------------------------------------
+ * Display::setColorTransform()
+ */
+
+TEST_F(DisplayTest, setColorTransformSetsTransform) {
+    // Identity matrix sets an identity state value
+    const mat4 identity;
+
+    EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, identity)).Times(1);
+
+    mDisplay.setColorTransform(identity);
+
+    EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mDisplay.getState().colorTransform);
+
+    // Non-identity matrix sets a non-identity state value
+    const mat4 nonIdentity = mat4() * 2;
+
+    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, nonIdentity)).Times(1);
+
+    mDisplay.setColorTransform(nonIdentity);
+
+    EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mDisplay.getState().colorTransform);
+}
+
+/* ------------------------------------------------------------------------
+ * Display::setColorMode()
+ */
+
+TEST_F(DisplayTest, setColorModeSetsModeUnlessNoChange) {
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    mDisplay.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+    EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+    // These values are expected to be the initial state.
+    ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
+    ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
+    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
+
+    // Otherwise if the values are unchanged, nothing happens
+    mDisplay.setColorMode(ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+                          ui::RenderIntent::COLORIMETRIC);
+
+    EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
+
+    // Otherwise if the values are different, updates happen
+    EXPECT_CALL(*renderSurface, setBufferDataspace(ui::Dataspace::SRGB)).Times(1);
+    EXPECT_CALL(mHwComposer,
+                setActiveColorMode(DEFAULT_DISPLAY_ID, ui::ColorMode::BT2100_PQ,
+                                   ui::RenderIntent::TONE_MAP_COLORIMETRIC))
+            .Times(1);
+
+    mDisplay.setColorMode(ui::ColorMode::BT2100_PQ, ui::Dataspace::SRGB,
+                          ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+    EXPECT_EQ(ui::ColorMode::BT2100_PQ, mDisplay.getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::SRGB, mDisplay.getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay.getState().renderIntent);
+}
+
+TEST_F(DisplayTest, setColorModeDoesNothingForVirtualDisplay) {
+    impl::Display virtualDisplay{mCompositionEngine,
+                                 DisplayCreationArgs{false, true, DEFAULT_DISPLAY_ID}};
+
+    virtualDisplay.setColorMode(ui::ColorMode::BT2100_PQ, ui::Dataspace::SRGB,
+                                ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+    EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay.getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay.getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay.getState().renderIntent);
+}
+
+/* ------------------------------------------------------------------------
+ * Display::createDisplayColorProfile()
+ */
+
+TEST_F(DisplayTest, createDisplayColorProfileSetsDisplayColorProfile) {
+    EXPECT_TRUE(mDisplay.getDisplayColorProfile() == nullptr);
+    mDisplay.createDisplayColorProfile(
+            DisplayColorProfileCreationArgs{false, HdrCapabilities(), 0,
+                                            DisplayColorProfileCreationArgs::HwcColorModes()});
+    EXPECT_TRUE(mDisplay.getDisplayColorProfile() != nullptr);
+}
+
+/* ------------------------------------------------------------------------
+ * Display::createRenderSurface()
+ */
+
+TEST_F(DisplayTest, createRenderSurfaceSetsRenderSurface) {
+    EXPECT_TRUE(mDisplay.getRenderSurface() == nullptr);
+    mDisplay.createRenderSurface(RenderSurfaceCreationArgs{640, 480, nullptr, nullptr});
+    EXPECT_TRUE(mDisplay.getRenderSurface() != nullptr);
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
new file mode 100644
index 0000000..f060cff
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cmath>
+
+#include <compositionengine/impl/Output.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
+#include <compositionengine/mock/RenderSurface.h>
+#include <gtest/gtest.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+#include "RegionMatcher.h"
+#include "TransformMatcher.h"
+
+namespace android::compositionengine {
+namespace {
+
+using testing::Return;
+using testing::ReturnRef;
+using testing::StrictMock;
+
+class OutputTest : public testing::Test {
+public:
+    OutputTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+    }
+    ~OutputTest() override = default;
+
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    impl::Output mOutput{mCompositionEngine};
+};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(OutputTest, canInstantiateOutput) {
+    // The validation check checks each required component.
+    EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true));
+    EXPECT_CALL(*mRenderSurface, isValid()).WillOnce(Return(true));
+
+    EXPECT_TRUE(mOutput.isValid());
+
+    // If we take away the required components, it is no longer valid.
+    mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>());
+
+    EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true));
+
+    EXPECT_FALSE(mOutput.isValid());
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setCompositionEnabled()
+ */
+
+TEST_F(OutputTest, setCompositionEnabledDoesNothingIfAlreadyEnabled) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+    mOutput.editState().isEnabled = true;
+
+    mOutput.setCompositionEnabled(true);
+
+    EXPECT_TRUE(mOutput.getState().isEnabled);
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+}
+
+TEST_F(OutputTest, setCompositionEnabledSetsEnabledAndDirtiesEntireOutput) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+    mOutput.editState().isEnabled = false;
+
+    mOutput.setCompositionEnabled(true);
+
+    EXPECT_TRUE(mOutput.getState().isEnabled);
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+}
+
+TEST_F(OutputTest, setCompositionEnabledSetsDisabledAndDirtiesEntireOutput) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+    mOutput.editState().isEnabled = true;
+
+    mOutput.setCompositionEnabled(false);
+
+    EXPECT_FALSE(mOutput.getState().isEnabled);
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setProjection()
+ */
+
+TEST_F(OutputTest, setProjectionTriviallyWorks) {
+    const ui::Transform transform{ui::Transform::ROT_180};
+    const int32_t orientation = 123;
+    const Rect frame{1, 2, 3, 4};
+    const Rect viewport{5, 6, 7, 8};
+    const Rect scissor{9, 10, 11, 12};
+    const bool needsFiltering = true;
+
+    mOutput.setProjection(transform, orientation, frame, viewport, scissor, needsFiltering);
+
+    EXPECT_THAT(mOutput.getState().transform, TransformEq(transform));
+    EXPECT_EQ(orientation, mOutput.getState().orientation);
+    EXPECT_EQ(frame, mOutput.getState().frame);
+    EXPECT_EQ(viewport, mOutput.getState().viewport);
+    EXPECT_EQ(scissor, mOutput.getState().scissor);
+    EXPECT_EQ(needsFiltering, mOutput.getState().needsFiltering);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setBounds()
+ */
+
+TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) {
+    const ui::Size displaySize{100, 200};
+
+    EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
+    EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
+
+    mOutput.setBounds(displaySize);
+
+    EXPECT_EQ(Rect(displaySize), mOutput.getState().bounds);
+
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setLayerStackFilter()
+ */
+
+TEST_F(OutputTest, setLayerStackFilterSetsFilterAndDirtiesEntireOutput) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+
+    const uint32_t layerStack = 123u;
+    mOutput.setLayerStackFilter(true, layerStack);
+
+    EXPECT_TRUE(mOutput.getState().singleLayerStack);
+    EXPECT_EQ(layerStack, mOutput.getState().singleLayerStackId);
+
+    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setColorTransform
+ */
+
+TEST_F(OutputTest, setColorTransformSetsTransform) {
+    // Identity matrix sets an identity state value
+    const mat4 identity;
+
+    mOutput.setColorTransform(identity);
+
+    EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mOutput.getState().colorTransform);
+
+    // Non-identity matrix sets a non-identity state value
+    const mat4 nonIdentity = mat4() * 2;
+
+    mOutput.setColorTransform(nonIdentity);
+
+    EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setColorMode
+ */
+
+TEST_F(OutputTest, setColorModeSetsModeUnlessNoChange) {
+    EXPECT_CALL(*mRenderSurface, setBufferDataspace(ui::Dataspace::SRGB)).Times(1);
+
+    mOutput.setColorMode(ui::ColorMode::BT2100_PQ, ui::Dataspace::SRGB,
+                         ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+    EXPECT_EQ(ui::ColorMode::BT2100_PQ, mOutput.getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::SRGB, mOutput.getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput.getState().renderIntent);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setRenderSurface()
+ */
+
+TEST_F(OutputTest, setRenderSurfaceResetsBounds) {
+    const ui::Size newDisplaySize{640, 480};
+
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    EXPECT_CALL(*renderSurface, getSize()).WillOnce(ReturnRef(newDisplaySize));
+
+    mOutput.setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
+
+    EXPECT_EQ(Rect(newDisplaySize), mOutput.getState().bounds);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::getPhysicalSpaceDirtyRegion()
+ */
+
+TEST_F(OutputTest, getPhysicalSpaceDirtyRegionWithRepaintEverythingTrue) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+    mOutput.editState().dirtyRegion.set(50, 300);
+
+    {
+        Region result = mOutput.getPhysicalSpaceDirtyRegion(true);
+
+        EXPECT_THAT(result, RegionEq(Region(displaySize)));
+    }
+
+    // For repaint everything == true, the returned value does not depend on the display
+    // rotation.
+    mOutput.editState().transform.set(ui::Transform::ROT_90, 0, 0);
+
+    {
+        Region result = mOutput.getPhysicalSpaceDirtyRegion(true);
+
+        EXPECT_THAT(result, RegionEq(Region(displaySize)));
+    }
+}
+
+TEST_F(OutputTest, getPhysicalSpaceDirtyRegionWithRepaintEverythingFalse) {
+    const Rect displaySize{100, 200};
+    mOutput.editState().bounds = displaySize;
+    mOutput.editState().dirtyRegion.set(50, 300);
+
+    {
+        Region result = mOutput.getPhysicalSpaceDirtyRegion(false);
+
+        // The dirtyRegion should be clipped to the display bounds.
+        EXPECT_THAT(result, RegionEq(Region(Rect(50, 200))));
+    }
+
+    mOutput.editState().transform.set(ui::Transform::ROT_90, displaySize.getWidth(),
+                                      displaySize.getHeight());
+
+    {
+        Region result = mOutput.getPhysicalSpaceDirtyRegion(false);
+
+        // The dirtyRegion should be rotated and clipped to the display bounds.
+        EXPECT_THAT(result, RegionEq(Region(Rect(100, 50))));
+    }
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
new file mode 100644
index 0000000..5a4efa9
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+namespace {
+
+// Checks for a region match
+MATCHER_P(RegionEq, expected, "") {
+    std::string buf;
+    buf.append("Regions are not equal\n");
+    expected.dump(buf, "expected region");
+    arg.dump(buf, "actual region");
+    *result_listener << buf;
+
+    size_t expectedRectCount = 0;
+    android::Rect const* expectedRects = expected.getArray(&expectedRectCount);
+    size_t actualRectCount = 0;
+    android::Rect const* actualRects = arg.getArray(&actualRectCount);
+
+    if (expectedRectCount != actualRectCount) return false;
+    for (size_t i = 0; i < expectedRectCount; i++) {
+        if (expectedRects[i] != actualRects[i]) return false;
+    }
+    return true;
+}
+
+} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
new file mode 100644
index 0000000..13cc663
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -0,0 +1,504 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdarg>
+#include <cstdint>
+
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/RenderSurface.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/Display.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <system/window.h>
+#include <ui/ANativeObjectBase.h>
+
+#include "MockHWComposer.h"
+
+namespace android::compositionengine {
+namespace {
+
+/* ------------------------------------------------------------------------
+ * MockNativeWindow
+ *
+ * An intentionally simplified Mock which implements a minimal subset of the full
+ * ANativeWindow interface.
+ */
+
+class MockNativeWindow : public ANativeObjectBase<ANativeWindow, MockNativeWindow, RefBase> {
+public:
+    MockNativeWindow() {
+        ANativeWindow::setSwapInterval = &forwardSetSwapInterval;
+        ANativeWindow::dequeueBuffer = &forwardDequeueBuffer;
+        ANativeWindow::cancelBuffer = &forwardCancelBuffer;
+        ANativeWindow::queueBuffer = &forwardQueueBuffer;
+        ANativeWindow::query = &forwardQuery;
+        ANativeWindow::perform = &forwardPerform;
+
+        ANativeWindow::dequeueBuffer_DEPRECATED = &forwardDequeueBufferDeprecated;
+        ANativeWindow::cancelBuffer_DEPRECATED = &forwardCancelBufferDeprecated;
+        ANativeWindow::lockBuffer_DEPRECATED = &forwardLockBufferDeprecated;
+        ANativeWindow::queueBuffer_DEPRECATED = &forwardQueueBufferDeprecated;
+    }
+
+    MOCK_METHOD1(setSwapInterval, int(int));
+    MOCK_METHOD2(dequeueBuffer, int(struct ANativeWindowBuffer**, int*));
+    MOCK_METHOD2(cancelBuffer, int(struct ANativeWindowBuffer*, int));
+    MOCK_METHOD2(queueBuffer, int(struct ANativeWindowBuffer*, int));
+    MOCK_CONST_METHOD2(query, int(int, int*));
+    MOCK_METHOD1(connect, int(int));
+    MOCK_METHOD1(lockBuffer_DEPRECATED, int(struct ANativeWindowBuffer*));
+    MOCK_METHOD1(setBuffersFormat, int(PixelFormat));
+    MOCK_METHOD1(setBuffersDataSpace, int(ui::Dataspace));
+    MOCK_METHOD1(setUsage, int(uint64_t));
+
+    static void unexpectedCall(...) { LOG_ALWAYS_FATAL("Unexpected ANativeWindow API call"); }
+
+    static int forwardSetSwapInterval(ANativeWindow* window, int interval) {
+        return getSelf(window)->setSwapInterval(interval);
+    }
+
+    static int forwardDequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
+                                    int* fenceFd) {
+        return getSelf(window)->dequeueBuffer(buffer, fenceFd);
+    }
+
+    static int forwardCancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
+                                   int fenceFd) {
+        return getSelf(window)->cancelBuffer(buffer, fenceFd);
+    }
+
+    static int forwardQueueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+        return getSelf(window)->queueBuffer(buffer, fenceFd);
+    }
+
+    static int forwardQuery(const ANativeWindow* window, int what, int* value) {
+        return getSelf(window)->query(what, value);
+    }
+
+    static int forwardPerform(ANativeWindow* window, int operation, ...) {
+        va_list args;
+        va_start(args, operation);
+        int result = NO_ERROR;
+        switch (operation) {
+            case NATIVE_WINDOW_API_CONNECT: {
+                int api = va_arg(args, int);
+                result = getSelf(window)->connect(api);
+                break;
+            }
+            case NATIVE_WINDOW_SET_BUFFERS_FORMAT: {
+                PixelFormat format = va_arg(args, PixelFormat);
+                result = getSelf(window)->setBuffersFormat(format);
+                break;
+            }
+            case NATIVE_WINDOW_SET_BUFFERS_DATASPACE: {
+                ui::Dataspace dataspace = static_cast<ui::Dataspace>(va_arg(args, int));
+                result = getSelf(window)->setBuffersDataSpace(dataspace);
+                break;
+            }
+            case NATIVE_WINDOW_SET_USAGE: {
+                // Note: Intentionally widens usage from 32 to 64 bits so we
+                // just have one implementation.
+                uint64_t usage = va_arg(args, uint32_t);
+                result = getSelf(window)->setUsage(usage);
+                break;
+            }
+            case NATIVE_WINDOW_SET_USAGE64: {
+                uint64_t usage = va_arg(args, uint64_t);
+                result = getSelf(window)->setUsage(usage);
+                break;
+            }
+            default:
+                LOG_ALWAYS_FATAL("Unexpected operation %d", operation);
+                break;
+        }
+
+        va_end(args);
+        return result;
+    }
+
+    static int forwardDequeueBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer** buffer) {
+        int ignoredFenceFd = -1;
+        return getSelf(window)->dequeueBuffer(buffer, &ignoredFenceFd);
+    }
+
+    static int forwardCancelBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer* buffer) {
+        return getSelf(window)->cancelBuffer(buffer, -1);
+    }
+
+    static int forwardLockBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer* buffer) {
+        return getSelf(window)->lockBuffer_DEPRECATED(buffer);
+    }
+
+    static int forwardQueueBufferDeprecated(ANativeWindow* window, ANativeWindowBuffer* buffer) {
+        return getSelf(window)->queueBuffer(buffer, -1);
+    }
+};
+
+/* ------------------------------------------------------------------------
+ * RenderSurfaceTest
+ */
+
+constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
+constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
+constexpr std::optional<DisplayId> DEFAULT_DISPLAY_ID = std::make_optional(DisplayId{123u});
+const std::string DEFAULT_DISPLAY_NAME = "Mock Display";
+
+using testing::_;
+using testing::ByMove;
+using testing::DoAll;
+using testing::Ref;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SetArgPointee;
+using testing::StrictMock;
+
+class RenderSurfaceTest : public testing::Test {
+public:
+    RenderSurfaceTest() {
+        EXPECT_CALL(mDisplay, getId()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_ID));
+        EXPECT_CALL(mDisplay, getName()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_NAME));
+        EXPECT_CALL(mCompositionEngine, getHwComposer).WillRepeatedly(ReturnRef(mHwComposer));
+        EXPECT_CALL(mCompositionEngine, getRenderEngine).WillRepeatedly(ReturnRef(mRenderEngine));
+    }
+    ~RenderSurfaceTest() override = default;
+
+    StrictMock<android::mock::HWComposer> mHwComposer;
+    StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    StrictMock<mock::Display> mDisplay;
+    sp<MockNativeWindow> mNativeWindow = new StrictMock<MockNativeWindow>();
+    sp<mock::DisplaySurface> mDisplaySurface = new StrictMock<mock::DisplaySurface>();
+    impl::RenderSurface mSurface{mCompositionEngine, mDisplay,
+                                 RenderSurfaceCreationArgs{DEFAULT_DISPLAY_WIDTH,
+                                                           DEFAULT_DISPLAY_HEIGHT, mNativeWindow,
+                                                           mDisplaySurface}};
+};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(RenderSurfaceTest, canInstantiate) {
+    EXPECT_TRUE(mSurface.isValid());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::initialize()
+ */
+
+TEST_F(RenderSurfaceTest, initializeConfiguresNativeWindow) {
+    EXPECT_CALL(*mNativeWindow, connect(NATIVE_WINDOW_API_EGL)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mNativeWindow, setBuffersFormat(HAL_PIXEL_FORMAT_RGBA_8888))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+
+    mSurface.initialize();
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::getSize()
+ */
+
+TEST_F(RenderSurfaceTest, sizeReturnsConstructedSize) {
+    const ui::Size expected{DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT};
+
+    EXPECT_EQ(expected, mSurface.getSize());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::getClientTargetAcquireFence()
+ */
+
+TEST_F(RenderSurfaceTest, getClientTargetAcquireFenceForwardsCall) {
+    sp<Fence> fence = new Fence();
+
+    EXPECT_CALL(*mDisplaySurface, getClientTargetAcquireFence()).WillOnce(ReturnRef(fence));
+
+    EXPECT_EQ(fence.get(), mSurface.getClientTargetAcquireFence().get());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::setDisplaySize()
+ */
+
+TEST_F(RenderSurfaceTest, setDisplaySizeAppliesChange) {
+    EXPECT_CALL(*mDisplaySurface, resizeBuffers(640, 480)).Times(1);
+
+    mSurface.setDisplaySize(ui::Size(640, 480));
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::setBufferDataspace()
+ */
+
+TEST_F(RenderSurfaceTest, setBufferDataspaceAppliesChange) {
+    EXPECT_CALL(*mNativeWindow, setBuffersDataSpace(ui::Dataspace::DISPLAY_P3))
+            .WillOnce(Return(NO_ERROR));
+
+    mSurface.setBufferDataspace(ui::Dataspace::DISPLAY_P3);
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::setProtected()
+ */
+
+TEST_F(RenderSurfaceTest, setProtectedTrueEnablesProtection) {
+    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED))
+            .WillOnce(Return(NO_ERROR));
+
+    mSurface.setProtected(true);
+}
+
+TEST_F(RenderSurfaceTest, setProtectedFalseDisablesProtection) {
+    EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR));
+
+    mSurface.setProtected(false);
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::beginFrame()
+ */
+
+TEST_F(RenderSurfaceTest, beginFrameAppliesChange) {
+    EXPECT_CALL(*mDisplaySurface, beginFrame(true)).WillOnce(Return(NO_ERROR));
+
+    EXPECT_EQ(NO_ERROR, mSurface.beginFrame(true));
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::prepareFrame()
+ */
+
+TEST_F(RenderSurfaceTest, prepareFrameTakesEarlyOutOnHwcError) {
+    std::vector<CompositionInfo> data;
+
+    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(data)))
+            .WillOnce(Return(INVALID_OPERATION));
+
+    EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame(data));
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesMixedComposition) {
+    std::vector<CompositionInfo> data;
+    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(data))).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_MIXED))
+            .WillOnce(Return(INVALID_OPERATION));
+
+    EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame(data));
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGlesComposition) {
+    std::vector<CompositionInfo> data;
+
+    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(data))).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GLES))
+            .WillOnce(Return(NO_ERROR));
+
+    EXPECT_EQ(NO_ERROR, mSurface.prepareFrame(data));
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyHwcComposition) {
+    std::vector<CompositionInfo> data;
+
+    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(data))).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
+            .WillOnce(Return(NO_ERROR));
+
+    EXPECT_EQ(NO_ERROR, mSurface.prepareFrame(data));
+}
+
+TEST_F(RenderSurfaceTest, prepareFrameHandlesNoComposition) {
+    std::vector<CompositionInfo> data;
+
+    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(data))).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
+            .WillOnce(Return(NO_ERROR));
+
+    EXPECT_EQ(NO_ERROR, mSurface.prepareFrame(data));
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::dequeueBuffer()
+ */
+
+TEST_F(RenderSurfaceTest, dequeueBufferObtainsABuffer) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+
+    EXPECT_CALL(*mNativeWindow, dequeueBuffer(_, _))
+            .WillOnce(
+                    DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR)));
+
+    EXPECT_EQ(buffer.get(), mSurface.dequeueBuffer().get());
+
+    EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::queueBuffer()
+ */
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesNoClientComposition) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    mSurface.mutableGraphicBufferForTest() = buffer;
+
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID))
+            .WillOnce(Return(false));
+    EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+    mSurface.queueBuffer();
+
+    EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesClientComposition) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    mSurface.mutableGraphicBufferForTest() = buffer;
+
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+    mSurface.queueBuffer();
+
+    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequest) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    mSurface.mutableGraphicBufferForTest() = buffer;
+
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+    mSurface.queueBuffer();
+
+    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequestWithNoBufferYetDequeued) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(*mNativeWindow, dequeueBuffer(_, _))
+            .WillOnce(
+                    DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR)));
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+    mSurface.queueBuffer();
+
+    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+TEST_F(RenderSurfaceTest, queueBufferHandlesNativeWindowQueueBufferFailureOnVirtualDisplay) {
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    mSurface.mutableGraphicBufferForTest() = buffer;
+
+    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
+            .WillOnce(Return(INVALID_OPERATION));
+    EXPECT_CALL(mDisplay, isVirtual()).WillOnce(Return(true));
+    EXPECT_CALL(*mNativeWindow, cancelBuffer(buffer->getNativeBuffer(), -1))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
+
+    mSurface.queueBuffer();
+
+    EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::onPresentDisplayCompleted()
+ */
+
+TEST_F(RenderSurfaceTest, onPresentDisplayCompletedForwardsSignal) {
+    EXPECT_CALL(*mDisplaySurface, onFrameCommitted()).Times(1);
+
+    mSurface.onPresentDisplayCompleted();
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::finishBuffer()
+ */
+
+TEST_F(RenderSurfaceTest, finishBufferJustFlushesRenderEngine) {
+    int fd = dup(1);
+
+    EXPECT_CALL(mRenderEngine, flush()).WillOnce(Return(ByMove(base::unique_fd(fd))));
+
+    mSurface.finishBuffer();
+
+    EXPECT_EQ(fd, mSurface.mutableBufferReadyForTest().release());
+}
+
+TEST_F(RenderSurfaceTest, finishBufferFlushesAndFinishesRenderEngine) {
+    EXPECT_CALL(mRenderEngine, flush()).WillOnce(Return(ByMove(base::unique_fd(-2))));
+    EXPECT_CALL(mRenderEngine, finish()).Times(1);
+
+    mSurface.finishBuffer();
+
+    EXPECT_EQ(-2, mSurface.mutableBufferReadyForTest().release());
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::setViewportAndProjection()
+ */
+
+TEST_F(RenderSurfaceTest, setViewportAndProjectionAppliesChang) {
+    mSurface.setSizeForTest(ui::Size(100, 200));
+
+    EXPECT_CALL(mRenderEngine,
+                setViewportAndProjection(100, 200, Rect(100, 200), ui::Transform::ROT_0))
+            .Times(1);
+
+    mSurface.setViewportAndProjection();
+}
+
+/* ------------------------------------------------------------------------
+ * RenderSurface::flip()
+ */
+
+TEST_F(RenderSurfaceTest, flipForwardsSignal) {
+    mSurface.setPageFlipCountForTest(500);
+
+    mSurface.flip();
+
+    EXPECT_EQ(501, mSurface.getPageFlipCount());
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h b/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
new file mode 100644
index 0000000..ea07bed
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+namespace {
+
+// Check for a transform match
+MATCHER_P(TransformEq, expected, "") {
+    std::string buf;
+    buf.append("Transforms are not equal\n");
+    expected.dump(buf, "expected transform");
+    arg.dump(buf, "actual transform");
+    *result_listener << buf;
+
+    const float TOLERANCE = 1e-3f;
+
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            if (std::fabs(expected[i][j] - arg[i][j]) > TOLERANCE) {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+} // namespace
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index e7b7fbe..54111de 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -18,47 +18,28 @@
 #undef LOG_TAG
 #define LOG_TAG "DisplayDevice"
 
-#include "DisplayDevice.h"
-
-#include <array>
-#include <unordered_set>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-
 #include <android-base/stringprintf.h>
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/DisplaySurface.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <configstore/Utils.h>
-#include <cutils/properties.h>
-#include <gui/Surface.h>
-#include <hardware/gralloc.h>
-#include <renderengine/RenderEngine.h>
-#include <sync/sync.h>
+#include <log/log.h>
 #include <system/window.h>
-#include <ui/DebugUtils.h>
-#include <ui/DisplayInfo.h>
-#include <ui/PixelFormat.h>
-#include <utils/Log.h>
-#include <utils/RefBase.h>
+#include <ui/GraphicTypes.h>
 
-#include "DisplayHardware/DisplaySurface.h"
-#include "DisplayHardware/HWComposer.h"
-#include "DisplayHardware/HWC2.h"
-#include "SurfaceFlinger.h"
+#include "DisplayDevice.h"
 #include "Layer.h"
+#include "SurfaceFlinger.h"
 
 namespace android {
 
-// retrieve triple buffer setting from configstore
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
 using android::base::StringAppendF;
-using android::ui::ColorMode;
-using android::ui::Dataspace;
-using android::ui::Hdr;
-using android::ui::RenderIntent;
 
 /*
  * Initialize the display to the specified values.
@@ -67,413 +48,72 @@
 
 uint32_t DisplayDevice::sPrimaryDisplayOrientation = 0;
 
-namespace {
-
-// ordered list of known SDR color modes
-const std::array<ColorMode, 3> sSdrColorModes = {
-        ColorMode::DISPLAY_BT2020,
-        ColorMode::DISPLAY_P3,
-        ColorMode::SRGB,
-};
-
-// ordered list of known HDR color modes
-const std::array<ColorMode, 2> sHdrColorModes = {
-        ColorMode::BT2100_PQ,
-        ColorMode::BT2100_HLG,
-};
-
-// ordered list of known SDR render intents
-const std::array<RenderIntent, 2> sSdrRenderIntents = {
-        RenderIntent::ENHANCE,
-        RenderIntent::COLORIMETRIC,
-};
-
-// ordered list of known HDR render intents
-const std::array<RenderIntent, 2> sHdrRenderIntents = {
-        RenderIntent::TONE_MAP_ENHANCE,
-        RenderIntent::TONE_MAP_COLORIMETRIC,
-};
-
-// map known color mode to dataspace
-Dataspace colorModeToDataspace(ColorMode mode) {
-    switch (mode) {
-        case ColorMode::SRGB:
-            return Dataspace::V0_SRGB;
-        case ColorMode::DISPLAY_P3:
-            return Dataspace::DISPLAY_P3;
-        case ColorMode::DISPLAY_BT2020:
-            return Dataspace::DISPLAY_BT2020;
-        case ColorMode::BT2100_HLG:
-            return Dataspace::BT2020_HLG;
-        case ColorMode::BT2100_PQ:
-            return Dataspace::BT2020_PQ;
-        default:
-            return Dataspace::UNKNOWN;
-    }
-}
-
-// Return a list of candidate color modes.
-std::vector<ColorMode> getColorModeCandidates(ColorMode mode) {
-    std::vector<ColorMode> candidates;
-
-    // add mode itself
-    candidates.push_back(mode);
-
-    // check if mode is HDR
-    bool isHdr = false;
-    for (auto hdrMode : sHdrColorModes) {
-        if (hdrMode == mode) {
-            isHdr = true;
-            break;
-        }
-    }
-
-    // add other HDR candidates when mode is HDR
-    if (isHdr) {
-        for (auto hdrMode : sHdrColorModes) {
-            if (hdrMode != mode) {
-                candidates.push_back(hdrMode);
-            }
-        }
-    }
-
-    // add other SDR candidates
-    for (auto sdrMode : sSdrColorModes) {
-        if (sdrMode != mode) {
-            candidates.push_back(sdrMode);
-        }
-    }
-
-    return candidates;
-}
-
-// Return a list of candidate render intents.
-std::vector<RenderIntent> getRenderIntentCandidates(RenderIntent intent) {
-    std::vector<RenderIntent> candidates;
-
-    // add intent itself
-    candidates.push_back(intent);
-
-    // check if intent is HDR
-    bool isHdr = false;
-    for (auto hdrIntent : sHdrRenderIntents) {
-        if (hdrIntent == intent) {
-            isHdr = true;
-            break;
-        }
-    }
-
-    if (isHdr) {
-        // add other HDR candidates when intent is HDR
-        for (auto hdrIntent : sHdrRenderIntents) {
-            if (hdrIntent != intent) {
-                candidates.push_back(hdrIntent);
-            }
-        }
-    } else {
-        // add other SDR candidates when intent is SDR
-        for (auto sdrIntent : sSdrRenderIntents) {
-            if (sdrIntent != intent) {
-                candidates.push_back(sdrIntent);
-            }
-        }
-    }
-
-    return candidates;
-}
-
-// Return the best color mode supported by HWC.
-ColorMode getHwcColorMode(
-        const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
-        ColorMode mode) {
-    std::vector<ColorMode> candidates = getColorModeCandidates(mode);
-    for (auto candidate : candidates) {
-        auto iter = hwcColorModes.find(candidate);
-        if (iter != hwcColorModes.end()) {
-            return candidate;
-        }
-    }
-
-    return ColorMode::NATIVE;
-}
-
-// Return the best render intent supported by HWC.
-RenderIntent getHwcRenderIntent(const std::vector<RenderIntent>& hwcIntents, RenderIntent intent) {
-    std::vector<RenderIntent> candidates = getRenderIntentCandidates(intent);
-    for (auto candidate : candidates) {
-        for (auto hwcIntent : hwcIntents) {
-            if (candidate == hwcIntent) {
-                return candidate;
-            }
-        }
-    }
-
-    return RenderIntent::COLORIMETRIC;
-}
-
-} // anonymous namespace
-
 DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(const sp<SurfaceFlinger>& flinger,
                                                      const wp<IBinder>& displayToken,
                                                      const std::optional<DisplayId>& displayId)
       : flinger(flinger), displayToken(displayToken), displayId(displayId) {}
 
 DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs&& args)
-      : lastCompositionHadVisibleLayers(false),
-        mFlinger(args.flinger),
+      : mFlinger(args.flinger),
         mDisplayToken(args.displayToken),
         mSequenceId(args.sequenceId),
-        mId(args.displayId),
-        mNativeWindow(args.nativeWindow),
-        mGraphicBuffer(nullptr),
-        mDisplaySurface(args.displaySurface),
         mDisplayInstallOrientation(args.displayInstallOrientation),
-        mPageFlipCount(0),
+        mCompositionDisplay{mFlinger->getCompositionEngine().createDisplay(
+                compositionengine::DisplayCreationArgs{args.isSecure, args.isVirtual,
+                                                       args.displayId})},
         mIsVirtual(args.isVirtual),
-        mIsSecure(args.isSecure),
-        mLayerStack(NO_LAYER_STACK),
         mOrientation(),
-        mViewport(Rect::INVALID_RECT),
-        mFrame(Rect::INVALID_RECT),
-        mPowerMode(args.initialPowerMode),
         mActiveConfig(0),
-        mColorTransform(HAL_COLOR_TRANSFORM_IDENTITY),
-        mHasWideColorGamut(args.hasWideColorGamut),
-        mHasHdr10Plus(false),
-        mHasHdr10(false),
-        mHasHLG(false),
-        mHasDolbyVision(false),
-        mSupportedPerFrameMetadata(args.supportedPerFrameMetadata),
         mIsPrimary(args.isPrimary) {
-    populateColorModes(args.hwcColorModes);
+    mCompositionDisplay->createRenderSurface(
+            compositionengine::RenderSurfaceCreationArgs{ANativeWindow_getWidth(
+                                                                 args.nativeWindow.get()),
+                                                         ANativeWindow_getHeight(
+                                                                 args.nativeWindow.get()),
+                                                         args.nativeWindow, args.displaySurface});
 
-    ALOGE_IF(!mNativeWindow, "No native window was set for display");
-    ALOGE_IF(!mDisplaySurface, "No display surface was set for display");
+    mCompositionDisplay->createDisplayColorProfile(
+            compositionengine::DisplayColorProfileCreationArgs{args.hasWideColorGamut,
+                                                               std::move(args.hdrCapabilities),
+                                                               args.supportedPerFrameMetadata,
+                                                               args.hwcColorModes});
 
-    std::vector<Hdr> types = args.hdrCapabilities.getSupportedHdrTypes();
-    for (Hdr hdrType : types) {
-        switch (hdrType) {
-            case Hdr::HDR10_PLUS:
-                mHasHdr10Plus = true;
-                break;
-            case Hdr::HDR10:
-                mHasHdr10 = true;
-                break;
-            case Hdr::HLG:
-                mHasHLG = true;
-                break;
-            case Hdr::DOLBY_VISION:
-                mHasDolbyVision = true;
-                break;
-            default:
-                ALOGE("UNKNOWN HDR capability: %d", static_cast<int32_t>(hdrType));
-        }
+    if (!mCompositionDisplay->isValid()) {
+        ALOGE("Composition Display did not validate!");
     }
 
-    float minLuminance = args.hdrCapabilities.getDesiredMinLuminance();
-    float maxLuminance = args.hdrCapabilities.getDesiredMaxLuminance();
-    float maxAverageLuminance = args.hdrCapabilities.getDesiredMaxAverageLuminance();
+    mCompositionDisplay->getRenderSurface()->initialize();
 
-    minLuminance = minLuminance <= 0.0 ? sDefaultMinLumiance : minLuminance;
-    maxLuminance = maxLuminance <= 0.0 ? sDefaultMaxLumiance : maxLuminance;
-    maxAverageLuminance = maxAverageLuminance <= 0.0 ? sDefaultMaxLumiance : maxAverageLuminance;
-    if (this->hasWideColorGamut()) {
-        // insert HDR10/HLG as we will force client composition for HDR10/HLG
-        // layers
-        if (!hasHDR10Support()) {
-            types.push_back(Hdr::HDR10);
-        }
-
-        if (!hasHLGSupport()) {
-            types.push_back(Hdr::HLG);
-        }
-    }
-    mHdrCapabilities = HdrCapabilities(types, maxLuminance, maxAverageLuminance, minLuminance);
-
-    ANativeWindow* const window = mNativeWindow.get();
-
-    int status = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
-    ALOGE_IF(status != NO_ERROR, "Unable to connect BQ producer: %d", status);
-    status = native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
-    ALOGE_IF(status != NO_ERROR, "Unable to set BQ format to RGBA888: %d", status);
-    status = native_window_set_usage(window, GRALLOC_USAGE_HW_RENDER);
-    ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for GPU rendering: %d", status);
-
-    mDisplayWidth = ANativeWindow_getWidth(window);
-    mDisplayHeight = ANativeWindow_getHeight(window);
+    setPowerMode(args.initialPowerMode);
 
     // initialize the display orientation transform.
-    setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);
+    setProjection(DisplayState::eOrientationDefault, Rect::INVALID_RECT, Rect::INVALID_RECT);
 }
 
 DisplayDevice::~DisplayDevice() = default;
 
-void DisplayDevice::disconnect(HWComposer& hwc) {
-    if (mId) {
-        hwc.disconnectDisplay(*mId);
-        mId.reset();
-    }
+void DisplayDevice::disconnect() {
+    mCompositionDisplay->disconnect();
 }
 
 int DisplayDevice::getWidth() const {
-    return mDisplayWidth;
+    return mCompositionDisplay->getState().bounds.getWidth();
 }
 
 int DisplayDevice::getHeight() const {
-    return mDisplayHeight;
+    return mCompositionDisplay->getState().bounds.getHeight();
 }
 
 void DisplayDevice::setDisplayName(const std::string& displayName) {
     if (!displayName.empty()) {
         // never override the name with an empty name
         mDisplayName = displayName;
+        mCompositionDisplay->setName(displayName);
     }
 }
 
 uint32_t DisplayDevice::getPageFlipCount() const {
-    return mPageFlipCount;
-}
-
-void DisplayDevice::flip() const
-{
-    mPageFlipCount++;
-}
-
-status_t DisplayDevice::beginFrame(bool mustRecompose) const {
-    return mDisplaySurface->beginFrame(mustRecompose);
-}
-
-status_t DisplayDevice::prepareFrame(HWComposer& hwc,
-        std::vector<CompositionInfo>& compositionData) {
-    if (mId) {
-        status_t error = hwc.prepare(*mId, compositionData);
-        if (error != NO_ERROR) {
-            return error;
-        }
-    }
-
-    DisplaySurface::CompositionType compositionType;
-    bool hasClient = hwc.hasClientComposition(mId);
-    bool hasDevice = hwc.hasDeviceComposition(mId);
-    if (hasClient && hasDevice) {
-        compositionType = DisplaySurface::COMPOSITION_MIXED;
-    } else if (hasClient) {
-        compositionType = DisplaySurface::COMPOSITION_GLES;
-    } else if (hasDevice) {
-        compositionType = DisplaySurface::COMPOSITION_HWC;
-    } else {
-        // Nothing to do -- when turning the screen off we get a frame like
-        // this. Call it a HWC frame since we won't be doing any GLES work but
-        // will do a prepare/set cycle.
-        compositionType = DisplaySurface::COMPOSITION_HWC;
-    }
-    return mDisplaySurface->prepareFrame(compositionType);
-}
-
-void DisplayDevice::setProtected(bool useProtected) {
-    uint64_t usageFlags = GRALLOC_USAGE_HW_RENDER;
-    if (useProtected) {
-        usageFlags |= GRALLOC_USAGE_PROTECTED;
-    }
-    const int status = native_window_set_usage(mNativeWindow.get(), usageFlags);
-    ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for protected content: %d", status);
-}
-
-sp<GraphicBuffer> DisplayDevice::dequeueBuffer() {
-    int fd;
-    ANativeWindowBuffer* buffer;
-
-    status_t res = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fd);
-
-    if (res != NO_ERROR) {
-        ALOGE("ANativeWindow::dequeueBuffer failed for display [%s] with error: %d",
-              getDisplayName().c_str(), res);
-        // Return fast here as we can't do much more - any rendering we do
-        // now will just be wrong.
-        return mGraphicBuffer;
-    }
-
-    ALOGW_IF(mGraphicBuffer != nullptr, "Clobbering a non-null pointer to a buffer [%p].",
-             mGraphicBuffer->getNativeBuffer()->handle);
-    mGraphicBuffer = GraphicBuffer::from(buffer);
-
-    // Block until the buffer is ready
-    // TODO(alecmouri): it's perhaps more appropriate to block renderengine so
-    // that the gl driver can block instead.
-    if (fd >= 0) {
-        sync_wait(fd, -1);
-        close(fd);
-    }
-
-    return mGraphicBuffer;
-}
-
-void DisplayDevice::queueBuffer(HWComposer& hwc) {
-    if (hwc.hasClientComposition(mId) || hwc.hasFlipClientTargetRequest(mId)) {
-        // hasFlipClientTargetRequest could return true even if we haven't
-        // dequeued a buffer before. Try dequeueing one if we don't have a
-        // buffer ready.
-        if (mGraphicBuffer == nullptr) {
-            ALOGI("Attempting to queue a client composited buffer without one "
-                  "previously dequeued for display [%s]. Attempting to dequeue "
-                  "a scratch buffer now",
-                  mDisplayName.c_str());
-            // We shouldn't deadlock here, since mGraphicBuffer == nullptr only
-            // after a successful call to queueBuffer, or if dequeueBuffer has
-            // never been called.
-            dequeueBuffer();
-        }
-
-        if (mGraphicBuffer == nullptr) {
-            ALOGE("No buffer is ready for display [%s]", mDisplayName.c_str());
-        } else {
-            status_t res = mNativeWindow->queueBuffer(mNativeWindow.get(),
-                                                      mGraphicBuffer->getNativeBuffer(),
-                                                      dup(mBufferReady));
-            if (res != NO_ERROR) {
-                ALOGE("Error when queueing buffer for display [%s]: %d", mDisplayName.c_str(), res);
-                // We risk blocking on dequeueBuffer if the primary display failed
-                // to queue up its buffer, so crash here.
-                if (isPrimary()) {
-                    LOG_ALWAYS_FATAL("ANativeWindow::queueBuffer failed with error: %d", res);
-                } else {
-                    mNativeWindow->cancelBuffer(mNativeWindow.get(),
-                                                mGraphicBuffer->getNativeBuffer(),
-                                                dup(mBufferReady));
-                }
-            }
-
-            mBufferReady.reset();
-            mGraphicBuffer = nullptr;
-        }
-    }
-
-    status_t result = mDisplaySurface->advanceFrame();
-    if (result != NO_ERROR) {
-        ALOGE("[%s] failed pushing new frame to HWC: %d", mDisplayName.c_str(), result);
-    }
-}
-
-void DisplayDevice::onPresentDisplayCompleted() {
-    mDisplaySurface->onFrameCommitted();
-}
-
-void DisplayDevice::setViewportAndProjection() const {
-    size_t w = mDisplayWidth;
-    size_t h = mDisplayHeight;
-    Rect sourceCrop(0, 0, w, h);
-    mFlinger->getRenderEngine().setViewportAndProjection(w, h, sourceCrop, ui::Transform::ROT_0);
-}
-
-void DisplayDevice::finishBuffer() {
-    mBufferReady = mFlinger->getRenderEngine().flush();
-    if (mBufferReady.get() < 0) {
-        mFlinger->getRenderEngine().finish();
-    }
-}
-
-const sp<Fence>& DisplayDevice::getClientTargetAcquireFence() const {
-    return mDisplaySurface->getClientTargetAcquireFence();
+    return mCompositionDisplay->getRenderSurface()->getPageFlipCount();
 }
 
 // ----------------------------------------------------------------------------
@@ -494,21 +134,10 @@
     return mLayersNeedingFences;
 }
 
-Region DisplayDevice::getDirtyRegion(bool repaintEverything) const {
-    Region dirty;
-    if (repaintEverything) {
-        dirty.set(getBounds());
-    } else {
-        const ui::Transform& planeTransform(mGlobalTransform);
-        dirty = planeTransform.transform(this->dirtyRegion);
-        dirty.andSelf(getBounds());
-    }
-    return dirty;
-}
-
 // ----------------------------------------------------------------------------
 void DisplayDevice::setPowerMode(int mode) {
     mPowerMode = mode;
+    getCompositionDisplay()->setCompositionEnabled(mPowerMode != HWC_POWER_MODE_OFF);
 }
 
 int DisplayDevice::getPowerMode()  const {
@@ -529,88 +158,37 @@
 }
 
 // ----------------------------------------------------------------------------
-void DisplayDevice::setActiveColorMode(ColorMode mode) {
-    mActiveColorMode = mode;
-}
-
-ColorMode DisplayDevice::getActiveColorMode() const {
-    return mActiveColorMode;
-}
-
-RenderIntent DisplayDevice::getActiveRenderIntent() const {
-    return mActiveRenderIntent;
-}
-
-void DisplayDevice::setActiveRenderIntent(RenderIntent renderIntent) {
-    mActiveRenderIntent = renderIntent;
-}
-
-void DisplayDevice::setColorTransform(const mat4& transform) {
-    const bool isIdentity = (transform == mat4());
-    mColorTransform =
-            isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
-}
-
-android_color_transform_t DisplayDevice::getColorTransform() const {
-    return mColorTransform;
-}
-
-void DisplayDevice::setCompositionDataSpace(ui::Dataspace dataspace) {
-    mCompositionDataSpace = dataspace;
-    ANativeWindow* const window = mNativeWindow.get();
-    native_window_set_buffers_data_space(window, static_cast<android_dataspace>(dataspace));
-}
 
 ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
-    return mCompositionDataSpace;
+    return mCompositionDisplay->getState().dataspace;
 }
 
 // ----------------------------------------------------------------------------
 
 void DisplayDevice::setLayerStack(uint32_t stack) {
-    mLayerStack = stack;
-    dirtyRegion.set(bounds());
+    mCompositionDisplay->setLayerStackFilter(!isPrimary(), stack);
 }
 
 // ----------------------------------------------------------------------------
 
-uint32_t DisplayDevice::getOrientationTransform() const {
-    uint32_t transform = 0;
-    switch (mOrientation) {
-        case DisplayState::eOrientationDefault:
-            transform = ui::Transform::ROT_0;
-            break;
-        case DisplayState::eOrientation90:
-            transform = ui::Transform::ROT_90;
-            break;
-        case DisplayState::eOrientation180:
-            transform = ui::Transform::ROT_180;
-            break;
-        case DisplayState::eOrientation270:
-            transform = ui::Transform::ROT_270;
-            break;
-    }
-    return transform;
-}
-
-status_t DisplayDevice::orientationToTransfrom(
-        int orientation, int w, int h, ui::Transform* tr)
-{
-    uint32_t flags = 0;
+uint32_t DisplayDevice::displayStateOrientationToTransformOrientation(int orientation) {
     switch (orientation) {
     case DisplayState::eOrientationDefault:
-        flags = ui::Transform::ROT_0;
-        break;
+        return ui::Transform::ROT_0;
     case DisplayState::eOrientation90:
-        flags = ui::Transform::ROT_90;
-        break;
+        return ui::Transform::ROT_90;
     case DisplayState::eOrientation180:
-        flags = ui::Transform::ROT_180;
-        break;
+        return ui::Transform::ROT_180;
     case DisplayState::eOrientation270:
-        flags = ui::Transform::ROT_270;
-        break;
+        return ui::Transform::ROT_270;
     default:
+        return ui::Transform::ROT_INVALID;
+    }
+}
+
+status_t DisplayDevice::orientationToTransfrom(int orientation, int w, int h, ui::Transform* tr) {
+    uint32_t flags = displayStateOrientationToTransformOrientation(orientation);
+    if (flags == ui::Transform::ROT_INVALID) {
         return BAD_VALUE;
     }
     tr->set(flags, w, h);
@@ -618,12 +196,7 @@
 }
 
 void DisplayDevice::setDisplaySize(const int newWidth, const int newHeight) {
-    dirtyRegion.set(getBounds());
-
-    mDisplaySurface->resizeBuffers(newWidth, newHeight);
-
-    mDisplayWidth = newWidth;
-    mDisplayHeight = newHeight;
+    mCompositionDisplay->setBounds(ui::Size(newWidth, newHeight));
 }
 
 void DisplayDevice::setProjection(int orientation,
@@ -631,8 +204,11 @@
     Rect viewport(newViewport);
     Rect frame(newFrame);
 
-    const int w = mDisplayWidth;
-    const int h = mDisplayHeight;
+    mOrientation = orientation;
+
+    const Rect& displayBounds = getCompositionDisplay()->getState().bounds;
+    const int w = displayBounds.width();
+    const int h = displayBounds.height();
 
     ui::Transform R;
     DisplayDevice::orientationToTransfrom(orientation, w, h, &R);
@@ -656,8 +232,6 @@
         }
     }
 
-    dirtyRegion.set(getBounds());
-
     ui::Transform TL, TP, S;
     float src_width  = viewport.width();
     float src_height = viewport.height();
@@ -676,7 +250,7 @@
     TL.set(-src_x, -src_y);
     TP.set(dst_x, dst_y);
 
-    // need to take care of primary display rotation for mGlobalTransform
+    // need to take care of primary display rotation for globalTransform
     // for case if the panel is not installed aligned with device orientation
     if (isPrimary()) {
         DisplayDevice::orientationToTransfrom(
@@ -687,38 +261,25 @@
     // The viewport and frame are both in the logical orientation.
     // Apply the logical translation, scale to physical size, apply the
     // physical translation and finally rotate to the physical orientation.
-    mGlobalTransform = R * TP * S * TL;
+    ui::Transform globalTransform = R * TP * S * TL;
 
-    const uint8_t type = mGlobalTransform.getType();
-    mNeedsFiltering = (!mGlobalTransform.preserveRects() ||
-            (type >= ui::Transform::SCALE));
+    const uint8_t type = globalTransform.getType();
+    const bool needsFiltering =
+            (!globalTransform.preserveRects() || (type >= ui::Transform::SCALE));
 
-    mScissor = mGlobalTransform.transform(viewport);
-    if (mScissor.isEmpty()) {
-        mScissor = getBounds();
+    Rect scissor = globalTransform.transform(viewport);
+    if (scissor.isEmpty()) {
+        scissor = displayBounds;
     }
 
-    mOrientation = orientation;
     if (isPrimary()) {
-        uint32_t transform = 0;
-        switch (mOrientation) {
-            case DisplayState::eOrientationDefault:
-                transform = ui::Transform::ROT_0;
-                break;
-            case DisplayState::eOrientation90:
-                transform = ui::Transform::ROT_90;
-                break;
-            case DisplayState::eOrientation180:
-                transform = ui::Transform::ROT_180;
-                break;
-            case DisplayState::eOrientation270:
-                transform = ui::Transform::ROT_270;
-                break;
-        }
-        sPrimaryDisplayOrientation = transform;
+        sPrimaryDisplayOrientation = displayStateOrientationToTransformOrientation(orientation);
     }
-    mViewport = viewport;
-    mFrame = frame;
+
+    getCompositionDisplay()->setProjection(globalTransform,
+                                           displayStateOrientationToTransformOrientation(
+                                                   orientation),
+                                           frame, viewport, scissor, needsFiltering);
 }
 
 uint32_t DisplayDevice::getPrimaryDisplayOrientationTransform() {
@@ -726,149 +287,94 @@
 }
 
 std::string DisplayDevice::getDebugName() const {
-    const auto id = mId ? to_string(*mId) + ", " : std::string();
+    const auto id = getId() ? to_string(*getId()) + ", " : std::string();
     return base::StringPrintf("DisplayDevice{%s%s%s\"%s\"}", id.c_str(),
                               isPrimary() ? "primary, " : "", isVirtual() ? "virtual, " : "",
                               mDisplayName.c_str());
 }
 
 void DisplayDevice::dump(std::string& result) const {
-    const ui::Transform& tr(mGlobalTransform);
-    ANativeWindow* const window = mNativeWindow.get();
     StringAppendF(&result, "+ %s\n", getDebugName().c_str());
-    StringAppendF(&result,
-                  "  layerStack=%u, (%4dx%4d), ANativeWindow=%p "
-                  "format=%d, orient=%2d (type=%08x), flips=%u, isSecure=%d, "
-                  "powerMode=%d, activeConfig=%d, numLayers=%zu\n",
-                  mLayerStack, mDisplayWidth, mDisplayHeight, window,
-                  ANativeWindow_getFormat(window), mOrientation, tr.getType(), getPageFlipCount(),
-                  mIsSecure, mPowerMode, mActiveConfig, mVisibleLayersSortedByZ.size());
-    StringAppendF(&result,
-                  "   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",
-                  mViewport.left, mViewport.top, mViewport.right, mViewport.bottom, mFrame.left,
-                  mFrame.top, mFrame.right, mFrame.bottom, mScissor.left, mScissor.top,
-                  mScissor.right, mScissor.bottom, tr[0][0], tr[1][0], tr[2][0], tr[0][1], tr[1][1],
-                  tr[2][1], tr[0][2], tr[1][2], tr[2][2]);
-    auto const surface = static_cast<Surface*>(window);
-    ui::Dataspace dataspace = surface->getBuffersDataSpace();
-    StringAppendF(&result,
-                  "   wideColorGamut=%d, hdr10plus =%d, hdr10=%d, colorMode=%s, dataspace: %s "
-                  "(%d)\n",
-                  mHasWideColorGamut, mHasHdr10Plus, mHasHdr10,
-                  decodeColorMode(mActiveColorMode).c_str(),
-                  dataspaceDetails(static_cast<android_dataspace>(dataspace)).c_str(), dataspace);
 
-    String8 surfaceDump;
-    mDisplaySurface->dumpAsString(surfaceDump);
-    result.append(surfaceDump.string(), surfaceDump.size());
+    result.append("   ");
+    StringAppendF(&result, "powerMode=%d, ", mPowerMode);
+    StringAppendF(&result, "activeConfig=%d, ", mActiveConfig);
+    StringAppendF(&result, "numLayers=%zu\n", mVisibleLayersSortedByZ.size());
+    getCompositionDisplay()->dump(result);
 }
 
-// Map dataspace/intent to the best matched dataspace/colorMode/renderIntent
-// supported by HWC.
-void DisplayDevice::addColorMode(
-        const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes,
-        const ColorMode mode, const RenderIntent intent) {
-    // find the best color mode
-    const ColorMode hwcColorMode = getHwcColorMode(hwcColorModes, mode);
-
-    // find the best render intent
-    auto iter = hwcColorModes.find(hwcColorMode);
-    const auto& hwcIntents =
-            iter != hwcColorModes.end() ? iter->second : std::vector<RenderIntent>();
-    const RenderIntent hwcIntent = getHwcRenderIntent(hwcIntents, intent);
-
-    const Dataspace dataspace = colorModeToDataspace(mode);
-    const Dataspace hwcDataspace = colorModeToDataspace(hwcColorMode);
-
-    ALOGV("%s: map (%s, %s) to (%s, %s, %s)", getDebugName().c_str(),
-          dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
-          decodeRenderIntent(intent).c_str(),
-          dataspaceDetails(static_cast<android_dataspace_t>(hwcDataspace)).c_str(),
-          decodeColorMode(hwcColorMode).c_str(), decodeRenderIntent(hwcIntent).c_str());
-
-    mColorModes[getColorModeKey(dataspace, intent)] = {hwcDataspace, hwcColorMode, hwcIntent};
+bool DisplayDevice::hasRenderIntent(ui::RenderIntent intent) const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasRenderIntent(intent);
 }
 
-void DisplayDevice::populateColorModes(
-        const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes) {
-    if (!hasWideColorGamut()) {
-        return;
-    }
+// ----------------------------------------------------------------------------
 
-    // collect all known SDR render intents
-    std::unordered_set<RenderIntent> sdrRenderIntents(sSdrRenderIntents.begin(),
-                                                      sSdrRenderIntents.end());
-    auto iter = hwcColorModes.find(ColorMode::SRGB);
-    if (iter != hwcColorModes.end()) {
-        for (auto intent : iter->second) {
-            sdrRenderIntents.insert(intent);
-        }
-    }
-
-    // add all known SDR combinations
-    for (auto intent : sdrRenderIntents) {
-        for (auto mode : sSdrColorModes) {
-            addColorMode(hwcColorModes, mode, intent);
-        }
-    }
-
-    // collect all known HDR render intents
-    std::unordered_set<RenderIntent> hdrRenderIntents(sHdrRenderIntents.begin(),
-                                                      sHdrRenderIntents.end());
-    iter = hwcColorModes.find(ColorMode::BT2100_PQ);
-    if (iter != hwcColorModes.end()) {
-        for (auto intent : iter->second) {
-            hdrRenderIntents.insert(intent);
-        }
-    }
-
-    // add all known HDR combinations
-    for (auto intent : sHdrRenderIntents) {
-        for (auto mode : sHdrColorModes) {
-            addColorMode(hwcColorModes, mode, intent);
-        }
-    }
+const std::optional<DisplayId>& DisplayDevice::getId() const {
+    return mCompositionDisplay->getId();
 }
 
-bool DisplayDevice::hasRenderIntent(RenderIntent intent) const {
-    // assume a render intent is supported when SRGB supports it; we should
-    // get rid of that assumption.
-    auto iter = mColorModes.find(getColorModeKey(Dataspace::V0_SRGB, intent));
-    return iter != mColorModes.end() && iter->second.renderIntent == intent;
+bool DisplayDevice::isSecure() const {
+    return mCompositionDisplay->isSecure();
 }
 
-bool DisplayDevice::hasLegacyHdrSupport(Dataspace dataspace) const {
-    if ((dataspace == Dataspace::BT2020_PQ && hasHDR10Support()) ||
-        (dataspace == Dataspace::BT2020_HLG && hasHLGSupport())) {
-        auto iter =
-                mColorModes.find(getColorModeKey(dataspace, RenderIntent::TONE_MAP_COLORIMETRIC));
-        return iter == mColorModes.end() || iter->second.dataspace != dataspace;
-    }
-
-    return false;
+const Rect& DisplayDevice::getBounds() const {
+    return mCompositionDisplay->getState().bounds;
 }
 
-void DisplayDevice::getBestColorMode(Dataspace dataspace, RenderIntent intent,
-                                     Dataspace* outDataspace, ColorMode* outMode,
-                                     RenderIntent* outIntent) const {
-    auto iter = mColorModes.find(getColorModeKey(dataspace, intent));
-    if (iter != mColorModes.end()) {
-        *outDataspace = iter->second.dataspace;
-        *outMode = iter->second.colorMode;
-        *outIntent = iter->second.renderIntent;
-    } else {
-        // this is unexpected on a WCG display
-        if (hasWideColorGamut()) {
-            ALOGE("map unknown (%s)/(%s) to default color mode",
-                  dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
-                  decodeRenderIntent(intent).c_str());
-        }
+const Region& DisplayDevice::getUndefinedRegion() const {
+    return mCompositionDisplay->getState().undefinedRegion;
+}
 
-        *outDataspace = Dataspace::UNKNOWN;
-        *outMode = ColorMode::NATIVE;
-        *outIntent = RenderIntent::COLORIMETRIC;
-    }
+bool DisplayDevice::needsFiltering() const {
+    return mCompositionDisplay->getState().needsFiltering;
+}
+
+uint32_t DisplayDevice::getLayerStack() const {
+    return mCompositionDisplay->getState().singleLayerStackId;
+}
+
+const ui::Transform& DisplayDevice::getTransform() const {
+    return mCompositionDisplay->getState().transform;
+}
+
+const Rect& DisplayDevice::getViewport() const {
+    return mCompositionDisplay->getState().viewport;
+}
+
+const Rect& DisplayDevice::getFrame() const {
+    return mCompositionDisplay->getState().frame;
+}
+
+const Rect& DisplayDevice::getScissor() const {
+    return mCompositionDisplay->getState().scissor;
+}
+
+bool DisplayDevice::hasWideColorGamut() const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasWideColorGamut();
+}
+
+bool DisplayDevice::hasHDR10PlusSupport() const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasHDR10PlusSupport();
+}
+
+bool DisplayDevice::hasHDR10Support() const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasHDR10Support();
+}
+
+bool DisplayDevice::hasHLGSupport() const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasHLGSupport();
+}
+
+bool DisplayDevice::hasDolbyVisionSupport() const {
+    return mCompositionDisplay->getDisplayColorProfile()->hasDolbyVisionSupport();
+}
+
+int DisplayDevice::getSupportedPerFrameMetadata() const {
+    return mCompositionDisplay->getDisplayColorProfile()->getSupportedPerFrameMetadata();
+}
+
+const HdrCapabilities& DisplayDevice::getHdrCapabilities() const {
+    return mCompositionDisplay->getDisplayColorProfile()->getHdrCapabilities();
 }
 
 std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 250a650..9674f0d 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -44,7 +44,6 @@
 
 namespace android {
 
-class DisplaySurface;
 class Fence;
 class HWComposer;
 class IGraphicBufferProducer;
@@ -55,35 +54,33 @@
 struct DisplayDeviceCreationArgs;
 struct DisplayInfo;
 
-class DisplayDevice : public LightRefBase<DisplayDevice>
-{
+namespace compositionengine {
+class Display;
+class DisplaySurface;
+} // namespace compositionengine
+
+class DisplayDevice : public LightRefBase<DisplayDevice> {
 public:
     constexpr static float sDefaultMinLumiance = 0.0;
     constexpr static float sDefaultMaxLumiance = 500.0;
 
-    // region in layer-stack space
-    mutable Region dirtyRegion;
-    // region in screen space
-    Region undefinedRegion;
-    bool lastCompositionHadVisibleLayers;
-
     enum {
         NO_LAYER_STACK = 0xFFFFFFFF,
     };
 
     explicit DisplayDevice(DisplayDeviceCreationArgs&& args);
-    ~DisplayDevice();
+    virtual ~DisplayDevice();
+
+    std::shared_ptr<compositionengine::Display> getCompositionDisplay() const {
+        return mCompositionDisplay;
+    }
 
     bool isVirtual() const { return mIsVirtual; }
     bool isPrimary() const { return mIsPrimary; }
 
     // isSecure indicates whether this display can be trusted to display
     // secure surfaces.
-    bool isSecure() const { return mIsSecure; }
-
-    // Flip the front and back buffers if the back buffer is "dirty".  Might
-    // be instantaneous, might involve copying the frame buffer around.
-    void flip() const;
+    bool isSecure() const;
 
     int         getWidth() const;
     int         getHeight() const;
@@ -93,44 +90,34 @@
     const Vector< sp<Layer> >& getVisibleLayersSortedByZ() const;
     void                    setLayersNeedingFences(const Vector< sp<Layer> >& layers);
     const Vector< sp<Layer> >& getLayersNeedingFences() const;
-    Region                  getDirtyRegion(bool repaintEverything) const;
 
     void                    setLayerStack(uint32_t stack);
     void                    setDisplaySize(const int newWidth, const int newHeight);
     void                    setProjection(int orientation, const Rect& viewport, const Rect& frame);
 
     int                     getOrientation() const { return mOrientation; }
-    uint32_t                getOrientationTransform() const;
     static uint32_t         getPrimaryDisplayOrientationTransform();
-    const ui::Transform&   getTransform() const { return mGlobalTransform; }
-    const Rect              getViewport() const { return mViewport; }
-    const Rect              getFrame() const { return mFrame; }
-    const Rect&             getScissor() const { return mScissor; }
-    bool                    needsFiltering() const { return mNeedsFiltering; }
+    const ui::Transform& getTransform() const;
+    const Rect& getViewport() const;
+    const Rect& getFrame() const;
+    const Rect& getScissor() const;
+    bool needsFiltering() const;
+    uint32_t getLayerStack() const;
 
-    uint32_t                getLayerStack() const { return mLayerStack; }
-
-    const std::optional<DisplayId>& getId() const { return mId; }
+    const std::optional<DisplayId>& getId() const;
     const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
     int32_t getSequenceId() const { return mSequenceId; }
 
-    int32_t getSupportedPerFrameMetadata() const { return mSupportedPerFrameMetadata; }
+    const Region& getUndefinedRegion() const;
 
-    // We pass in mustRecompose so we can keep VirtualDisplaySurface's state
-    // machine happy without actually queueing a buffer if nothing has changed
-    status_t beginFrame(bool mustRecompose) const;
-    status_t prepareFrame(HWComposer& hwc, std::vector<CompositionInfo>& compositionInfo);
+    int32_t getSupportedPerFrameMetadata() const;
 
-    bool hasWideColorGamut() const { return mHasWideColorGamut; }
+    bool hasWideColorGamut() const;
     // Whether h/w composer has native support for specific HDR type.
-    bool hasHDR10PlusSupport() const { return mHasHdr10Plus; }
-    bool hasHDR10Support() const { return mHasHdr10; }
-    bool hasHLGSupport() const { return mHasHLG; }
-    bool hasDolbyVisionSupport() const { return mHasDolbyVision; }
-
-    // Return true if the HDR dataspace is supported but
-    // there is no corresponding color mode.
-    bool hasLegacyHdrSupport(ui::Dataspace dataspace) const;
+    bool hasHDR10PlusSupport() const;
+    bool hasHDR10Support() const;
+    bool hasHLGSupport() const;
+    bool hasDolbyVisionSupport() const;
 
     // The returned HdrCapabilities is the combination of HDR capabilities from
     // hardware composer and RenderEngine. When the DisplayDevice supports wide
@@ -138,41 +125,17 @@
     // color space for both PQ and HLG HDR contents. The minimum and maximum
     // luminance will be set to sDefaultMinLumiance and sDefaultMaxLumiance
     // respectively if hardware composer doesn't return meaningful values.
-    const HdrCapabilities& getHdrCapabilities() const { return mHdrCapabilities; }
+    const HdrCapabilities& getHdrCapabilities() const;
 
     // Return true if intent is supported by the display.
     bool hasRenderIntent(ui::RenderIntent intent) const;
 
-    void getBestColorMode(ui::Dataspace dataspace, ui::RenderIntent intent,
-                          ui::Dataspace* outDataspace, ui::ColorMode* outMode,
-                          ui::RenderIntent* outIntent) const;
-
-    void setProtected(bool useProtected);
-    // Queues the drawn buffer for consumption by HWC.
-    void queueBuffer(HWComposer& hwc);
-    // Allocates a buffer as scratch space for GPU composition
-    sp<GraphicBuffer> dequeueBuffer();
-
-    // called after h/w composer has completed its set() call
-    void onPresentDisplayCompleted();
-
-    Rect getBounds() const {
-        return Rect(mDisplayWidth, mDisplayHeight);
-    }
-    inline Rect bounds() const { return getBounds(); }
+    const Rect& getBounds() const;
+    const Rect& bounds() const { return getBounds(); }
 
     void setDisplayName(const std::string& displayName);
     const std::string& getDisplayName() const { return mDisplayName; }
 
-    // Acquires a new buffer for GPU composition.
-    void readyNewBuffer();
-    // Marks the current buffer has finished, so that it can be presented and
-    // swapped out.
-    void finishBuffer();
-    void setViewportAndProjection() const;
-
-    const sp<Fence>& getClientTargetAcquireFence() const;
-
     /* ------------------------------------------------------------------------
      * Display power mode management.
      */
@@ -180,13 +143,6 @@
     void setPowerMode(int mode);
     bool isPoweredOn() const;
 
-    ui::ColorMode getActiveColorMode() const;
-    void setActiveColorMode(ui::ColorMode mode);
-    ui::RenderIntent getActiveRenderIntent() const;
-    void setActiveRenderIntent(ui::RenderIntent renderIntent);
-    android_color_transform_t getColorTransform() const;
-    void setColorTransform(const mat4& transform);
-    void setCompositionDataSpace(ui::Dataspace dataspace);
     ui::Dataspace getCompositionDataSpace() const;
 
     /* ------------------------------------------------------------------------
@@ -196,7 +152,7 @@
     void setActiveConfig(int mode);
 
     // release HWC resources (if any) for removable displays
-    void disconnect(HWComposer& hwc);
+    void disconnect();
 
     /* ------------------------------------------------------------------------
      * Debugging
@@ -206,29 +162,18 @@
     void dump(std::string& result) const;
 
 private:
+    /*
+     *  Constants, set during initialization
+     */
     const sp<SurfaceFlinger> mFlinger;
     const wp<IBinder> mDisplayToken;
     const int32_t mSequenceId;
 
-    std::optional<DisplayId> mId;
+    const int mDisplayInstallOrientation;
+    const std::shared_ptr<compositionengine::Display> mCompositionDisplay;
 
-    // ANativeWindow this display is rendering into
-    sp<ANativeWindow> mNativeWindow;
-    // Current buffer that this display can render to.
-    sp<GraphicBuffer> mGraphicBuffer;
-    sp<DisplaySurface> mDisplaySurface;
-    // File descriptor indicating that mGraphicBuffer is ready for display, i.e.
-    // that drawing to the buffer is now complete.
-    base::unique_fd mBufferReady;
-
-    int             mDisplayWidth;
-    int             mDisplayHeight;
-    const int       mDisplayInstallOrientation;
-    mutable uint32_t mPageFlipCount;
-    std::string     mDisplayName;
-
+    std::string mDisplayName;
     const bool mIsVirtual;
-    const bool mIsSecure;
 
     /*
      * Can only accessed from the main thread, these members
@@ -243,66 +188,17 @@
     /*
      * Transaction state
      */
+    static uint32_t displayStateOrientationToTransformOrientation(int orientation);
     static status_t orientationToTransfrom(int orientation,
                                            int w, int h, ui::Transform* tr);
 
-    // The identifier of the active layer stack for this display. Several displays
-    // can use the same layer stack: A z-ordered group of layers (sometimes called
-    // "surfaces"). Any given layer can only be on a single layer stack.
-    uint32_t mLayerStack;
-
     int mOrientation;
     static uint32_t sPrimaryDisplayOrientation;
-    // user-provided visible area of the layer stack
-    Rect mViewport;
-    // user-provided rectangle where mViewport gets mapped to
-    Rect mFrame;
-    // pre-computed scissor to apply to the display
-    Rect mScissor;
-    ui::Transform mGlobalTransform;
-    bool mNeedsFiltering;
+
     // Current power mode
     int mPowerMode;
     // Current active config
     int mActiveConfig;
-    // current active color mode
-    ui::ColorMode mActiveColorMode = ui::ColorMode::NATIVE;
-    // Current active render intent.
-    ui::RenderIntent mActiveRenderIntent = ui::RenderIntent::COLORIMETRIC;
-    ui::Dataspace mCompositionDataSpace = ui::Dataspace::UNKNOWN;
-    // Current color transform
-    android_color_transform_t mColorTransform;
-
-    // Need to know if display is wide-color capable or not.
-    // Initialized by SurfaceFlinger when the DisplayDevice is created.
-    // Fed to RenderEngine during composition.
-    bool mHasWideColorGamut;
-    bool mHasHdr10Plus;
-    bool mHasHdr10;
-    bool mHasHLG;
-    bool mHasDolbyVision;
-    HdrCapabilities mHdrCapabilities;
-    const int32_t mSupportedPerFrameMetadata;
-
-    // Mappings from desired Dataspace/RenderIntent to the supported
-    // Dataspace/ColorMode/RenderIntent.
-    using ColorModeKey = uint64_t;
-    struct ColorModeValue {
-        ui::Dataspace dataspace;
-        ui::ColorMode colorMode;
-        ui::RenderIntent renderIntent;
-    };
-
-    static ColorModeKey getColorModeKey(ui::Dataspace dataspace, ui::RenderIntent intent) {
-        return (static_cast<uint64_t>(dataspace) << 32) | static_cast<uint32_t>(intent);
-    }
-    void populateColorModes(
-            const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hwcColorModes);
-    void addColorMode(
-            const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hwcColorModes,
-            const ui::ColorMode mode, const ui::RenderIntent intent);
-
-    std::unordered_map<ColorModeKey, ColorModeValue> mColorModes;
 
     // TODO(b/74619554): Remove special cases for primary display.
     const bool mIsPrimary;
@@ -341,7 +237,7 @@
     bool isVirtual{false};
     bool isSecure{false};
     sp<ANativeWindow> nativeWindow;
-    sp<DisplaySurface> displaySurface;
+    sp<compositionengine::DisplaySurface> displaySurface;
     int displayInstallOrientation{DisplayState::eOrientationDefault};
     bool hasWideColorGamut{false};
     HdrCapabilities hdrCapabilities;
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 2431dfd..18c524f 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -17,15 +17,15 @@
 #ifndef ANDROID_SF_FRAMEBUFFER_SURFACE_H
 #define ANDROID_SF_FRAMEBUFFER_SURFACE_H
 
-#include "DisplayIdentification.h"
-#include "DisplaySurface.h"
-#include "HWComposerBufferCache.h"
-
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <compositionengine/DisplaySurface.h>
 #include <gui/ConsumerBase.h>
 
+#include "DisplayIdentification.h"
+#include "HWComposerBufferCache.h"
+
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
@@ -36,8 +36,7 @@
 
 // ---------------------------------------------------------------------------
 
-class FramebufferSurface : public ConsumerBase,
-                           public DisplaySurface {
+class FramebufferSurface : public ConsumerBase, public compositionengine::DisplaySurface {
 public:
     FramebufferSurface(HWComposer& hwc, DisplayId displayId,
                        const sp<IGraphicBufferConsumer>& consumer);
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 27d3dc5..1c2853a 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -38,12 +38,16 @@
 #define VDS_LOGV(msg, ...) ALOGV("[%s] " msg, \
         mDisplayName.c_str(), ##__VA_ARGS__)
 
-static const char* dbgCompositionTypeStr(DisplaySurface::CompositionType type) {
+static const char* dbgCompositionTypeStr(compositionengine::DisplaySurface::CompositionType type) {
     switch (type) {
-        case DisplaySurface::COMPOSITION_UNKNOWN: return "UNKNOWN";
-        case DisplaySurface::COMPOSITION_GLES:    return "GLES";
-        case DisplaySurface::COMPOSITION_HWC:     return "HWC";
-        case DisplaySurface::COMPOSITION_MIXED:   return "MIXED";
+        case compositionengine::DisplaySurface::COMPOSITION_UNKNOWN:
+            return "UNKNOWN";
+        case compositionengine::DisplaySurface::COMPOSITION_GLES:
+            return "GLES";
+        case compositionengine::DisplaySurface::COMPOSITION_HWC:
+            return "HWC";
+        case compositionengine::DisplaySurface::COMPOSITION_MIXED:
+            return "MIXED";
         default:                                  return "<INVALID>";
     }
 }
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index 33678df..87ae7dd 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -20,13 +20,13 @@
 #include <optional>
 #include <string>
 
-#include "DisplayIdentification.h"
-#include "DisplaySurface.h"
-#include "HWComposerBufferCache.h"
-
+#include <compositionengine/DisplaySurface.h>
 #include <gui/ConsumerBase.h>
 #include <gui/IGraphicBufferProducer.h>
 
+#include "DisplayIdentification.h"
+#include "HWComposerBufferCache.h"
+
 // ---------------------------------------------------------------------------
 namespace android {
 // ---------------------------------------------------------------------------
@@ -73,7 +73,7 @@
  * the HWC output buffer. When HWC composition is complete, the scratch buffer
  * is released and the output buffer is queued to the sink.
  */
-class VirtualDisplaySurface : public DisplaySurface,
+class VirtualDisplaySurface : public compositionengine::DisplaySurface,
                               public BnGraphicBufferProducer,
                               private ConsumerBase {
 public:
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index ee49610..aa9bc15 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2135,7 +2135,7 @@
     return mRemovedFromCurrentState;
 }
 
-InputWindowInfo Layer::fillInputInfo(const Rect& screenBounds) {
+InputWindowInfo Layer::fillInputInfo() {
     InputWindowInfo info = mDrawingState.inputInfo;
 
     ui::Transform t = getTransform();
@@ -2148,20 +2148,18 @@
     }
 
     // Transform layer size to screen space and inset it by surface insets.
-    Rect layerBounds = getCroppedBufferSize(getDrawingState());
+    Rect layerBounds = getBufferSize(getDrawingState());
+    if (!layerBounds.isValid()) {
+        layerBounds = getCroppedBufferSize(getDrawingState());
+    }
     layerBounds = t.transform(layerBounds);
     layerBounds.inset(info.surfaceInset, info.surfaceInset, info.surfaceInset, info.surfaceInset);
 
-    // Intersect with screen bounds to shrink the frame by the surface insets. The surface insets
-    // are not set on the screen bounds directly since the surface inset region may already be
-    // cropped by a parent layer.
-    Rect frame;
-    screenBounds.intersect(layerBounds, &frame);
-
-    info.frameLeft = frame.left;
-    info.frameTop = frame.top;
-    info.frameRight = frame.right;
-    info.frameBottom = frame.bottom;
+    // Input coordinate should match the layer bounds.
+    info.frameLeft = layerBounds.left;
+    info.frameTop = layerBounds.top;
+    info.frameRight = layerBounds.right;
+    info.frameBottom = layerBounds.bottom;
 
     // Position the touchable region relative to frame screen location and restrict it to frame
     // bounds.
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 7bc7a82..95a8630 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -773,7 +773,7 @@
     bool mPendingHWCDestroy{false};
     void setInputInfo(const InputWindowInfo& info);
 
-    InputWindowInfo fillInputInfo(const Rect& screenBounds);
+    InputWindowInfo fillInputInfo();
     bool hasInput() const;
 
 protected:
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index b74b901..9b2a6fc 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -657,6 +657,9 @@
     Mutex::Autolock lock(mMutex);
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
     nsecs_t phase = mReferenceTime + mPhase;
+    if (mPeriod == 0) {
+        return 0;
+    }
     return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase;
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1af1112..5df2876 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -37,6 +37,10 @@
 #include <binder/PermissionCache.h>
 
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <dvr/vr_flinger.h>
 #include <gui/BufferQueue.h>
 #include <gui/GuiConfig.h>
@@ -1019,40 +1023,11 @@
 
 ColorMode SurfaceFlinger::getActiveColorMode(const sp<IBinder>& displayToken) {
     if (const auto display = getDisplayDevice(displayToken)) {
-        return display->getActiveColorMode();
+        return display->getCompositionDisplay()->getState().colorMode;
     }
     return static_cast<ColorMode>(BAD_VALUE);
 }
 
-void SurfaceFlinger::setActiveColorModeInternal(const sp<DisplayDevice>& display, ColorMode mode,
-                                                Dataspace dataSpace, RenderIntent renderIntent) {
-    if (display->isVirtual()) {
-        ALOGE("%s: Invalid operation on virtual display", __FUNCTION__);
-        return;
-    }
-
-    ColorMode currentMode = display->getActiveColorMode();
-    Dataspace currentDataSpace = display->getCompositionDataSpace();
-    RenderIntent currentRenderIntent = display->getActiveRenderIntent();
-
-    if (mode == currentMode && dataSpace == currentDataSpace &&
-        renderIntent == currentRenderIntent) {
-        return;
-    }
-
-    display->setActiveColorMode(mode);
-    display->setCompositionDataSpace(dataSpace);
-    display->setActiveRenderIntent(renderIntent);
-
-    const auto displayId = display->getId();
-    LOG_ALWAYS_FATAL_IF(!displayId);
-    getHwComposer().setActiveColorMode(*displayId, mode, renderIntent);
-
-    ALOGV("Set active color mode: %s (%d), active render intent: %s (%d), display=%s",
-          decodeColorMode(mode).c_str(), mode, decodeRenderIntent(renderIntent).c_str(),
-          renderIntent, to_string(*displayId).c_str());
-}
-
 status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
     postMessageSync(new LambdaMessage([&] {
         Vector<ColorMode> modes;
@@ -1071,8 +1046,8 @@
             ALOGW("Attempt to set active color mode %s (%d) for virtual display",
                   decodeColorMode(mode).c_str(), mode);
         } else {
-            setActiveColorModeInternal(display, mode, Dataspace::UNKNOWN,
-                                       RenderIntent::COLORIMETRIC);
+            display->getCompositionDisplay()->setColorMode(mode, Dataspace::UNKNOWN,
+                                                           RenderIntent::COLORIMETRIC);
         }
     }));
 
@@ -1163,6 +1138,20 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
+                                     int32_t* outBufferId) {
+    if (!outBufferId) {
+        return BAD_VALUE;
+    }
+    *outBufferId = mBufferStateLayerCache.add(token, buffer);
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) {
+    mBufferStateLayerCache.release(token, bufferId);
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
     postMessageSync(new LambdaMessage([&] {
         Mutex::Autolock _l(mStateLock);
@@ -1688,9 +1677,11 @@
     postComposition();
 
     mHadClientComposition = false;
-    for (const auto& [token, display] : mDisplays) {
+    for (const auto& [token, displayDevice] : mDisplays) {
+        auto display = displayDevice->getCompositionDisplay();
+        const auto displayId = display->getId();
         mHadClientComposition =
-                mHadClientComposition || getHwComposer().hasClientComposition(display->getId());
+                mHadClientComposition || getHwComposer().hasClientComposition(displayId);
     }
 
     // Setup RenderEngine sync fences if native sync is supported.
@@ -1730,13 +1721,14 @@
     // build the h/w work list
     if (CC_UNLIKELY(mGeometryInvalid)) {
         mGeometryInvalid = false;
-        for (const auto& [token, display] : mDisplays) {
+        for (const auto& [token, displayDevice] : mDisplays) {
+            auto display = displayDevice->getCompositionDisplay();
             const auto displayId = display->getId();
             if (!displayId) {
                 continue;
             }
 
-            const Vector<sp<Layer>>& currentLayers = display->getVisibleLayersSortedByZ();
+            const Vector<sp<Layer>>& currentLayers = displayDevice->getVisibleLayersSortedByZ();
             for (size_t i = 0; i < currentLayers.size(); i++) {
                 const auto& layer = currentLayers[i];
 
@@ -1747,7 +1739,7 @@
                     }
                 }
 
-                layer->setGeometry(display, i);
+                layer->setGeometry(displayDevice, i);
                 if (mDebugDisableHWC || mDebugRegion) {
                     layer->forceClientComposition(*displayId);
                 }
@@ -1756,29 +1748,27 @@
     }
 
     // Set the per-frame data
-    for (const auto& [token, display] : mDisplays) {
+    for (const auto& [token, displayDevice] : mDisplays) {
+        auto display = displayDevice->getCompositionDisplay();
         const auto displayId = display->getId();
         if (!displayId) {
             continue;
         }
+        auto* profile = display->getDisplayColorProfile();
 
         if (mDrawingState.colorMatrixChanged) {
             display->setColorTransform(mDrawingState.colorMatrix);
-            status_t result =
-                    getHwComposer().setColorTransform(*displayId, mDrawingState.colorMatrix);
-            ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display %s: %d",
-                     to_string(*displayId).c_str(), result);
         }
-        for (auto& layer : display->getVisibleLayersSortedByZ()) {
+        for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
             if (layer->isHdrY410()) {
                 layer->forceClientComposition(*displayId);
             } else if ((layer->getDataSpace() == Dataspace::BT2020_PQ ||
                         layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) &&
-                    !display->hasHDR10Support()) {
+                       !profile->hasHDR10Support()) {
                 layer->forceClientComposition(*displayId);
             } else if ((layer->getDataSpace() == Dataspace::BT2020_HLG ||
                         layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) &&
-                    !display->hasHLGSupport()) {
+                       !profile->hasHLGSupport()) {
                 layer->forceClientComposition(*displayId);
             }
 
@@ -1797,16 +1787,17 @@
                 continue;
             }
 
-            layer->setPerFrameData(*displayId, display->getTransform(), display->getViewport(),
-                                   display->getSupportedPerFrameMetadata());
+            const auto& displayState = display->getState();
+            layer->setPerFrameData(*displayId, displayState.transform, displayState.viewport,
+                                   displayDevice->getSupportedPerFrameMetadata());
         }
 
         if (useColorManagement) {
             ColorMode  colorMode;
             Dataspace dataSpace;
             RenderIntent renderIntent;
-            pickColorMode(display, &colorMode, &dataSpace, &renderIntent);
-            setActiveColorModeInternal(display, colorMode, dataSpace, renderIntent);
+            pickColorMode(displayDevice, &colorMode, &dataSpace, &renderIntent);
+            display->setColorMode(colorMode, dataSpace, renderIntent);
         }
     }
 
@@ -1830,34 +1821,37 @@
     }
 }
 
-void SurfaceFlinger::doDebugFlashRegions(const sp<DisplayDevice>& display, bool repaintEverything)
-{
+void SurfaceFlinger::doDebugFlashRegions(const sp<DisplayDevice>& displayDevice,
+                                         bool repaintEverything) {
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
+
     // is debugging enabled
     if (CC_LIKELY(!mDebugRegion))
         return;
 
-    if (display->isPoweredOn()) {
+    if (displayState.isEnabled) {
         // transform the dirty region into this screen's coordinate space
-        const Region dirtyRegion(display->getDirtyRegion(repaintEverything));
+        const Region dirtyRegion = display->getPhysicalSpaceDirtyRegion(repaintEverything);
         if (!dirtyRegion.isEmpty()) {
             // redraw the whole screen
-            doComposeSurfaces(display);
+            doComposeSurfaces(displayDevice);
 
             // and draw the dirty region
             auto& engine(getRenderEngine());
             engine.fillRegionWithColor(dirtyRegion, 1, 0, 1, 1);
 
-            display->queueBuffer(getHwComposer());
+            display->getRenderSurface()->queueBuffer();
         }
     }
 
-    postFramebuffer(display);
+    postFramebuffer(displayDevice);
 
     if (mDebugRegion > 1) {
         usleep(mDebugRegion * 1000);
     }
 
-    prepareFrame(display);
+    prepareFrame(displayDevice);
 }
 
 void SurfaceFlinger::doTracing(const char* where) {
@@ -2022,16 +2016,18 @@
     ASSERT_ON_STACK_GUARD();
 
     // |mStateLock| not needed as we are on the main thread
-    const auto display = getDefaultDisplayDeviceLocked();
-    DEFINE_STACK_GUARD(display);
+    const auto displayDevice = getDefaultDisplayDeviceLocked();
+    DEFINE_STACK_GUARD(displayDevice);
 
     getBE().mGlCompositionDoneTimeline.updateSignalTimes();
     std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
     DEFINE_STACK_GUARD(glCompositionDoneFenceTime);
 
-    if (display && getHwComposer().hasClientComposition(display->getId())) {
+    if (displayDevice && getHwComposer().hasClientComposition(displayDevice->getId())) {
         glCompositionDoneFenceTime =
-                std::make_shared<FenceTime>(display->getClientTargetAcquireFence());
+                std::make_shared<FenceTime>(displayDevice->getCompositionDisplay()
+                                                    ->getRenderSurface()
+                                                    ->getClientTargetAcquireFence());
         getBE().mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
     } else {
         glCompositionDoneFenceTime = FenceTime::NO_FENCE;
@@ -2040,8 +2036,8 @@
     ASSERT_ON_STACK_GUARD();
 
     getBE().mDisplayTimeline.updateSignalTimes();
-    mPreviousPresentFence =
-            display ? getHwComposer().getPresentFence(*display->getId()) : Fence::NO_FENCE;
+    mPreviousPresentFence = displayDevice ? getHwComposer().getPresentFence(*displayDevice->getId())
+                                          : Fence::NO_FENCE;
     auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFence);
     DEFINE_STACK_GUARD(presentFenceTime);
     getBE().mDisplayTimeline.push(presentFenceTime);
@@ -2073,8 +2069,9 @@
     }
 
     mDrawingState.traverseInZOrder([&](Layer* layer) {
-        bool frameLatched = layer->onPostComposition(display->getId(), glCompositionDoneFenceTime,
-                                                     presentFenceTime, compositorTiming);
+        bool frameLatched =
+                layer->onPostComposition(displayDevice->getId(), glCompositionDoneFenceTime,
+                                         presentFenceTime, compositorTiming);
         DEFINE_STACK_GUARD(frameLatched);
         if (frameLatched) {
             recordBufferingStats(layer->getName().string(),
@@ -2099,7 +2096,8 @@
     }
 
     if (!hasSyncFramework) {
-        if (display && getHwComposer().isConnected(*display->getId()) && display->isPoweredOn()) {
+        if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
+            displayDevice->isPoweredOn()) {
             if (mUseScheduler) {
                 mScheduler->enableHardwareVsync();
             } else {
@@ -2118,10 +2116,11 @@
                     std::move(presentFenceTime));
 
             ASSERT_ON_STACK_GUARD();
-        } else if (display && getHwComposer().isConnected(*display->getId())) {
+        } else if (displayDevice && getHwComposer().isConnected(*displayDevice->getId())) {
             // The HWC doesn't support present fences, so use the refresh
             // timestamp instead.
-            const nsecs_t presentTime = getHwComposer().getRefreshTimestamp(*display->getId());
+            const nsecs_t presentTime =
+                    getHwComposer().getRefreshTimestamp(*displayDevice->getId());
             DEFINE_STACK_GUARD(presentTime);
 
             mAnimFrameTracker.setActualPresentTime(presentTime);
@@ -2143,7 +2142,8 @@
 
     ASSERT_ON_STACK_GUARD();
 
-    if (display && getHwComposer().isConnected(*display->getId()) && !display->isPoweredOn()) {
+    if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
+        !displayDevice->isPoweredOn()) {
         return;
     }
 
@@ -2211,20 +2211,22 @@
         invalidateHwcGeometry();
 
         for (const auto& pair : mDisplays) {
-            const auto& display = pair.second;
+            const auto& displayDevice = pair.second;
+            auto display = displayDevice->getCompositionDisplay();
+            const auto& displayState = display->getState();
             Region opaqueRegion;
             Region dirtyRegion;
             Vector<sp<Layer>> layersSortedByZ;
             Vector<sp<Layer>> layersNeedingFences;
-            const ui::Transform& tr = display->getTransform();
-            const Rect bounds = display->getBounds();
-            if (display->isPoweredOn()) {
-                computeVisibleRegions(display, dirtyRegion, opaqueRegion);
+            const ui::Transform& tr = displayState.transform;
+            const Rect bounds = displayState.bounds;
+            if (displayState.isEnabled) {
+                computeVisibleRegions(displayDevice, dirtyRegion, opaqueRegion);
 
                 mDrawingState.traverseInZOrder([&](Layer* layer) {
                     bool hwcLayerDestroyed = false;
-                    const auto displayId = display->getId();
-                    if (layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) {
+                    const auto displayId = displayDevice->getId();
+                    if (display->belongsInOutput(layer->getLayerStack())) {
                         Region drawRegion(tr.transform(
                                 layer->visibleNonTransparentRegion));
                         drawRegion.andSelf(bounds);
@@ -2255,11 +2257,14 @@
                     }
                 });
             }
-            display->setVisibleLayersSortedByZ(layersSortedByZ);
-            display->setLayersNeedingFences(layersNeedingFences);
-            display->undefinedRegion.set(bounds);
-            display->undefinedRegion.subtractSelf(tr.transform(opaqueRegion));
-            display->dirtyRegion.orSelf(dirtyRegion);
+            displayDevice->setVisibleLayersSortedByZ(layersSortedByZ);
+            displayDevice->setLayersNeedingFences(layersNeedingFences);
+
+            Region undefinedRegion{bounds};
+            undefinedRegion.subtractSelf(tr.transform(opaqueRegion));
+
+            display->editState().undefinedRegion = undefinedRegion;
+            display->editState().dirtyRegion.orSelf(dirtyRegion);
         }
     }
 }
@@ -2324,9 +2329,11 @@
     Dataspace hdrDataSpace;
     Dataspace bestDataSpace = getBestDataspace(display, &hdrDataSpace);
 
+    auto* profile = display->getCompositionDisplay()->getDisplayColorProfile();
+
     // respect hdrDataSpace only when there is no legacy HDR support
-    const bool isHdr = hdrDataSpace != Dataspace::UNKNOWN &&
-        !display->hasLegacyHdrSupport(hdrDataSpace);
+    const bool isHdr =
+            hdrDataSpace != Dataspace::UNKNOWN && !profile->hasLegacyHdrSupport(hdrDataSpace);
     if (isHdr) {
         bestDataSpace = hdrDataSpace;
     }
@@ -2345,14 +2352,16 @@
             break;
     }
 
-    display->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent);
+    profile->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent);
 }
 
-void SurfaceFlinger::beginFrame(const sp<DisplayDevice>& display)
-{
-    bool dirty = !display->getDirtyRegion(false).isEmpty();
-    bool empty = display->getVisibleLayersSortedByZ().size() == 0;
-    bool wasEmpty = !display->lastCompositionHadVisibleLayers;
+void SurfaceFlinger::beginFrame(const sp<DisplayDevice>& displayDevice) {
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
+
+    bool dirty = !display->getPhysicalSpaceDirtyRegion(false).isEmpty();
+    bool empty = displayDevice->getVisibleLayersSortedByZ().size() == 0;
+    bool wasEmpty = !displayState.lastCompositionHadVisibleLayers;
 
     // If nothing has changed (!dirty), don't recompose.
     // If something changed, but we don't currently have any visible layers,
@@ -2366,44 +2375,50 @@
 
     const char flagPrefix[] = {'-', '+'};
     static_cast<void>(flagPrefix);
-    ALOGV_IF(display->isVirtual(), "%s: %s composition for %s (%cdirty %cempty %cwasEmpty)",
-             __FUNCTION__, mustRecompose ? "doing" : "skipping", display->getDebugName().c_str(),
-             flagPrefix[dirty], flagPrefix[empty], flagPrefix[wasEmpty]);
+    ALOGV_IF(displayDevice->isVirtual(), "%s: %s composition for %s (%cdirty %cempty %cwasEmpty)",
+             __FUNCTION__, mustRecompose ? "doing" : "skipping",
+             displayDevice->getDebugName().c_str(), flagPrefix[dirty], flagPrefix[empty],
+             flagPrefix[wasEmpty]);
 
-    display->beginFrame(mustRecompose);
+    display->getRenderSurface()->beginFrame(mustRecompose);
 
     if (mustRecompose) {
-        display->lastCompositionHadVisibleLayers = !empty;
+        display->editState().lastCompositionHadVisibleLayers = !empty;
     }
 }
 
-void SurfaceFlinger::prepareFrame(const sp<DisplayDevice>& display)
-{
-    if (!display->isPoweredOn()) {
+void SurfaceFlinger::prepareFrame(const sp<DisplayDevice>& displayDevice) {
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
+
+    if (!displayState.isEnabled) {
         return;
     }
 
-    status_t result = display->prepareFrame(getHwComposer(),
-                                            getBE().mCompositionInfo[display->getDisplayToken()]);
+    status_t result = display->getRenderSurface()->prepareFrame(
+            getBE().mCompositionInfo[displayDevice->getDisplayToken()]);
     ALOGE_IF(result != NO_ERROR, "prepareFrame failed for %s: %d (%s)",
-             display->getDebugName().c_str(), result, strerror(-result));
+             displayDevice->getDebugName().c_str(), result, strerror(-result));
 }
 
-void SurfaceFlinger::doComposition(const sp<DisplayDevice>& display, bool repaintEverything) {
+void SurfaceFlinger::doComposition(const sp<DisplayDevice>& displayDevice, bool repaintEverything) {
     ATRACE_CALL();
     ALOGV("doComposition");
 
-    if (display->isPoweredOn()) {
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
+
+    if (displayState.isEnabled) {
         // transform the dirty region into this screen's coordinate space
-        const Region dirtyRegion(display->getDirtyRegion(repaintEverything));
+        const Region dirtyRegion = display->getPhysicalSpaceDirtyRegion(repaintEverything);
 
         // repaint the framebuffer (if needed)
-        doDisplayComposition(display, dirtyRegion);
+        doDisplayComposition(displayDevice, dirtyRegion);
 
-        display->dirtyRegion.clear();
-        display->flip();
+        display->editState().dirtyRegion.clear();
+        display->getRenderSurface()->flip();
     }
-    postFramebuffer(display);
+    postFramebuffer(displayDevice);
 }
 
 void SurfaceFlinger::postFrame()
@@ -2418,20 +2433,22 @@
     }
 }
 
-void SurfaceFlinger::postFramebuffer(const sp<DisplayDevice>& display)
-{
+void SurfaceFlinger::postFramebuffer(const sp<DisplayDevice>& displayDevice) {
     ATRACE_CALL();
     ALOGV("postFramebuffer");
 
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
+    const auto displayId = display->getId();
+
     mPostFramebufferTime = systemTime();
 
-    if (display->isPoweredOn()) {
-        const auto displayId = display->getId();
+    if (displayState.isEnabled) {
         if (displayId) {
             getHwComposer().presentAndGetReleaseFences(*displayId);
         }
-        display->onPresentDisplayCompleted();
-        for (auto& layer : display->getVisibleLayersSortedByZ()) {
+        display->getRenderSurface()->onPresentDisplayCompleted();
+        for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
             sp<Fence> releaseFence = Fence::NO_FENCE;
 
             // The layer buffer from the previous frame (if any) is released
@@ -2448,8 +2465,9 @@
             // client target acquire fence when it is available, even though
             // this is suboptimal.
             if (layer->getCompositionType(displayId) == HWC2::Composition::Client) {
-                releaseFence = Fence::merge("LayerRelease", releaseFence,
-                                            display->getClientTargetAcquireFence());
+                releaseFence =
+                        Fence::merge("LayerRelease", releaseFence,
+                                     display->getRenderSurface()->getClientTargetAcquireFence());
             }
 
             layer->getBE().onLayerDisplayed(releaseFence);
@@ -2458,10 +2476,10 @@
         // We've got a list of layers needing fences, that are disjoint with
         // display->getVisibleLayersSortedByZ.  The best we can do is to
         // supply them with the present fence.
-        if (!display->getLayersNeedingFences().isEmpty()) {
+        if (!displayDevice->getLayersNeedingFences().isEmpty()) {
             sp<Fence> presentFence =
                     displayId ? getHwComposer().getPresentFence(*displayId) : Fence::NO_FENCE;
-            for (auto& layer : display->getLayersNeedingFences()) {
+            for (auto& layer : displayDevice->getLayersNeedingFences()) {
                 layer->getBE().onLayerDisplayed(presentFence);
             }
         }
@@ -2542,7 +2560,7 @@
 
 sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
         const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
-        const DisplayDeviceState& state, const sp<DisplaySurface>& dispSurface,
+        const DisplayDeviceState& state, const sp<compositionengine::DisplaySurface>& dispSurface,
         const sp<IGraphicBufferProducer>& producer) {
     DisplayDeviceCreationArgs creationArgs(this, displayToken, displayId);
     creationArgs.sequenceId = state.sequenceId;
@@ -2603,8 +2621,8 @@
         defaultColorMode = ColorMode::SRGB;
         defaultDataSpace = Dataspace::V0_SRGB;
     }
-    setActiveColorModeInternal(display, defaultColorMode, defaultDataSpace,
-                               RenderIntent::COLORIMETRIC);
+    display->getCompositionDisplay()->setColorMode(defaultColorMode, defaultDataSpace,
+                                                   RenderIntent::COLORIMETRIC);
     if (!state.isVirtual()) {
         LOG_ALWAYS_FATAL_IF(!displayId);
         display->setActiveConfig(getHwComposer().getActiveConfigIndex(*displayId));
@@ -2641,7 +2659,7 @@
 
                 // in drawing state but not in current state
                 if (const auto display = getDisplayDeviceLocked(draw.keyAt(i))) {
-                    display->disconnect(getHwComposer());
+                    display->disconnect();
                 }
                 if (internalDisplayId && internalDisplayId == draw[i].displayId) {
                     if (mUseScheduler) {
@@ -2671,7 +2689,7 @@
                     // from the drawing state, so that it get re-added
                     // below.
                     if (const auto display = getDisplayDeviceLocked(displayToken)) {
-                        display->disconnect(getHwComposer());
+                        display->disconnect();
                     }
                     mDisplays.erase(displayToken);
                     mDrawingState.displays.removeItemsAt(i);
@@ -2702,7 +2720,7 @@
             if (draw.indexOfKey(curr.keyAt(i)) < 0) {
                 const DisplayDeviceState& state(curr[i]);
 
-                sp<DisplaySurface> dispSurface;
+                sp<compositionengine::DisplaySurface> dispSurface;
                 sp<IGraphicBufferProducer> producer;
                 sp<IGraphicBufferProducer> bqProducer;
                 sp<IGraphicBufferConsumer> bqConsumer;
@@ -2861,7 +2879,7 @@
                 // if not, pick the only display it's on.
                 hintDisplay = nullptr;
                 for (const auto& [token, display] : mDisplays) {
-                    if (layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) {
+                    if (display->getCompositionDisplay()->belongsInOutput(layer->getLayerStack())) {
                         if (hintDisplay) {
                             hintDisplay = nullptr;
                             break;
@@ -2944,8 +2962,7 @@
         if (layer->hasInput()) {
             // When calculating the screen bounds we ignore the transparent region since it may
             // result in an unwanted offset.
-            inputHandles.add(layer->fillInputInfo(
-                    layer->computeScreenBounds(false /* reduceTransparentRegion */)));
+            inputHandles.add(layer->fillInputInfo());
         }
     });
     mInputFlinger->setInputWindows(inputHandles);
@@ -3028,11 +3045,13 @@
     mTransactionCV.broadcast();
 }
 
-void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& display,
+void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displayDevice,
                                            Region& outDirtyRegion, Region& outOpaqueRegion) {
     ATRACE_CALL();
     ALOGV("computeVisibleRegions");
 
+    auto display = displayDevice->getCompositionDisplay();
+
     Region aboveOpaqueLayers;
     Region aboveCoveredLayers;
     Region dirty;
@@ -3044,7 +3063,7 @@
         const Layer::State& s(layer->getDrawingState());
 
         // only consider the layers on the given layer stack
-        if (!layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) {
+        if (!display->belongsInOutput(layer->getLayerStack())) {
             return;
         }
 
@@ -3168,9 +3187,10 @@
 }
 
 void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
-    for (const auto& [token, display] : mDisplays) {
-        if (layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) {
-            display->dirtyRegion.orSelf(dirty);
+    for (const auto& [token, displayDevice] : mDisplays) {
+        auto display = displayDevice->getCompositionDisplay();
+        if (display->belongsInOutput(layer->getLayerStack())) {
+            display->editState().dirtyRegion.orSelf(dirty);
         }
     }
 }
@@ -3257,30 +3277,35 @@
     mGeometryInvalid = true;
 }
 
-void SurfaceFlinger::doDisplayComposition(const sp<DisplayDevice>& display,
+void SurfaceFlinger::doDisplayComposition(const sp<DisplayDevice>& displayDevice,
                                           const Region& inDirtyRegion) {
+    auto display = displayDevice->getCompositionDisplay();
+
     // We only need to actually compose the display if:
     // 1) It is being handled by hardware composer, which may need this to
     //    keep its virtual display state machine in sync, or
     // 2) There is work to be done (the dirty region isn't empty)
-    if (!display->getId() && inDirtyRegion.isEmpty()) {
+    if (!displayDevice->getId() && inDirtyRegion.isEmpty()) {
         ALOGV("Skipping display composition");
         return;
     }
 
     ALOGV("doDisplayComposition");
-    if (!doComposeSurfaces(display)) return;
+    if (!doComposeSurfaces(displayDevice)) return;
 
     // swap buffers (presentation)
-    display->queueBuffer(getHwComposer());
+    display->getRenderSurface()->queueBuffer();
 }
 
-bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& display) {
+bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice) {
     ALOGV("doComposeSurfaces");
 
-    const Region bounds(display->bounds());
-    const DisplayRenderArea renderArea(display);
+    auto display = displayDevice->getCompositionDisplay();
+    const auto& displayState = display->getState();
     const auto displayId = display->getId();
+
+    const Region bounds(displayState.bounds);
+    const DisplayRenderArea renderArea(displayDevice);
     const bool hasClientComposition = getHwComposer().hasClientComposition(displayId);
     ATRACE_INT("hasClientComposition", hasClientComposition);
 
@@ -3293,12 +3318,12 @@
     if (hasClientComposition) {
         ALOGV("hasClientComposition");
 
-        sp<GraphicBuffer> buf = display->dequeueBuffer();
+        sp<GraphicBuffer> buf = display->getRenderSurface()->dequeueBuffer();
 
         if (buf == nullptr) {
             ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
                   "client composition for this frame",
-                  display->getDisplayName().c_str());
+                  displayDevice->getDisplayName().c_str());
             return false;
         }
 
@@ -3308,17 +3333,18 @@
 
         if (fbo->getStatus() != NO_ERROR) {
             ALOGW("Binding buffer for display [%s] failed with status: %d",
-                  display->getDisplayName().c_str(), fbo->getStatus());
+                  displayDevice->getDisplayName().c_str(), fbo->getStatus());
             return false;
         }
 
+        const auto* profile = display->getDisplayColorProfile();
         Dataspace outputDataspace = Dataspace::UNKNOWN;
-        if (display->hasWideColorGamut()) {
-            outputDataspace = display->getCompositionDataSpace();
+        if (profile->hasWideColorGamut()) {
+            outputDataspace = displayState.dataspace;
         }
         getRenderEngine().setOutputDataSpace(outputDataspace);
         getRenderEngine().setDisplayMaxLuminance(
-                display->getHdrCapabilities().getDesiredMaxLuminance());
+                profile->getHdrCapabilities().getDesiredMaxLuminance());
 
         const bool hasDeviceComposition = getHwComposer().hasDeviceComposition(displayId);
         const bool skipClientColorTransform =
@@ -3332,7 +3358,7 @@
             colorMatrix = mDrawingState.colorMatrix;
         }
 
-        display->setViewportAndProjection();
+        display->getRenderSurface()->setViewportAndProjection();
 
         // Never touch the framebuffer if we don't have any framebuffer layers
         if (hasDeviceComposition) {
@@ -3346,10 +3372,10 @@
             // we start with the whole screen area and remove the scissor part
             // we're left with the letterbox region
             // (common case is that letterbox ends-up being empty)
-            const Region letterbox = bounds.subtract(display->getScissor());
+            const Region letterbox = bounds.subtract(displayState.scissor);
 
             // compute the area to clear
-            const Region region = display->undefinedRegion.merge(letterbox);
+            const Region region = displayState.undefinedRegion.merge(letterbox);
 
             // screen is already cleared here
             if (!region.isEmpty()) {
@@ -3358,8 +3384,8 @@
             }
         }
 
-        const Rect& bounds = display->getBounds();
-        const Rect& scissor = display->getScissor();
+        const Rect& bounds = displayState.bounds;
+        const Rect& scissor = displayState.scissor;
         if (scissor != bounds) {
             // scissor doesn't match the screen's dimensions, so we
             // need to clear everything outside of it and enable
@@ -3375,9 +3401,9 @@
      */
 
     ALOGV("Rendering client layers");
-    const ui::Transform& displayTransform = display->getTransform();
+    const ui::Transform& displayTransform = displayState.transform;
     bool firstLayer = true;
-    for (auto& layer : display->getVisibleLayersSortedByZ()) {
+    for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
         const Region clip(bounds.intersect(
                 displayTransform.transform(layer->visibleRegion)));
         ALOGV("Layer: %s", layer->getName().string());
@@ -3426,7 +3452,7 @@
     if (hasClientComposition) {
         getRenderEngine().setColorTransform(mat4());
         getRenderEngine().disableScissor();
-        display->finishBuffer();
+        display->getRenderSurface()->finishBuffer();
         // Clear out error flags here so that we don't wait until next
         // composition to log.
         getRenderEngine().checkErrors();
@@ -3456,9 +3482,11 @@
         }
         if (parent == nullptr && addToCurrentState) {
             mCurrentState.layersSortedByZ.add(lbc);
-        } else if (parent == nullptr || parent->isRemovedFromCurrentState()) {
-                ALOGE("addClientLayer called with a removed parent");
-                lbc->onRemovedFromCurrentState();
+        } else if (parent == nullptr) {
+            lbc->onRemovedFromCurrentState();
+        } else if (parent->isRemovedFromCurrentState()) {
+            parent->addChild(lbc);
+            lbc->onRemovedFromCurrentState();
         } else {
             parent->addChild(lbc);
         }
@@ -3509,8 +3537,8 @@
         auto& [applyToken, transactionQueue] = *it;
 
         while (!transactionQueue.empty()) {
-            const auto& [states, displays, flags] = transactionQueue.front();
-            if (composerStateContainsUnsignaledFences(states)) {
+            const auto& [states, displays, flags, desiredPresentTime] = transactionQueue.front();
+            if (!transactionIsReadyToBeApplied(desiredPresentTime, states)) {
                 break;
             }
             applyTransactionState(states, displays, flags, mInputWindowCommands);
@@ -3544,23 +3572,33 @@
     return false;
 }
 
-bool SurfaceFlinger::composerStateContainsUnsignaledFences(const Vector<ComposerState>& states) {
+bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
+                                                   const Vector<ComposerState>& states) {
+    const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+    // Do not present if the desiredPresentTime has not passed unless it is more than one second
+    // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
+    if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime &&
+        desiredPresentTime < expectedPresentTime + s2ns(1)) {
+        return false;
+    }
+
     for (const ComposerState& state : states) {
         const layer_state_t& s = state.state;
         if (!(s.what & layer_state_t::eAcquireFenceChanged)) {
             continue;
         }
         if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
-            return true;
+            return false;
         }
     }
-    return false;
+    return true;
 }
 
 void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states,
                                          const Vector<DisplayState>& displays, uint32_t flags,
                                          const sp<IBinder>& applyToken,
-                                         const InputWindowCommands& inputWindowCommands) {
+                                         const InputWindowCommands& inputWindowCommands,
+                                         int64_t desiredPresentTime) {
     ATRACE_CALL();
     Mutex::Autolock _l(mStateLock);
 
@@ -3570,8 +3608,8 @@
 
     // If its TransactionQueue already has a pending TransactionState or if it is pending
     if (mTransactionQueues.find(applyToken) != mTransactionQueues.end() ||
-        composerStateContainsUnsignaledFences(states)) {
-        mTransactionQueues[applyToken].emplace(states, displays, flags);
+        !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
+        mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime);
         setTransactionFlags(eTransactionNeeded);
         return;
     }
@@ -3933,6 +3971,11 @@
             callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
         }
     }
+    if (what & layer_state_t::eCachedBufferChanged) {
+        sp<GraphicBuffer> buffer =
+                mBufferStateLayerCache.get(s.cachedBuffer.token, s.cachedBuffer.bufferId);
+        if (layer->setBuffer(buffer)) flags |= eTraversalNeeded;
+    }
     if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
     // Do not put anything that updates layer state or modifies flags after
     // setTransactionCompletedListener
@@ -4161,7 +4204,7 @@
     d.width = 0;
     d.height = 0;
     displays.add(d);
-    setTransactionState(state, displays, 0, nullptr, mInputWindowCommands);
+    setTransactionState(state, displays, 0, nullptr, mInputWindowCommands, -1);
 
     setPowerModeInternal(display, HWC_POWER_MODE_NORMAL);
 
@@ -4655,7 +4698,7 @@
             StringAppendF(&result, "    %s (%d)\n", decodeColorMode(mode).c_str(), mode);
         }
 
-        ColorMode currentMode = display->getActiveColorMode();
+        ColorMode currentMode = display->getCompositionDisplay()->getState().colorMode;
         StringAppendF(&result, "    Current color mode: %s (%d)\n",
                       decodeColorMode(currentMode).c_str(), currentMode);
     }
@@ -4691,18 +4734,21 @@
     return layersProto;
 }
 
-LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(const DisplayDevice& display) const {
+LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(const DisplayDevice& displayDevice) const {
     LayersProto layersProto;
 
     SizeProto* resolution = layersProto.mutable_resolution();
-    resolution->set_w(display.getWidth());
-    resolution->set_h(display.getHeight());
+    resolution->set_w(displayDevice.getWidth());
+    resolution->set_h(displayDevice.getHeight());
 
-    layersProto.set_color_mode(decodeColorMode(display.getActiveColorMode()));
-    layersProto.set_color_transform(decodeColorTransform(display.getColorTransform()));
-    layersProto.set_global_transform(static_cast<int32_t>(display.getOrientationTransform()));
+    auto display = displayDevice.getCompositionDisplay();
+    const auto& displayState = display->getState();
 
-    const auto displayId = display.getId();
+    layersProto.set_color_mode(decodeColorMode(displayState.colorMode));
+    layersProto.set_color_transform(decodeColorTransform(displayState.colorTransform));
+    layersProto.set_global_transform(displayState.orientation);
+
+    const auto displayId = displayDevice.getId();
     LOG_ALWAYS_FATAL_IF(!displayId);
     mDrawingState.traverseInZOrder([&](Layer* layer) {
         if (!layer->visibleRegion.isEmpty() && layer->getBE().mHwcLayers.count(*displayId)) {
@@ -4823,7 +4869,8 @@
     getRenderEngine().dump(result);
 
     if (const auto display = getDefaultDisplayDeviceLocked()) {
-        display->undefinedRegion.dump(result, "undefinedRegion");
+        display->getCompositionDisplay()->getState().undefinedRegion.dump(result,
+                                                                          "undefinedRegion");
         StringAppendF(&result, "  orientation=%d, isPoweredOn=%d\n", display->getOrientation(),
                       display->isPoweredOn());
     }
@@ -5015,7 +5062,9 @@
         case CREATE_CONNECTION:
         case GET_COLOR_MANAGEMENT:
         case GET_COMPOSITION_PREFERENCE:
-        case GET_PROTECTED_CONTENT_SUPPORT: {
+        case GET_PROTECTED_CONTENT_SUPPORT:
+        case CACHE_BUFFER:
+        case UNCACHE_BUFFER: {
             return OK;
         }
         case CAPTURE_LAYERS:
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index a4bd3e9..499ebcd 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -47,6 +47,7 @@
 #include <utils/threads.h>
 
 #include "Barrier.h"
+#include "BufferStateLayerCache.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/HWComposer.h"
@@ -99,6 +100,10 @@
 class VSyncSource;
 struct CompositionInfo;
 
+namespace compositionengine {
+class DisplaySurface;
+} // namespace compositionengine
+
 namespace impl {
 class EventThread;
 } // namespace impl
@@ -414,7 +419,8 @@
     void setTransactionState(const Vector<ComposerState>& state,
                              const Vector<DisplayState>& displays, uint32_t flags,
                              const sp<IBinder>& applyToken,
-                             const InputWindowCommands& inputWindowCommands) override;
+                             const InputWindowCommands& inputWindowCommands,
+                             int64_t desiredPresentTime) override;
     void bootFinished() override;
     bool authenticateSurfaceTexture(
             const sp<IGraphicBufferProducer>& bufferProducer) const override;
@@ -460,6 +466,9 @@
                                        uint64_t timestamp,
                                        DisplayedFrameStats* outStats) const override;
     status_t getProtectedContentSupport(bool* outSupported) const override;
+    status_t cacheBuffer(const sp<IBinder>& token, const sp<GraphicBuffer>& buffer,
+                         int32_t* outBufferId) override;
+    status_t uncacheBuffer(const sp<IBinder>& token, int32_t bufferId) override;
 
     /* ------------------------------------------------------------------------
      * DeathRecipient interface
@@ -499,10 +508,6 @@
     // called on the main thread in response to setPowerMode()
     void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
 
-    // Called on the main thread in response to setActiveColorMode()
-    void setActiveColorModeInternal(const sp<DisplayDevice>& display, ui::ColorMode colorMode,
-                                    ui::Dataspace dataSpace, ui::RenderIntent renderIntent);
-
     // Returns whether the transaction actually modified any state
     bool handleMessageTransaction();
 
@@ -539,7 +544,8 @@
     void latchAndReleaseBuffer(const sp<Layer>& layer);
     void commitTransaction();
     bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
-    bool composerStateContainsUnsignaledFences(const Vector<ComposerState>& states);
+    bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
+                                       const Vector<ComposerState>& states);
     uint32_t setClientStateLocked(const ComposerState& composerState);
     uint32_t setDisplayStateLocked(const DisplayState& s);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands);
@@ -737,11 +743,11 @@
     /* ------------------------------------------------------------------------
      * Display management
      */
-    sp<DisplayDevice> setupNewDisplayDeviceInternal(const wp<IBinder>& displayToken,
-                                                    const std::optional<DisplayId>& displayId,
-                                                    const DisplayDeviceState& state,
-                                                    const sp<DisplaySurface>& dispSurface,
-                                                    const sp<IGraphicBufferProducer>& producer);
+    sp<DisplayDevice> setupNewDisplayDeviceInternal(
+            const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
+            const DisplayDeviceState& state,
+            const sp<compositionengine::DisplaySurface>& dispSurface,
+            const sp<IGraphicBufferProducer>& producer);
     void processDisplayChangesLocked();
     void processDisplayHotplugEventsLocked();
 
@@ -989,12 +995,17 @@
     };
     struct TransactionState {
         TransactionState(const Vector<ComposerState>& composerStates,
-                         const Vector<DisplayState>& displayStates, uint32_t transactionFlags)
-              : states(composerStates), displays(displayStates), flags(transactionFlags) {}
+                         const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
+                         int64_t desiredPresentTime)
+              : states(composerStates),
+                displays(displayStates),
+                flags(transactionFlags),
+                time(desiredPresentTime) {}
 
         Vector<ComposerState> states;
         Vector<DisplayState> displays;
         uint32_t flags;
+        int64_t time;
     };
     std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IBinderHash> mTransactionQueues;
 
@@ -1034,6 +1045,8 @@
     sp<IInputFlinger> mInputFlinger;
 
     InputWindowCommands mInputWindowCommands;
+
+    BufferStateLayerCache mBufferStateLayerCache;
 };
 }; // namespace android
 
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index e45af18..3ad6ec3 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -21,6 +21,7 @@
 #include <android/frameworks/displayservice/1.0/IDisplayService.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/graphics/allocator/2.0/IAllocator.h>
+#include <android/hardware/graphics/allocator/3.0/IAllocator.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
@@ -41,10 +42,14 @@
         return OK;
     }
 
-    using android::hardware::graphics::allocator::V2_0::IAllocator;
+    status_t result = hardware::registerPassthroughServiceImplementation<
+            android::hardware::graphics::allocator::V3_0::IAllocator>();
+    if (result == OK) {
+        return OK;
+    }
 
-    status_t result =
-        hardware::registerPassthroughServiceImplementation<IAllocator>();
+    result = hardware::registerPassthroughServiceImplementation<
+            android::hardware::graphics::allocator::V2_0::IAllocator>();
     if (result != OK) {
         ALOGE("could not start graphics allocator service");
         return result;
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 9339761..216532a 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -2323,6 +2323,143 @@
     Transaction().setSidebandStream(layer, nullptr).apply();
 }
 
+TEST_F(LayerTransactionTest, CacheBuffer_BufferState) {
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+
+    int32_t bufferId = -1;
+    ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+    ASSERT_GE(bufferId, 0);
+
+    ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+}
+
+TEST_F(LayerTransactionTest, CacheBuffers_BufferState) {
+    std::vector<int32_t> bufferIds;
+    int32_t bufferCount = 20;
+
+    for (int i = 0; i < bufferCount; i++) {
+        sp<GraphicBuffer> buffer =
+                new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                                  BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                          BufferUsage::COMPOSER_OVERLAY,
+                                  "test");
+        int32_t bufferId = -1;
+        ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+        if (bufferId < 0) {
+            EXPECT_GE(bufferId, 0);
+            break;
+        }
+        bufferIds.push_back(bufferId);
+    }
+
+    for (int32_t bufferId : bufferIds) {
+        ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+    }
+}
+
+TEST_F(LayerTransactionTest, CacheBufferInvalid_BufferState) {
+    sp<GraphicBuffer> buffer = nullptr;
+
+    int32_t bufferId = -1;
+    ASSERT_NE(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+    ASSERT_LT(bufferId, 0);
+}
+
+TEST_F(LayerTransactionTest, UncacheBufferTwice_BufferState) {
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+
+    int32_t bufferId = -1;
+    ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+    ASSERT_GE(bufferId, 0);
+
+    ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+    mClient->uncacheBuffer(bufferId);
+}
+
+TEST_F(LayerTransactionTest, UncacheBufferInvalidId_BufferState) {
+    mClient->uncacheBuffer(-1);
+    mClient->uncacheBuffer(0);
+    mClient->uncacheBuffer(1);
+    mClient->uncacheBuffer(5);
+    mClient->uncacheBuffer(1000);
+}
+
+TEST_F(LayerTransactionTest, SetCachedBuffer_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+
+    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    int32_t bufferId = -1;
+    ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(buffer, &bufferId));
+    ASSERT_GE(bufferId, 0);
+
+    Transaction().setCachedBuffer(layer, bufferId).apply();
+
+    auto shot = screenshot();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+
+    ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+}
+
+TEST_F(LayerTransactionTest, SetCachedBufferDelayed_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> cachedBuffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    int32_t bufferId = -1;
+    ASSERT_EQ(NO_ERROR, mClient->cacheBuffer(cachedBuffer, &bufferId));
+    ASSERT_GE(bufferId, 0);
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::BLUE);
+    Transaction().setBuffer(layer, buffer).apply();
+    {
+        SCOPED_TRACE("Uncached buffer");
+
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    fillGraphicBufferColor(cachedBuffer, Rect(0, 0, 32, 32), Color::RED);
+    Transaction().setCachedBuffer(layer, bufferId).apply();
+    {
+        SCOPED_TRACE("Cached buffer");
+
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    ASSERT_EQ(NO_ERROR, mClient->uncacheBuffer(bufferId));
+}
+
 class ColorTransformHelper {
 public:
     static void DegammaColorSingle(half& s) {
@@ -2568,11 +2705,23 @@
         }
     }
 
+    void addExpectedPresentTime(nsecs_t expectedPresentTime) {
+        mExpectedPresentTime = expectedPresentTime;
+    }
+
     void verifyTransactionStats(const TransactionStats& transactionStats) const {
         const auto& [latchTime, presentFence, surfaceStats] = transactionStats;
         if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
             ASSERT_GE(latchTime, 0) << "bad latch time";
             ASSERT_NE(presentFence, nullptr);
+            if (mExpectedPresentTime >= 0) {
+                ASSERT_EQ(presentFence->wait(3000), NO_ERROR);
+                ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTime - nsecs_t(5 * 1e6));
+                // if the panel is running at 30 hz, at the worst case, our expected time just
+                // misses vsync and we have to wait another 33.3ms
+                ASSERT_LE(presentFence->getSignalTime(),
+                          mExpectedPresentTime + nsecs_t(66.666666 * 1e6));
+            }
         } else {
             ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented";
             ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched";
@@ -2623,6 +2772,7 @@
         }
     };
     ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
+    nsecs_t mExpectedPresentTime = -1;
     std::unordered_map<sp<IBinder>, ExpectedSurfaceResult, IBinderHash> mExpectedSurfaceResults;
 };
 
@@ -3272,6 +3422,143 @@
     EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
 }
 
+TEST_F(LayerCallbackTest, DesiredPresentTime) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the future
+    nsecs_t time = systemTime() + (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected.addExpectedPresentTime(time);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback1;
+    int err = fillTransaction(transaction, &callback1, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the future
+    nsecs_t time = systemTime() + (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected1;
+    expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected1.addExpectedPresentTime(time);
+
+    CallbackHelper callback2;
+    err = fillTransaction(transaction, &callback2, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 33ms after the first frame
+    time += (33.3 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected2;
+    expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                         ExpectedResult::Buffer::ACQUIRED,
+                         ExpectedResult::PreviousBuffer::RELEASED);
+    expected2.addExpectedPresentTime(time);
+
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback1;
+    int err = fillTransaction(transaction, &callback1, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the future
+    nsecs_t time = systemTime() + (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected1;
+    expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected1.addExpectedPresentTime(time);
+
+    CallbackHelper callback2;
+    err = fillTransaction(transaction, &callback2, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 33ms before the previous frame
+    time -= (33.3 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected2;
+    expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                         ExpectedResult::Buffer::ACQUIRED,
+                         ExpectedResult::PreviousBuffer::RELEASED);
+
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_Past) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the past
+    nsecs_t time = systemTime() - (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected.addExpectedPresentTime(systemTime());
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
 class LayerUpdateTest : public LayerTransactionTest {
 protected:
     virtual void SetUp() {
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index f63c911..ad31a40 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -46,7 +46,6 @@
         "SchedulerUtilsTest.cpp",
         "TimeStatsTest.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
-        "mock/DisplayHardware/MockDisplaySurface.cpp",
         "mock/DisplayHardware/MockPowerAdvisor.cpp",
         "mock/gui/MockGraphicBufferConsumer.cpp",
         "mock/gui/MockGraphicBufferProducer.cpp",
@@ -63,7 +62,6 @@
         "libcompositionengine",
         "libcompositionengine_mocks",
         "librenderengine_mocks",
-
     ],
     header_libs: [
         "libsurfaceflinger_headers",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index e417350..e972785 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -17,6 +17,7 @@
 #undef LOG_TAG
 #define LOG_TAG "CompositionTest"
 
+#include <compositionengine/mock/DisplaySurface.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <gui/IProducerListener.h>
@@ -33,7 +34,6 @@
 
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockDisplaySurface.h"
 #include "mock/MockDispSync.h"
 #include "mock/MockEventControlThread.h"
 #include "mock/MockEventThread.h"
@@ -144,7 +144,8 @@
     TestableSurfaceFlinger mFlinger;
     sp<DisplayDevice> mDisplay;
     sp<DisplayDevice> mExternalDisplay;
-    sp<mock::DisplaySurface> mDisplaySurface = new mock::DisplaySurface();
+    sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
+            new compositionengine::mock::DisplaySurface();
     mock::NativeWindow* mNativeWindow = new mock::NativeWindow();
 
     sp<GraphicBuffer> mBuffer = new GraphicBuffer();
@@ -326,11 +327,14 @@
     }
 
     static void setupHwcCompositionCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC)).Times(1);
+        EXPECT_CALL(*test->mDisplaySurface,
+                    prepareFrame(compositionengine::DisplaySurface::COMPOSITION_HWC))
+                .Times(1);
     }
 
     static void setupRECompositionCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GLES))
+        EXPECT_CALL(*test->mDisplaySurface,
+                    prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GLES))
                 .Times(1);
         EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
                 .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index ff84a62..8201704 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -19,6 +19,9 @@
 
 #include <type_traits>
 
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/mock/DisplaySurface.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
@@ -28,7 +31,6 @@
 #include "DisplayIdentificationTest.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockDisplaySurface.h"
 #include "mock/MockDispSync.h"
 #include "mock/MockEventControlThread.h"
 #include "mock/MockEventThread.h"
@@ -1152,8 +1154,10 @@
         EXPECT_CALL(*mNativeWindow, perform(30)).Times(1);
         auto displayDevice = mInjector.inject();
 
-        displayDevice->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
-                                        &mOutColorMode, &mOutRenderIntent);
+        displayDevice->getCompositionDisplay()
+                ->getDisplayColorProfile()
+                ->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
+                                   &mOutColorMode, &mOutRenderIntent);
     }
 
     ui::Dataspace mOutDataspace;
@@ -1227,7 +1231,8 @@
 template <typename Case>
 void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() {
     const sp<BBinder> displayToken = new BBinder();
-    const sp<mock::DisplaySurface> displaySurface = new mock::DisplaySurface();
+    const sp<compositionengine::mock::DisplaySurface> displaySurface =
+            new compositionengine::mock::DisplaySurface();
     const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer();
 
     // --------------------------------------------------------------------
@@ -1970,7 +1975,7 @@
 
     // A display is set up
     auto nativeWindow = new mock::NativeWindow();
-    auto displaySurface = new mock::DisplaySurface();
+    auto displaySurface = new compositionengine::mock::DisplaySurface();
     sp<GraphicBuffer> buf = new GraphicBuffer();
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.setNativeWindow(nativeWindow);
@@ -2014,7 +2019,7 @@
 
     // A display is set up
     auto nativeWindow = new mock::NativeWindow();
-    auto displaySurface = new mock::DisplaySurface();
+    auto displaySurface = new compositionengine::mock::DisplaySurface();
     sp<GraphicBuffer> buf = new GraphicBuffer();
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.setNativeWindow(nativeWindow);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 15a8c5a..687941a 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -218,7 +218,7 @@
     auto setupNewDisplayDeviceInternal(const wp<IBinder>& displayToken,
                                        const std::optional<DisplayId>& displayId,
                                        const DisplayDeviceState& state,
-                                       const sp<DisplaySurface>& dispSurface,
+                                       const sp<compositionengine::DisplaySurface>& dispSurface,
                                        const sp<IGraphicBufferProducer>& producer) {
         return mFlinger->setupNewDisplayDeviceInternal(displayToken, displayId, state, dispSurface,
                                                        producer);
@@ -492,7 +492,7 @@
             return *this;
         }
 
-        auto& setDisplaySurface(const sp<DisplaySurface>& displaySurface) {
+        auto& setDisplaySurface(const sp<compositionengine::DisplaySurface>& displaySurface) {
             mCreationArgs.displaySurface = displaySurface;
             return *this;
         }