Faked HWC for SurfaceFlinger testing

Infrastructure and initial port of transaction tests. Faking the HWC
allows exercising the real path through the SurfaceFlinger, not relying
on screen captures. Faked HWC also opens up the possibility of faking
interactions like display hotplugs.

The tests are verifying the composition rectangles instead of a set of
select pixels. GLES rendering differences won't affect the
results. Also, the test expectations become clearer.

The ported transaction tests ran roughly twice as fast when compared
with the original transaction test. This is mostly due to the thighter
control over the vsyncs.

Test: Running the test on Marlin
Change-Id: I1c876cda78db94c1965498af957e64fdd23459ce
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index cc93105..4775e4e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -2,3 +2,5 @@
     name: "libsurfaceflingerincludes",
     export_include_dirs: ["."],
 }
+
+subdirs = ["tests/fakehwc"]
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index e34fa16..704b17e 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -157,15 +157,11 @@
     write64(metadata.usage);
 }
 
-Composer::Composer(bool useVrComposer)
+Composer::Composer(const std::string& serviceName)
     : mWriter(kWriterInitialSize),
-      mIsUsingVrComposer(useVrComposer)
+      mIsUsingVrComposer(serviceName == std::string("vr"))
 {
-    if (mIsUsingVrComposer) {
-        mComposer = IComposer::getService("vr");
-    } else {
-        mComposer = IComposer::getService(); // use default name
-    }
+    mComposer = IComposer::getService(serviceName);
 
     if (mComposer == nullptr) {
         LOG_ALWAYS_FATAL("failed to get hwcomposer service");
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 96dd833..40d2a4c 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -136,7 +136,7 @@
 // Composer is a wrapper to IComposer, a proxy to server-side composer.
 class Composer {
 public:
-    Composer(bool useVrComposer);
+    Composer(const std::string& serviceName);
 
     std::vector<IComposer::Capability> getCapabilities();
     std::string dumpDebugInfo();
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 270a732..b749ce6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -88,8 +88,8 @@
 
 // Device methods
 
-Device::Device(bool useVrComposer)
-  : mComposer(std::make_unique<Hwc2::Composer>(useVrComposer)),
+Device::Device(const std::string& serviceName)
+  : mComposer(std::make_unique<Hwc2::Composer>(serviceName)),
     mCapabilities(),
     mDisplays(),
     mHotplug(),
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 404bb28..9bcda1e 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -63,10 +63,9 @@
 class Device
 {
 public:
-    // useVrComposer is passed to the composer HAL. When true, the composer HAL
-    // will use the vr composer service, otherwise it uses the real hardware
-    // composer.
-    Device(bool useVrComposer);
+    // Service name is expected to be 'default' or 'vr' for normal use.
+    // 'vr' will slightly modify the behavior of the mComposer.
+    Device(const std::string& serviceName);
     ~Device();
 
     friend class HWC2::Display;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index ac2dde2..abf7dd1 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -59,7 +59,7 @@
 
 // ---------------------------------------------------------------------------
 
-HWComposer::HWComposer(bool useVrComposer)
+HWComposer::HWComposer(const std::string& serviceName)
     : mHwcDevice(),
       mDisplayData(2),
       mFreeDisplaySlots(),
@@ -74,7 +74,7 @@
         mVSyncCounts[i] = 0;
     }
 
-    loadHwcModule(useVrComposer);
+    loadHwcModule(serviceName);
 }
 
 HWComposer::~HWComposer() {}
@@ -103,10 +103,11 @@
 }
 
 // Load and prepare the hardware composer module.  Sets mHwc.
-void HWComposer::loadHwcModule(bool useVrComposer)
+void HWComposer::loadHwcModule(const std::string& serviceName)
 {
     ALOGV("loadHwcModule");
-    mHwcDevice = std::make_unique<HWC2::Device>(useVrComposer);
+    mHwcDevice = std::make_unique<HWC2::Device>(serviceName);
+
     mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 7463362..3dfb65b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -75,10 +75,9 @@
         virtual ~EventHandler() {}
     };
 
-    // useVrComposer is passed to the composer HAL. When true, the composer HAL
-    // will use the vr composer service, otherwise it uses the real hardware
-    // composer.
-    HWComposer(bool useVrComposer);
+    // Uses the named composer service. Valid choices for normal use
+    // are 'default' and 'vr'.
+    HWComposer(const std::string& serviceName);
 
     ~HWComposer();
 
@@ -170,7 +169,7 @@
 private:
     static const int32_t VIRTUAL_DISPLAY_ID_BASE = 2;
 
-    void loadHwcModule(bool useVrComposer);
+    void loadHwcModule(const std::string& serviceName);
 
     bool isValidDisplay(int32_t displayId) const;
     static void validateChange(HWC2::Composition from, HWC2::Composition to);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8eb4067..3903a55 100755
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -40,6 +40,7 @@
 
 #include <gui/BufferItem.h>
 #include <gui/BufferQueue.h>
+#include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
 
 #include "clz.h"
@@ -2363,71 +2364,51 @@
 // debugging
 // ----------------------------------------------------------------------------
 
-void Layer::dump(String8& result, Colorizer& colorizer) const
-{
-    const Layer::State& s(getDrawingState());
-
-    colorizer.colorize(result, Colorizer::GREEN);
-    result.appendFormat(
-            "+ %s %p (%s)\n",
-            getTypeId(), this, getName().string());
-    colorizer.reset(result);
-
-    s.activeTransparentRegion.dump(result, "transparentRegion");
-    visibleRegion.dump(result, "visibleRegion");
-    surfaceDamageRegion.dump(result, "surfaceDamageRegion");
-    sp<Client> client(mClientRef.promote());
-    PixelFormat pf = PIXEL_FORMAT_UNKNOWN;
-    const sp<GraphicBuffer>& buffer(getActiveBuffer());
-    if (buffer != NULL) {
-        pf = buffer->getPixelFormat();
-    }
-
+LayerDebugInfo Layer::getLayerDebugInfo() const {
+    LayerDebugInfo info;
+    const Layer::State& ds = getDrawingState();
+    info.mName = getName();
     sp<Layer> parent = getParent();
-
-    result.appendFormat(            "      "
-            "layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), "
-            "crop=(%4d,%4d,%4d,%4d), finalCrop=(%4d,%4d,%4d,%4d), "
-            "isOpaque=%1d, invalidate=%1d, "
-            "dataspace=%s, pixelformat=%s "
-#ifdef USE_HWC2
-            "alpha=%.3f, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
-#else
-            "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
-#endif
-            "      client=%p parent=%s\n",
-            getLayerStack(), s.z,
-            s.active.transform.tx(), s.active.transform.ty(),
-            s.active.w, s.active.h,
-            s.crop.left, s.crop.top,
-            s.crop.right, s.crop.bottom,
-            s.finalCrop.left, s.finalCrop.top,
-            s.finalCrop.right, s.finalCrop.bottom,
-            isOpaque(s), contentDirty,
-            dataspaceDetails(getDataSpace()).c_str(), decodePixelFormat(pf).c_str(),
-            s.alpha, s.flags,
-            s.active.transform[0][0], s.active.transform[0][1],
-            s.active.transform[1][0], s.active.transform[1][1],
-            client.get(), parent == nullptr ? "none" : parent->getName().string());
-
-    sp<const GraphicBuffer> buf0(mActiveBuffer);
-    uint32_t w0=0, h0=0, s0=0, f0=0;
-    if (buf0 != 0) {
-        w0 = buf0->getWidth();
-        h0 = buf0->getHeight();
-        s0 = buf0->getStride();
-        f0 = buf0->format;
+    info.mParentName = (parent == nullptr ? std::string("none") : parent->getName().string());
+    info.mType = String8(getTypeId());
+    info.mTransparentRegion = ds.activeTransparentRegion;
+    info.mVisibleRegion = visibleRegion;
+    info.mSurfaceDamageRegion = surfaceDamageRegion;
+    info.mLayerStack = getLayerStack();
+    info.mX = ds.active.transform.tx();
+    info.mY = ds.active.transform.ty();
+    info.mZ = ds.z;
+    info.mWidth = ds.active.w;
+    info.mHeight = ds.active.h;
+    info.mCrop = ds.crop;
+    info.mFinalCrop = ds.finalCrop;
+    info.mAlpha = ds.alpha;
+    info.mFlags = ds.flags;
+    info.mPixelFormat = getPixelFormat();
+    info.mDataSpace = getDataSpace();
+    info.mMatrix[0][0] = ds.active.transform[0][0];
+    info.mMatrix[0][1] = ds.active.transform[0][1];
+    info.mMatrix[1][0] = ds.active.transform[1][0];
+    info.mMatrix[1][1] = ds.active.transform[1][1];
+    {
+        sp<const GraphicBuffer> activeBuffer = getActiveBuffer();
+        if (activeBuffer != 0) {
+            info.mActiveBufferWidth = activeBuffer->getWidth();
+            info.mActiveBufferHeight = activeBuffer->getHeight();
+            info.mActiveBufferStride = activeBuffer->getStride();
+            info.mActiveBufferFormat = activeBuffer->format;
+        } else {
+            info.mActiveBufferWidth = 0;
+            info.mActiveBufferHeight = 0;
+            info.mActiveBufferStride = 0;
+            info.mActiveBufferFormat = 0;
+        }
     }
-    result.appendFormat(
-            "      "
-            "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X],"
-            " queued-frames=%d, mRefreshPending=%d\n",
-            mFormat, w0, h0, s0,f0,
-            mQueuedFrames, mRefreshPending);
-
-    if (mSurfaceFlingerConsumer != 0) {
-        mSurfaceFlingerConsumer->dumpState(result, "            ");
-    }
+    info.mNumQueuedFrames = getQueuedFrameCount();
+    info.mRefreshPending = isBufferLatched();
+    info.mIsOpaque = isOpaque(ds);
+    info.mContentDirty = contentDirty;
+    return info;
 }
 
 #ifdef USE_HWC2
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2306d1a..8df8c49 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -60,6 +60,7 @@
 class DisplayDevice;
 class GraphicBuffer;
 class SurfaceFlinger;
+class LayerDebugInfo;
 
 // ---------------------------------------------------------------------------
 
@@ -441,6 +442,8 @@
     bool hasQueuedFrame() const { return mQueuedFrames > 0 ||
             mSidebandStreamChanged || mAutoRefresh; }
 
+    int32_t getQueuedFrameCount() const { return mQueuedFrames; }
+
 #ifdef USE_HWC2
     // -----------------------------------------------------------------------
 
@@ -489,9 +492,9 @@
     inline  const State&    getCurrentState() const { return mCurrentState; }
     inline  State&          getCurrentState()       { return mCurrentState; }
 
+    LayerDebugInfo getLayerDebugInfo() const;
 
     /* always call base class first */
-    void dump(String8& result, Colorizer& colorizer) const;
 #ifdef USE_HWC2
     static void miniDumpHeader(String8& result);
     void miniDump(String8& result, int32_t hwcId) const;
@@ -689,6 +692,9 @@
     sp<IGraphicBufferProducer> getProducer() const;
     const String8& getName() const;
     void notifyAvailableFrames();
+
+    PixelFormat getPixelFormat() const { return mFormat; }
+
 private:
 
     // -----------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index aaaafbf..4154d6a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -46,6 +46,7 @@
 #include <gui/BufferQueue.h>
 #include <gui/GuiConfig.h>
 #include <gui/IDisplayEventConnection.h>
+#include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
 
 #include <ui/GraphicBufferAllocator.h>
@@ -123,6 +124,21 @@
 int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
 bool SurfaceFlinger::hasWideColorDisplay;
 
+
+std::string getHwcServiceName() {
+    char value[PROPERTY_VALUE_MAX] = {};
+    property_get("debug.sf.hwc_service_name", value, "default");
+    ALOGI("Using HWComposer service: '%s'", value);
+    return std::string(value);
+}
+
+bool useTrebleTestingOverride() {
+    char value[PROPERTY_VALUE_MAX] = {};
+    property_get("debug.sf.treble_testing_override", value, "false");
+    ALOGI("Treble testing override: '%s'", value);
+    return std::string(value) == "true";
+}
+
 SurfaceFlinger::SurfaceFlinger()
     :   BnSurfaceComposer(),
         mTransactionFlags(0),
@@ -134,6 +150,7 @@
         mHwc(nullptr),
         mRealHwc(nullptr),
         mVrHwc(nullptr),
+        mHwcServiceName(getHwcServiceName()),
         mRenderEngine(nullptr),
         mBootTime(systemTime()),
         mBuiltinDisplays(),
@@ -233,6 +250,15 @@
     // but since /data may be encrypted, we need to wait until after vold
     // comes online to attempt to read the property. The property is
     // instead read after the boot animation
+
+    if (useTrebleTestingOverride()) {
+        // Without the override SurfaceFlinger cannot connect to HIDL
+        // services that are not listed in the manifests.  Considered
+        // deriving the setting from the set service name, but it
+        // would be brittle if the name that's not 'default' is used
+        // for production purposes later on.
+        setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+    }
 }
 
 void SurfaceFlinger::onFirstRef()
@@ -594,7 +620,7 @@
     // initialize the primary display.
     LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
         "Starting with vr flinger active is not currently supported.");
-    mRealHwc = new HWComposer(false);
+    mRealHwc = new HWComposer(mHwcServiceName);
     mHwc = mRealHwc;
     mHwc->setEventHandler(static_cast<HWComposer::EventHandler*>(this));
 
@@ -1055,6 +1081,33 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+    if ((uid != AID_SHELL) &&
+            !PermissionCache::checkPermission(sDump, pid, uid)) {
+        ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid);
+        return PERMISSION_DENIED;
+    }
+
+    // Try to acquire a lock for 1s, fail gracefully
+    const status_t err = mStateLock.timedLock(s2ns(1));
+    const bool locked = (err == NO_ERROR);
+    if (!locked) {
+        ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err);
+        return TIMED_OUT;
+    }
+
+    outLayers->clear();
+    mCurrentState.traverseInZOrder([&](Layer* layer) {
+        outLayers->push_back(layer->getLayerDebugInfo());
+    });
+
+    mStateLock.unlock();
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
@@ -1312,7 +1365,7 @@
 
     if (vrFlingerRequestsDisplay && !mVrHwc) {
         // Construct new HWComposer without holding any locks.
-        mVrHwc = new HWComposer(true);
+        mVrHwc = new HWComposer("vr");
 
         // Set up the event handlers. This step is neccessary to initialize the internal state of
         // the hardware composer object properly. Our callbacks are designed such that if they are
@@ -3706,7 +3759,7 @@
     result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
     colorizer.reset(result);
     mCurrentState.traverseInZOrder([&](Layer* layer) {
-        layer->dump(result, colorizer);
+        result.append(to_string(layer->getLayerDebugInfo()).c_str());
     });
 
     /*
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index acfad46..5123b58 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -300,6 +300,7 @@
             HdrCapabilities* outCapabilities) const;
     virtual status_t enableVSyncInjections(bool enable);
     virtual status_t injectVSync(nsecs_t when);
+    virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const;
 
 
     /* ------------------------------------------------------------------------
@@ -628,6 +629,7 @@
 #ifdef USE_HWC2
     HWComposer* mRealHwc;
     HWComposer* mVrHwc;
+    const std::string mHwcServiceName; // "default" for real use, something else for testing.
 #endif
     // constant members (no synchronization needed for access)
     RenderEngine* mRenderEngine;
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index dae03b3..7aaa42a 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -41,6 +41,7 @@
 #include <gui/BufferQueue.h>
 #include <gui/GuiConfig.h>
 #include <gui/IDisplayEventConnection.h>
+#include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
 
 #include <ui/GraphicBufferAllocator.h>
@@ -931,6 +932,34 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+    if ((uid != AID_SHELL) &&
+            !PermissionCache::checkPermission(sDump, pid, uid)) {
+        ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid);
+        return PERMISSION_DENIED;
+    }
+
+    // Try to acquire a lock for 1s, fail gracefully
+    status_t err = mStateLock.timedLock(s2ns(1));
+    bool locked = (err == NO_ERROR);
+    if (!locked) {
+        ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err);
+        return TIMED_OUT;
+    }
+
+    outLayers->clear();
+    mCurrentState.traverseInZOrder([&](Layer* layer) {
+            outLayers->push_back(layer->getLayerDebugInfo());
+        });
+
+    mStateLock.unlock();
+
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
@@ -3261,7 +3290,7 @@
     result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
     colorizer.reset(result);
     mCurrentState.traverseInZOrder([&](Layer* layer) {
-        layer->dump(result, colorizer);
+        result.append(to_string(layer->getLayerDebugInfo()).c_str());
     });
 
     /*
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
new file mode 100644
index 0000000..94f3f25
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -0,0 +1,35 @@
+cc_test {
+    name: "sffakehwc_test",
+    srcs: [
+         "FakeComposerClient.cpp",
+         "FakeComposerService.cpp",
+         "FakeComposerUtils.cpp",
+         "SFFakeHwc_test.cpp"
+    ],
+    shared_libs: [
+        "libcutils",
+        "libutils",
+        "libbinder",
+        "libui",
+        "libgui",
+        "liblog",
+        "libnativewindow",
+        "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.mapper@2.0",
+        "libhwbinder",
+        "libhardware",
+        "libhidlbase",
+        "libsync",
+        "libfmq",
+        "libbase",
+        "libhidltransport"
+    ],
+    static_libs: [
+        "libhwcomposer-client",
+        "libsurfaceflingerincludes",
+        "libtrace_proto",
+        "libgmock"
+    ],
+    tags: ["tests"],
+    test_suites: ["device-tests"]
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
new file mode 100644
index 0000000..60916f3
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -0,0 +1,613 @@
+/*
+ * Copyright (C) 2017 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 "FakeComposer"
+
+#include "FakeComposerClient.h"
+
+#include <gui/SurfaceComposerClient.h>
+
+#include <log/log.h>
+
+#include <gtest/gtest.h>
+
+#include <inttypes.h>
+#include <time.h>
+#include <algorithm>
+#include <condition_variable>
+#include <iostream>
+#include <mutex>
+#include <set>
+#include <thread>
+
+constexpr Config NULL_DISPLAY_CONFIG = static_cast<Config>(0);
+constexpr Display DEFAULT_DISPLAY = static_cast<Display>(1);
+
+using namespace sftest;
+
+using android::Condition;
+using android::Mutex;
+
+using Clock = std::chrono::steady_clock;
+using TimePoint = std::chrono::time_point<Clock>;
+
+namespace {
+
+// Internal state of a layer in the HWC API.
+class LayerImpl {
+public:
+    LayerImpl() = default;
+
+    bool mValid = true;
+    RenderState mRenderState;
+    uint32_t mZ = 0;
+};
+
+// Struct for storing per frame rectangle state. Contains the render
+// state shared to the test case. Basically a snapshot and a subset of
+// LayerImpl sufficient to re-create the pixels of a layer for the
+// frame.
+struct FrameRect {
+public:
+    FrameRect(Layer layer_, const RenderState& state, uint32_t z_)
+          : layer(layer_), renderState(state), z(z_) {}
+
+    const Layer layer;
+    const RenderState renderState;
+    const uint32_t z;
+};
+
+// Collection of FrameRects forming one rendered frame. Could store
+// related fences and other data in the future.
+class Frame {
+public:
+    Frame() = default;
+    std::vector<std::unique_ptr<FrameRect>> rectangles;
+};
+
+class DelayedEventGenerator {
+public:
+    DelayedEventGenerator(std::function<void()> onTimerExpired)
+          : mOnTimerExpired(onTimerExpired), mThread([this]() { loop(); }) {}
+
+    ~DelayedEventGenerator() {
+        ALOGI("DelayedEventGenerator exiting.");
+        {
+            std::unique_lock<std::mutex> lock(mMutex);
+            mRunning = false;
+            mWakeups.clear();
+            mCondition.notify_one();
+        }
+        mThread.join();
+        ALOGI("DelayedEventGenerator exited.");
+    }
+
+    void wakeAfter(std::chrono::nanoseconds waitTime) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mWakeups.insert(Clock::now() + waitTime);
+        mCondition.notify_one();
+    }
+
+private:
+    void loop() {
+        while (true) {
+            // Lock scope
+            {
+                std::unique_lock<std::mutex> lock(mMutex);
+                mCondition.wait(lock, [this]() { return !mRunning || !mWakeups.empty(); });
+                if (!mRunning && mWakeups.empty()) {
+                    // This thread should only exit once the destructor has been called and all
+                    // wakeups have been processed
+                    return;
+                }
+
+                // At this point, mWakeups will not be empty
+
+                TimePoint target = *(mWakeups.begin());
+                auto status = mCondition.wait_until(lock, target);
+                while (status == std::cv_status::no_timeout) {
+                    // This was either a spurious wakeup or another wakeup was added, so grab the
+                    // oldest point and wait again
+                    target = *(mWakeups.begin());
+                    status = mCondition.wait_until(lock, target);
+                }
+
+                // status must have been timeout, so we can finally clear this point
+                mWakeups.erase(target);
+            }
+            // Callback *without* locks!
+            mOnTimerExpired();
+        }
+    }
+
+    std::function<void()> mOnTimerExpired;
+    std::thread mThread;
+    std::mutex mMutex;
+    std::condition_variable mCondition;
+    bool mRunning = true;
+    std::set<TimePoint> mWakeups;
+};
+
+} // namespace
+
+FakeComposerClient::FakeComposerClient()
+      : mCallbacksOn(false),
+        mClient(nullptr),
+        mCurrentConfig(NULL_DISPLAY_CONFIG),
+        mVsyncEnabled(false),
+        mLayers(),
+        mDelayedEventGenerator(
+                std::make_unique<DelayedEventGenerator>([this]() { this->requestVSync(); })),
+        mSurfaceComposer(nullptr) {}
+
+FakeComposerClient::~FakeComposerClient() {}
+
+void FakeComposerClient::removeClient() {
+    ALOGV("removeClient");
+    // TODO: Ahooga! Only thing current lifetime management choices in
+    // APIs make possible. Sad.
+    delete this;
+}
+
+void FakeComposerClient::enableCallback(bool enable) {
+    ALOGV("enableCallback");
+    mCallbacksOn = enable;
+    if (mCallbacksOn) {
+        mClient->onHotplug(DEFAULT_DISPLAY, IComposerCallback::Connection::CONNECTED);
+    }
+}
+
+void FakeComposerClient::hotplugDisplay(Display display, IComposerCallback::Connection state) {
+    if (mCallbacksOn) {
+        mClient->onHotplug(display, state);
+    }
+}
+
+uint32_t FakeComposerClient::getMaxVirtualDisplayCount() {
+    ALOGV("getMaxVirtualDisplayCount");
+    return 1;
+}
+
+Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/,
+                                               PixelFormat* /*format*/, Display* /*outDisplay*/) {
+    ALOGV("createVirtualDisplay");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) {
+    ALOGV("destroyVirtualDisplay");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) {
+    ALOGV("createLayer");
+    *outLayer = mLayers.size();
+    auto newLayer = std::make_unique<LayerImpl>();
+    mLayers.push_back(std::move(newLayer));
+    return Error::NONE;
+}
+
+Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) {
+    ALOGV("destroyLayer");
+    mLayers[layer]->mValid = false;
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getActiveConfig(Display /*display*/, Config* outConfig) {
+    ALOGV("getActiveConfig");
+
+    // TODO Assert outConfig != nullptr
+
+    // TODO This is my reading of the
+    // IComposerClient::getActiveConfig, but returning BAD_CONFIG
+    // seems to not fit SurfaceFlinger plans. See version 2 below.
+    // if (mCurrentConfig == NULL_DISPLAY_CONFIG) {
+    //     return Error::BAD_CONFIG;
+    // }
+    //*outConfig = mCurrentConfig;
+    *outConfig = 1; // Very special config for you my friend
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/,
+                                                 uint32_t /*height*/, PixelFormat /*format*/,
+                                                 Dataspace /*dataspace*/) {
+    ALOGV("getClientTargetSupport");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getColorModes(Display /*display*/, hidl_vec<ColorMode>* /*outModes*/) {
+    ALOGV("getColorModes");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayAttribute(Display display, Config config,
+                                              IComposerClient::Attribute attribute,
+                                              int32_t* outValue) {
+    ALOGV("getDisplayAttribute (%d, %d, %d, %p)", static_cast<int>(display),
+          static_cast<int>(config), static_cast<int>(attribute), outValue);
+
+    // TODO: SOOO much fun to be had with these alone
+    switch (attribute) {
+        case IComposerClient::Attribute::WIDTH:
+            *outValue = 1920;
+            break;
+        case IComposerClient::Attribute::HEIGHT:
+            *outValue = 1080;
+            break;
+        case IComposerClient::Attribute::VSYNC_PERIOD:
+            *outValue = 1666666666;
+            break; // TOOD: Tests break down if lowered to 16ms?
+        case IComposerClient::Attribute::DPI_X:
+            *outValue = 240;
+            break;
+        case IComposerClient::Attribute::DPI_Y:
+            *outValue = 240;
+            break;
+        default:
+            LOG_ALWAYS_FATAL("Say what!?! New attribute");
+    }
+
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayConfigs(Display /*display*/, hidl_vec<Config>* outConfigs) {
+    ALOGV("getDisplayConfigs");
+    // TODO assert display == 1, outConfigs != nullptr
+
+    outConfigs->resize(1);
+    (*outConfigs)[0] = 1;
+
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) {
+    ALOGV("getDisplayName");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getDisplayType(Display /*display*/,
+                                         IComposerClient::DisplayType* outType) {
+    ALOGV("getDisplayType");
+    // TODO: This setting nothing on the output had no effect on initial trials. Is first display
+    // assumed to be physical?
+    *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL);
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) {
+    ALOGV("getDozeSupport");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::getHdrCapabilities(Display /*display*/, hidl_vec<Hdr>* /*outTypes*/,
+                                             float* /*outMaxLuminance*/,
+                                             float* /*outMaxAverageLuminance*/,
+                                             float* /*outMinLuminance*/) {
+    ALOGV("getHdrCapabilities");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setActiveConfig(Display /*display*/, Config config) {
+    ALOGV("setActiveConfig");
+    mCurrentConfig = config;
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setColorMode(Display /*display*/, ColorMode /*mode*/) {
+    ALOGV("setColorMode");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setPowerMode(Display /*display*/, IComposerClient::PowerMode /*mode*/) {
+    ALOGV("setPowerMode");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setVsyncEnabled(Display /*display*/, IComposerClient::Vsync enabled) {
+    mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE);
+    ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/,
+                                            int32_t /*hint*/) {
+    ALOGV("setColorTransform");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/,
+                                          int32_t /*acquireFence*/, int32_t /*dataspace*/,
+                                          const std::vector<hwc_rect_t>& /*damage*/) {
+    ALOGV("setClientTarget");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/,
+                                          int32_t /*releaseFence*/) {
+    ALOGV("setOutputBuffer");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::validateDisplay(
+        Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
+        std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
+        uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
+        std::vector<uint32_t>* /*outRequestMasks*/) {
+    ALOGV("validateDisplay");
+    // TODO: Assume touching nothing means All Korrekt!
+    return Error::NONE;
+}
+
+Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) {
+    ALOGV("acceptDisplayChanges");
+    // Didn't ask for changes because software is omnipotent.
+    return Error::NONE;
+}
+
+bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) {
+    return a->z <= b->z;
+}
+
+Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/,
+                                         std::vector<Layer>* /*outLayers*/,
+                                         std::vector<int32_t>* /*outReleaseFences*/) {
+    ALOGV("presentDisplay");
+    // TODO Leaving layers and their fences out for now. Doing so
+    // means that we've already processed everything. Important to
+    // test that the fences are respected, though. (How?)
+
+    std::unique_ptr<Frame> newFrame(new Frame);
+    for (uint64_t layer = 0; layer < mLayers.size(); layer++) {
+        const LayerImpl& layerImpl = *mLayers[layer];
+
+        if (!layerImpl.mValid) continue;
+
+        auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ);
+        newFrame->rectangles.push_back(std::move(rect));
+    }
+    std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering);
+    {
+        Mutex::Autolock _l(mStateMutex);
+        mFrames.push_back(std::move(newFrame));
+        mFramesAvailable.broadcast();
+    }
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/,
+                                                 int32_t /*x*/, int32_t /*y*/) {
+    ALOGV("setLayerCursorPosition");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer, buffer_handle_t buffer,
+                                         int32_t acquireFence) {
+    ALOGV("setLayerBuffer");
+    LayerImpl& l = getLayerImpl(layer);
+    if (buffer != l.mRenderState.mBuffer) {
+        l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not?
+    }
+    l.mRenderState.mBuffer = buffer;
+    l.mRenderState.mAcquireFence = acquireFence;
+
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/,
+                                                const std::vector<hwc_rect_t>& /*damage*/) {
+    ALOGV("setLayerSurfaceDamage");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) {
+    ALOGV("setLayerBlendMode");
+    getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode);
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer,
+                                        IComposerClient::Color color) {
+    ALOGV("setLayerColor");
+    getLayerImpl(layer).mRenderState.mLayerColor.r = color.r;
+    getLayerImpl(layer).mRenderState.mLayerColor.g = color.g;
+    getLayerImpl(layer).mRenderState.mLayerColor.b = color.b;
+    getLayerImpl(layer).mRenderState.mLayerColor.a = color.a;
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/,
+                                                  int32_t /*type*/) {
+    ALOGV("setLayerCompositionType");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/,
+                                            int32_t /*dataspace*/) {
+    ALOGV("setLayerDataspace");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer,
+                                               const hwc_rect_t& frame) {
+    ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right,
+          frame.bottom);
+    getLayerImpl(layer).mRenderState.mDisplayFrame = frame;
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) {
+    ALOGV("setLayerPlaneAlpha");
+    getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha;
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/,
+                                                 buffer_handle_t /*stream*/) {
+    ALOGV("setLayerSidebandStream");
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer,
+                                             const hwc_frect_t& crop) {
+    ALOGV("setLayerSourceCrop");
+    getLayerImpl(layer).mRenderState.mSourceCrop = crop;
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer, int32_t transform) {
+    ALOGV("setLayerTransform");
+    getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform);
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer,
+                                                const std::vector<hwc_rect_t>& visible) {
+    ALOGV("setLayerVisibleRegion");
+    getLayerImpl(layer).mRenderState.mVisibleRegion = visible;
+    return Error::NONE;
+}
+
+Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) {
+    ALOGV("setLayerZOrder");
+    getLayerImpl(layer).mZ = z;
+    return Error::NONE;
+}
+
+//////////////////////////////////////////////////////////////////
+
+void FakeComposerClient::setClient(ComposerClient* client) {
+    mClient = client;
+}
+
+void FakeComposerClient::requestVSync(uint64_t vsyncTime) {
+    if (mCallbacksOn) {
+        uint64_t timestamp = vsyncTime;
+        ALOGV("Vsync");
+        if (timestamp == 0) {
+            struct timespec ts;
+            clock_gettime(CLOCK_MONOTONIC, &ts);
+            timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
+        }
+        if (mSurfaceComposer != nullptr) {
+            mSurfaceComposer->injectVSync(timestamp);
+        } else {
+            mClient->onVsync(DEFAULT_DISPLAY, timestamp);
+        }
+    }
+}
+
+void FakeComposerClient::runVSyncAfter(std::chrono::nanoseconds wait) {
+    mDelayedEventGenerator->wakeAfter(wait);
+}
+
+LayerImpl& FakeComposerClient::getLayerImpl(Layer handle) {
+    // TODO Change these to an internal state check that can be
+    // invoked from the gtest? GTest macros do not seem all that safe
+    // when used outside the test class
+    EXPECT_GE(handle, static_cast<Layer>(0));
+    EXPECT_LT(handle, mLayers.size());
+    return *(mLayers[handle]);
+}
+
+int FakeComposerClient::getFrameCount() const {
+    return mFrames.size();
+}
+
+static std::vector<RenderState> extractRenderState(
+        const std::vector<std::unique_ptr<FrameRect>>& internalRects) {
+    std::vector<RenderState> result;
+    result.reserve(internalRects.size());
+    for (const std::unique_ptr<FrameRect>& rect : internalRects) {
+        result.push_back(rect->renderState);
+    }
+    return result;
+}
+
+std::vector<RenderState> FakeComposerClient::getFrameRects(int frame) const {
+    Mutex::Autolock _l(mStateMutex);
+    return extractRenderState(mFrames[frame]->rectangles);
+}
+
+std::vector<RenderState> FakeComposerClient::getLatestFrame() const {
+    Mutex::Autolock _l(mStateMutex);
+    return extractRenderState(mFrames[mFrames.size() - 1]->rectangles);
+}
+
+void FakeComposerClient::runVSyncAndWait(std::chrono::nanoseconds maxWait) {
+    int currentFrame = 0;
+    {
+        Mutex::Autolock _l(mStateMutex); // I hope this is ok...
+        currentFrame = static_cast<int>(mFrames.size());
+        requestVSync();
+    }
+    waitUntilFrame(currentFrame + 1, maxWait);
+}
+
+void FakeComposerClient::waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait) const {
+    Mutex::Autolock _l(mStateMutex);
+    while (mFrames.size() < static_cast<size_t>(targetFrame)) {
+        android::status_t result = mFramesAvailable.waitRelative(mStateMutex, maxWait.count());
+        if (result == android::TIMED_OUT) {
+            ALOGE("Waiting for frame %d (at frame %zu now) timed out after %lld ns", targetFrame,
+                  mFrames.size(), maxWait.count());
+            return;
+        }
+    }
+}
+
+void FakeComposerClient::clearFrames() {
+    Mutex::Autolock _l(mStateMutex);
+    mFrames.clear();
+    for (const std::unique_ptr<LayerImpl>& layer : mLayers) {
+        if (layer->mValid) {
+            layer->mRenderState.mSwapCount = 0;
+        }
+    }
+}
+
+void FakeComposerClient::onSurfaceFlingerStart() {
+    mSurfaceComposer == nullptr;
+    do {
+        mSurfaceComposer = new android::SurfaceComposerClient;
+        android::status_t initResult = mSurfaceComposer->initCheck();
+        if (initResult != android::NO_ERROR) {
+            ALOGD("Init result: %d", initResult);
+            mSurfaceComposer = nullptr;
+            std::this_thread::sleep_for(10ms);
+        }
+    } while (mSurfaceComposer == nullptr);
+    ALOGD("SurfaceComposerClient created");
+    mSurfaceComposer->enableVSyncInjections(true);
+}
+
+void FakeComposerClient::onSurfaceFlingerStop() {
+    mSurfaceComposer->dispose();
+    mSurfaceComposer.clear();
+}
+
+// Includes destroyed layers, stored in order of creation.
+int FakeComposerClient::getLayerCount() const {
+    return mLayers.size();
+}
+
+Layer FakeComposerClient::getLayer(size_t index) const {
+    // NOTE: If/when passing calls through to actual implementation,
+    // this might get more involving.
+    return static_cast<Layer>(index);
+}
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
new file mode 100644
index 0000000..294abb2
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2017 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 "ComposerClient.h"
+#include "RenderState.h"
+
+#include <utils/Condition.h>
+
+#include <chrono>
+
+using namespace android::hardware::graphics::composer::V2_1;
+using namespace android::hardware::graphics::composer::V2_1::implementation;
+using namespace android::hardware;
+using namespace std::chrono_literals;
+
+namespace {
+class LayerImpl;
+class Frame;
+class DelayedEventGenerator;
+} // namespace
+
+namespace android {
+class SurfaceComposerClient;
+} // namespace android
+
+namespace sftest {
+
+class FakeComposerClient : public ComposerBase {
+public:
+    FakeComposerClient();
+    virtual ~FakeComposerClient();
+
+    void removeClient() override;
+    void enableCallback(bool enable) override;
+    uint32_t getMaxVirtualDisplayCount() override;
+    Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
+                               Display* outDisplay) override;
+    Error destroyVirtualDisplay(Display display) override;
+    Error createLayer(Display display, Layer* outLayer) override;
+    Error destroyLayer(Display display, Layer layer) override;
+
+    Error getActiveConfig(Display display, Config* outConfig) override;
+    Error getClientTargetSupport(Display display, uint32_t width, uint32_t height,
+                                 PixelFormat format, Dataspace dataspace) override;
+    Error getColorModes(Display display, hidl_vec<ColorMode>* outModes) override;
+    Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
+                              int32_t* outValue) override;
+    Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
+    Error getDisplayName(Display display, hidl_string* outName) override;
+    Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
+    Error getDozeSupport(Display display, bool* outSupport) override;
+    Error getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes, float* outMaxLuminance,
+                             float* outMaxAverageLuminance, float* outMinLuminance) override;
+
+    Error setActiveConfig(Display display, Config config) override;
+    Error setColorMode(Display display, ColorMode mode) override;
+    Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
+    Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
+
+    Error setColorTransform(Display display, const float* matrix, int32_t hint) override;
+    Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence,
+                          int32_t dataspace, const std::vector<hwc_rect_t>& damage) override;
+    Error setOutputBuffer(Display display, buffer_handle_t buffer, int32_t releaseFence) override;
+    Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers,
+                          std::vector<IComposerClient::Composition>* outCompositionTypes,
+                          uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
+                          std::vector<uint32_t>* outRequestMasks) override;
+    Error acceptDisplayChanges(Display display) override;
+    Error presentDisplay(Display display, int32_t* outPresentFence, std::vector<Layer>* outLayers,
+                         std::vector<int32_t>* outReleaseFences) override;
+
+    Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
+    Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
+                         int32_t acquireFence) override;
+    Error setLayerSurfaceDamage(Display display, Layer layer,
+                                const std::vector<hwc_rect_t>& damage) override;
+    Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
+    Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override;
+    Error setLayerCompositionType(Display display, Layer layer, int32_t type) override;
+    Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override;
+    Error setLayerDisplayFrame(Display display, Layer layer, const hwc_rect_t& frame) override;
+    Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
+    Error setLayerSidebandStream(Display display, Layer layer, buffer_handle_t stream) override;
+    Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override;
+    Error setLayerTransform(Display display, Layer layer, int32_t transform) override;
+    Error setLayerVisibleRegion(Display display, Layer layer,
+                                const std::vector<hwc_rect_t>& visible) override;
+    Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+
+    void setClient(ComposerClient* client);
+
+    void requestVSync(uint64_t vsyncTime = 0);
+    // We don't want tests hanging, so always use a timeout. Remember
+    // to always check the number of frames with test ASSERT_!
+    // Wait until next frame is rendered after requesting vsync.
+    void runVSyncAndWait(std::chrono::nanoseconds maxWait = 100ms);
+    void runVSyncAfter(std::chrono::nanoseconds wait);
+
+    int getFrameCount() const;
+    // We don't want tests hanging, so always use a timeout. Remember
+    // to always check the number of frames with test ASSERT_!
+    void waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait = 100ms) const;
+    std::vector<RenderState> getFrameRects(int frame) const;
+    std::vector<RenderState> getLatestFrame() const;
+    void clearFrames();
+
+    void onSurfaceFlingerStart();
+    void onSurfaceFlingerStop();
+
+    int getLayerCount() const;
+    Layer getLayer(size_t index) const;
+
+    void hotplugDisplay(Display display, IComposerCallback::Connection state);
+
+private:
+    LayerImpl& getLayerImpl(Layer handle);
+
+    bool mCallbacksOn;
+    ComposerClient* mClient;
+    Config mCurrentConfig;
+    bool mVsyncEnabled;
+    std::vector<std::unique_ptr<LayerImpl>> mLayers;
+    std::vector<std::unique_ptr<Frame>> mFrames;
+    // Using a pointer to hide the implementation into the CPP file.
+    std::unique_ptr<DelayedEventGenerator> mDelayedEventGenerator;
+    android::sp<android::SurfaceComposerClient> mSurfaceComposer; // For VSync injections
+    mutable android::Mutex mStateMutex;
+    mutable android::Condition mFramesAvailable;
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
new file mode 100644
index 0000000..c411604
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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 "FakeHwcService"
+#include <log/log.h>
+
+#include "FakeComposerService.h"
+
+using namespace android::hardware;
+
+namespace sftest {
+
+FakeComposerService::FakeComposerService(android::sp<ComposerClient>& client) : mClient(client) {}
+
+FakeComposerService::~FakeComposerService() {
+    ALOGI("Maybe killing client %p", mClient.get());
+    // Rely on sp to kill the client.
+}
+
+Return<void> FakeComposerService::getCapabilities(getCapabilities_cb hidl_cb) {
+    ALOGI("FakeComposerService::getCapabilities");
+    hidl_cb(hidl_vec<Capability>());
+    return Void();
+}
+
+Return<void> FakeComposerService::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+    ALOGI("FakeComposerService::dumpDebugInfo");
+    hidl_cb(hidl_string());
+    return Void();
+}
+
+Return<void> FakeComposerService::createClient(createClient_cb hidl_cb) {
+    ALOGI("FakeComposerService::createClient %p", mClient.get());
+    mClient->initialize();
+    hidl_cb(Error::NONE, mClient);
+    return Void();
+}
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
new file mode 100644
index 0000000..5204084
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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 "ComposerClient.h"
+
+using namespace android::hardware::graphics::composer::V2_1;
+using namespace android::hardware::graphics::composer::V2_1::implementation;
+using android::hardware::Return;
+
+namespace sftest {
+
+class FakeComposerService : public IComposer {
+public:
+    FakeComposerService(android::sp<ComposerClient>& client);
+    virtual ~FakeComposerService();
+
+    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
+    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
+    Return<void> createClient(createClient_cb hidl_cb) override;
+
+private:
+    android::sp<ComposerClient> mClient;
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
new file mode 100644
index 0000000..51956ec
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2017 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 "FakeHwcUtil"
+#include <log/log.h>
+
+#include "FakeComposerUtils.h"
+#include "RenderState.h"
+
+#include "SurfaceFlinger.h" // Get the name of the service...
+
+#include <binder/IServiceManager.h>
+
+#include <cutils/properties.h>
+
+#include <iomanip>
+#include <thread>
+
+using android::String16;
+using android::sp;
+using namespace std::chrono_literals;
+using namespace sftest;
+using std::setw;
+
+namespace sftest {
+
+// clang-format off
+inline void printSourceRectAligned(::std::ostream& os, const hwc_frect_t& sourceRect, int align) {
+    os << std::fixed << std::setprecision(1) << "("
+       << setw(align) << sourceRect.left << setw(0) << ","
+       << setw(align) << sourceRect.top << setw(0) << ","
+       << setw(align) << sourceRect.right << setw(0) << ","
+       << setw(align) << sourceRect.bottom << setw(0) << ")";
+}
+
+inline void printDisplayRectAligned(::std::ostream& os, const hwc_rect_t& displayRect, int align) {
+    os << "("
+       << setw(align) << displayRect.left << setw(0) << ","
+       << setw(align) << displayRect.top << setw(0) << ","
+       << setw(align) << displayRect.right << setw(0) << ","
+       << setw(align) << displayRect.bottom << setw(0) << ")";
+}
+// clang-format on
+
+inline ::std::ostream& operator<<(::std::ostream& os, const sftest::RenderState& state) {
+    printSourceRectAligned(os, state.mSourceCrop, 7);
+    os << "->";
+    printDisplayRectAligned(os, state.mDisplayFrame, 5);
+    return os << " Swaps:" << state.mSwapCount << " Alpha:" << std::setprecision(3)
+              << state.mPlaneAlpha << " Xform:" << state.mTransform;
+}
+
+// Helper for verifying the parts of the RenderState
+template <typename T>
+bool valuesMatch(::testing::AssertionResult& message, const T& ref, const T& val,
+                 const char* name) {
+    if (ref != val) {
+        message = message << "Expected " << name << ":" << ref << ", got:" << val << ".";
+        return false;
+    }
+    return true;
+}
+
+::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val) {
+    // TODO: Message could start as success and be assigned as failure.
+    // Only problem is that utility assumes it to be failure and just adds stuff. Would
+    // need still special case the initial failure in the utility?
+    // TODO: ... or would it be possible to break this back to gtest primitives?
+    ::testing::AssertionResult message = ::testing::AssertionFailure();
+    bool passes = true;
+
+    // The work here is mostly about providing good log strings for differences
+    passes &= valuesMatch(message, ref.mDisplayFrame, val.mDisplayFrame, "display frame");
+    passes &= valuesMatch(message, ref.mPlaneAlpha, val.mPlaneAlpha, "alpha");
+    passes &= valuesMatch(message, ref.mSwapCount, val.mSwapCount, "swap count");
+    passes &= valuesMatch(message, ref.mSourceCrop, val.mSourceCrop, "source crop");
+    // ... add more
+    if (passes) {
+        return ::testing::AssertionSuccess();
+    }
+    return message;
+}
+
+::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref,
+                                         const std::vector<RenderState>& val) {
+    ::testing::AssertionResult message = ::testing::AssertionFailure();
+    bool passed = true;
+    if (ref.size() != val.size()) {
+        message << "Expected " << ref.size() << " rects, got " << val.size() << ".";
+        passed = false;
+    }
+    for (size_t rectIndex = 0; rectIndex < std::min(ref.size(), val.size()); rectIndex++) {
+        ::testing::AssertionResult rectResult = rectsAreSame(ref[rectIndex], val[rectIndex]);
+        if (rectResult == false) {
+            message << "First different rect at " << rectIndex << ": " << rectResult.message();
+            passed = false;
+            break;
+        }
+    }
+
+    if (passed) {
+        return ::testing::AssertionSuccess();
+    } else {
+        message << "\nReference:";
+        for (auto state = ref.begin(); state != ref.end(); ++state) {
+            message << "\n" << *state;
+        }
+        message << "\nActual:";
+        for (auto state = val.begin(); state != val.end(); ++state) {
+            message << "\n" << *state;
+        }
+    }
+    return message;
+}
+
+void startSurfaceFlinger() {
+    ALOGI("Start SurfaceFlinger");
+    system("start surfaceflinger");
+
+    sp<android::IServiceManager> sm(android::defaultServiceManager());
+    sp<android::IBinder> sf;
+    while (sf == nullptr) {
+        std::this_thread::sleep_for(10ms);
+        sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName()));
+    }
+    ALOGV("SurfaceFlinger running");
+}
+
+void stopSurfaceFlinger() {
+    ALOGI("Stop SurfaceFlinger");
+    system("stop surfaceflinger");
+    sp<android::IServiceManager> sm(android::defaultServiceManager());
+    sp<android::IBinder> sf;
+    while (sf != nullptr) {
+        std::this_thread::sleep_for(10ms);
+        sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName()));
+    }
+    ALOGV("SurfaceFlinger stopped");
+}
+
+////////////////////////////////////////////////
+
+void FakeHwcEnvironment::SetUp() {
+    ALOGI("Test env setup");
+    system("setenforce 0");
+    system("stop");
+    property_set("debug.sf.nobootanimation", "1");
+    {
+        char value[PROPERTY_VALUE_MAX];
+        property_get("debug.sf.nobootanimation", value, "0");
+        LOG_FATAL_IF(atoi(value) != 1, "boot skip not set");
+    }
+    // TODO: Try registering the mock as the default service instead.
+    property_set("debug.sf.hwc_service_name", "mock");
+    // This allows the SurfaceFlinger to load a HIDL service not listed in manifest files.
+    property_set("debug.sf.treble_testing_override", "true");
+}
+
+void FakeHwcEnvironment::TearDown() {
+    ALOGI("Test env tear down");
+    system("stop");
+    // Wait for mock call signaling teardown?
+    property_set("debug.sf.nobootanimation", "0");
+    property_set("debug.sf.hwc_service_name", "default");
+    ALOGI("Test env tear down - done");
+}
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
new file mode 100644
index 0000000..74dc0e5
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2017 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 "FakeComposerClient.h"
+
+#include <gui/SurfaceComposerClient.h>
+
+#include <hardware/hwcomposer_defs.h>
+
+#include <log/log.h>
+
+#include <gtest/gtest.h>
+
+// clang-format off
+// Note: This needs to reside in the global namespace for the GTest to use it
+inline ::std::ostream& operator<<(::std::ostream& os, const hwc_rect_t& rect) {
+    return os << "(" << rect.left << ","
+              << rect.top << ","
+              << rect.right << ","
+              << rect.bottom << ")";
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, const hwc_frect_t& rect) {
+    return os << "(" << rect.left << ","
+              << rect.top << ","
+              << rect.right << ","
+              << rect.bottom << ")";
+}
+// clang-format on
+
+namespace sftest {
+
+class RenderState;
+
+// clang-format off
+inline bool operator==(const hwc_rect_t& a, const hwc_rect_t& b) {
+    return a.top == b.top &&
+            a.left == b.left &&
+            a.bottom == b.bottom &&
+            a.right == b.right;
+}
+
+inline bool operator==(const hwc_frect_t& a, const hwc_frect_t& b) {
+    return a.top == b.top &&
+            a.left == b.left &&
+            a.bottom == b.bottom &&
+            a.right == b.right;
+}
+// clang-format on
+
+inline bool operator!=(const hwc_rect_t& a, const hwc_rect_t& b) {
+    return !(a == b);
+}
+
+inline bool operator!=(const hwc_frect_t& a, const hwc_frect_t& b) {
+    return !(a == b);
+}
+
+::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val);
+::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref,
+                                         const std::vector<RenderState>& val);
+
+void startSurfaceFlinger();
+void stopSurfaceFlinger();
+
+class FakeHwcEnvironment : public ::testing::Environment {
+public:
+    virtual ~FakeHwcEnvironment() {}
+    void SetUp() override;
+    void TearDown() override;
+};
+
+/*
+ * All surface state changes are supposed to happen inside a global
+ * transaction. GlobalTransactionScope object at the beginning of
+ * scope automates the process. The resulting scope gives a visual cue
+ * on the span of the transaction as well.
+ *
+ * Closing the transaction is synchronous, i.e., it waits for
+ * SurfaceFlinger to composite one frame. Now, the FakeComposerClient
+ * is built to explicitly request vsyncs one at the time. A delayed
+ * request must be made before closing the transaction or the test
+ * thread stalls until SurfaceFlinger does an emergency vsync by
+ * itself. GlobalTransactionScope encapsulates this vsync magic.
+ */
+class GlobalTransactionScope {
+public:
+    GlobalTransactionScope(FakeComposerClient& composer) : mComposer(composer) {
+        android::SurfaceComposerClient::openGlobalTransaction();
+    }
+    ~GlobalTransactionScope() {
+        int frameCount = mComposer.getFrameCount();
+        mComposer.runVSyncAfter(1ms);
+        android::SurfaceComposerClient::closeGlobalTransaction(true);
+        // Make sure that exactly one frame has been rendered.
+        mComposer.waitUntilFrame(frameCount + 1);
+        LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(),
+                            "Unexpected frame advance. Delta: %d",
+                            mComposer.getFrameCount() - frameCount);
+    }
+    FakeComposerClient& mComposer;
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/RenderState.h b/services/surfaceflinger/tests/fakehwc/RenderState.h
new file mode 100644
index 0000000..0059289
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/RenderState.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 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 <hardware/hwcomposer2.h>
+
+#include <vector>
+
+namespace sftest {
+// Description of a rendered rectangle.  Should only contain
+// instructions necessary to rasterize the rectangle. The full scene
+// is given as a sorted list of rectangles, bottom layer at index 0.
+class RenderState {
+public:
+    RenderState() = default;
+    // Default copy-ctor
+
+    hwc_rect_t mDisplayFrame = {0, 0, 0, 0};
+    hwc_frect_t mSourceCrop = {0.f, 0.f, 0.f, 0.f};
+    std::vector<hwc_rect_t> mVisibleRegion;
+    hwc2_blend_mode_t mBlendMode = HWC2_BLEND_MODE_NONE;
+    buffer_handle_t mBuffer = 0;
+    uint32_t mSwapCount = 0;   // How many set buffer calls to the layer.
+    int32_t mAcquireFence = 0; // Probably should not be here.
+    float mPlaneAlpha = 0.f;
+    hwc_color_t mLayerColor = {0, 0, 0, 0};
+    hwc_transform_t mTransform = static_cast<hwc_transform_t>(0);
+};
+
+} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
new file mode 100644
index 0000000..8902ede
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -0,0 +1,1306 @@
+/*
+ * Copyright (C) 2017 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 "FakeHwcTest"
+
+#include "FakeComposerClient.h"
+#include "FakeComposerService.h"
+#include "FakeComposerUtils.h"
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/LayerDebugInfo.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <private/gui/ComposerService.h>
+#include <private/gui/LayerState.h>
+
+#include <ui/DisplayInfo.h>
+
+#include <android/native_window.h>
+
+#include <android/hidl/manager/1.0/IServiceManager.h>
+
+#include <hwbinder/ProcessState.h>
+
+#include <binder/ProcessState.h>
+
+#include <log/log.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <limits>
+
+using namespace std::chrono_literals;
+
+using namespace android;
+using namespace android::hardware;
+
+using namespace sftest;
+
+namespace {
+
+// Mock test helpers
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::_;
+
+///////////////////////////////////////////////
+
+struct TestColor {
+public:
+    uint8_t r;
+    uint8_t g;
+    uint8_t b;
+    uint8_t a;
+};
+
+constexpr static TestColor RED = {195, 63, 63, 255};
+constexpr static TestColor LIGHT_RED = {255, 177, 177, 255};
+constexpr static TestColor GREEN = {63, 195, 63, 255};
+constexpr static TestColor BLUE = {63, 63, 195, 255};
+constexpr static TestColor DARK_GRAY = {63, 63, 63, 255};
+constexpr static TestColor LIGHT_GRAY = {200, 200, 200, 255};
+
+// Fill an RGBA_8888 formatted surface with a single color.
+static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const TestColor& color,
+                             bool unlock = true) {
+    ANativeWindow_Buffer outBuffer;
+    sp<Surface> s = sc->getSurface();
+    ASSERT_TRUE(s != nullptr);
+    ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
+    uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
+    for (int y = 0; y < outBuffer.height; y++) {
+        for (int x = 0; x < outBuffer.width; x++) {
+            uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
+            pixel[0] = color.r;
+            pixel[1] = color.g;
+            pixel[2] = color.b;
+            pixel[3] = color.a;
+        }
+    }
+    if (unlock) {
+        ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+    }
+}
+
+inline RenderState makeSimpleRect(int left, int top, int right, int bottom) {
+    RenderState res;
+    res.mDisplayFrame = hwc_rect_t{left, top, right, bottom};
+    res.mPlaneAlpha = 1.0f;
+    res.mSwapCount = 0;
+    res.mSourceCrop = hwc_frect_t{0.f, 0.f, static_cast<float>(right - left),
+                                  static_cast<float>(bottom - top)};
+    return res;
+}
+
+inline RenderState makeSimpleRect(unsigned int left, unsigned int top, unsigned int right,
+                                  unsigned int bottom) {
+    EXPECT_LE(left, static_cast<unsigned int>(INT_MAX));
+    EXPECT_LE(top, static_cast<unsigned int>(INT_MAX));
+    EXPECT_LE(right, static_cast<unsigned int>(INT_MAX));
+    EXPECT_LE(bottom, static_cast<unsigned int>(INT_MAX));
+    return makeSimpleRect(static_cast<int>(left), static_cast<int>(top), static_cast<int>(right),
+                          static_cast<int>(bottom));
+}
+
+////////////////////////////////////////////////
+
+class DisplayTest : public ::testing::Test {
+public:
+    class MockComposerClient : public FakeComposerClient {
+    public:
+        MOCK_METHOD2(getDisplayType, Error(Display display, ComposerClient::DisplayType* outType));
+        MOCK_METHOD4(getDisplayAttribute,
+                     Error(Display display, Config config, IComposerClient::Attribute attribute,
+                           int32_t* outValue));
+
+        // Re-routing to basic fake implementation
+        Error getDisplayAttributeFake(Display display, Config config,
+                                      IComposerClient::Attribute attribute, int32_t* outValue) {
+            return FakeComposerClient::getDisplayAttribute(display, config, attribute, outValue);
+        }
+    };
+
+protected:
+    void SetUp() override;
+    void TearDown() override;
+
+    sp<IComposer> mFakeService;
+    sp<SurfaceComposerClient> mComposerClient;
+
+    MockComposerClient* mMockComposer;
+};
+
+void DisplayTest::SetUp() {
+    // TODO: The mMockComposer should be a unique_ptr, but it needs to
+    // outlive the test class.  Currently ComposerClient only dies
+    // when the service is replaced. The Mock deletes itself when
+    // removeClient is called on it, which is ugly.  This can be
+    // changed if HIDL ServiceManager allows removing services or
+    // ComposerClient starts taking the ownership of the contained
+    // implementation class. Moving the fake class to the HWC2
+    // interface instead of the current Composer interface might also
+    // change the situation.
+    mMockComposer = new MockComposerClient;
+    sp<ComposerClient> client = new ComposerClient(*mMockComposer);
+    mMockComposer->setClient(client.get());
+    mFakeService = new FakeComposerService(client);
+    mFakeService->registerAsService("mock");
+
+    android::hardware::ProcessState::self()->startThreadPool();
+    android::ProcessState::self()->startThreadPool();
+
+    EXPECT_CALL(*mMockComposer, getDisplayType(1, _))
+            .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
+                            Return(Error::NONE)));
+    // Seems to be doubled right now, once for display ID 1 and once for 0. This sounds fishy
+    // but encoding that here exactly.
+    EXPECT_CALL(*mMockComposer, getDisplayAttribute(1, 1, _, _))
+            .Times(5)
+            .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
+    // TODO: Find out what code is generating the ID 0.
+    EXPECT_CALL(*mMockComposer, getDisplayAttribute(0, 1, _, _))
+            .Times(5)
+            .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
+
+    startSurfaceFlinger();
+
+    // Fake composer wants to enable VSync injection
+    mMockComposer->onSurfaceFlingerStart();
+
+    mComposerClient = new SurfaceComposerClient;
+    ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+}
+
+void DisplayTest::TearDown() {
+    mComposerClient->dispose();
+    mComposerClient = nullptr;
+
+    // Fake composer needs to release SurfaceComposerClient before the stop.
+    mMockComposer->onSurfaceFlingerStop();
+    stopSurfaceFlinger();
+
+    mFakeService = nullptr;
+    // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime
+    // management.
+    mMockComposer = nullptr;
+}
+
+TEST_F(DisplayTest, Hotplug) {
+    ALOGD("DisplayTest::Hotplug");
+
+    EXPECT_CALL(*mMockComposer, getDisplayType(2, _))
+            .Times(2)
+            .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
+                                  Return(Error::NONE)));
+    // The attribute queries will get done twice. This is for defaults
+    EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, _, _))
+            .Times(2 * 3)
+            .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
+    // ... and then special handling for dimensions. Specifying this
+    // rules later means that gmock will try them first, i.e.,
+    // ordering of width/height vs. the default implementation for
+    // other queries is significant.
+    EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::WIDTH, _))
+            .Times(2)
+            .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
+
+    EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::HEIGHT, _))
+            .Times(2)
+            .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
+
+    // TODO: Width and height queries are not actually called. Display
+    // info returns dimensions 0x0 in display info. Why?
+
+    mMockComposer->hotplugDisplay(static_cast<Display>(2),
+                                  IComposerCallback::Connection::CONNECTED);
+
+    {
+        sp<android::IBinder> display(
+                SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
+        DisplayInfo info;
+        SurfaceComposerClient::getDisplayInfo(display, &info);
+        ASSERT_EQ(400u, info.w);
+        ASSERT_EQ(200u, info.h);
+
+        auto surfaceControl =
+                mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w, info.h,
+                                               PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(surfaceControl != nullptr);
+        ASSERT_TRUE(surfaceControl->isValid());
+        fillSurfaceRGBA8(surfaceControl, BLUE);
+
+        {
+            GlobalTransactionScope gts(*mMockComposer);
+            mComposerClient->setDisplayLayerStack(display, 0);
+
+            ASSERT_EQ(NO_ERROR, surfaceControl->setLayer(INT32_MAX - 2));
+            ASSERT_EQ(NO_ERROR, surfaceControl->show());
+        }
+    }
+
+    mMockComposer->hotplugDisplay(static_cast<Display>(2),
+                                  IComposerCallback::Connection::DISCONNECTED);
+
+    mMockComposer->clearFrames();
+
+    mMockComposer->hotplugDisplay(static_cast<Display>(2),
+                                  IComposerCallback::Connection::CONNECTED);
+
+    {
+        sp<android::IBinder> display(
+                SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
+        DisplayInfo info;
+        SurfaceComposerClient::getDisplayInfo(display, &info);
+        ASSERT_EQ(400u, info.w);
+        ASSERT_EQ(200u, info.h);
+
+        auto surfaceControl =
+                mComposerClient->createSurface(String8("Display Test Surface Bar"), info.w, info.h,
+                                               PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(surfaceControl != nullptr);
+        ASSERT_TRUE(surfaceControl->isValid());
+        fillSurfaceRGBA8(surfaceControl, BLUE);
+
+        {
+            GlobalTransactionScope gts(*mMockComposer);
+            mComposerClient->setDisplayLayerStack(display, 0);
+
+            ASSERT_EQ(NO_ERROR, surfaceControl->setLayer(INT32_MAX - 2));
+            ASSERT_EQ(NO_ERROR, surfaceControl->show());
+        }
+    }
+    mMockComposer->hotplugDisplay(static_cast<Display>(2),
+                                  IComposerCallback::Connection::DISCONNECTED);
+}
+
+////////////////////////////////////////////////
+
+class TransactionTest : public ::testing::Test {
+protected:
+    // Layer array indexing constants.
+    constexpr static int BG_LAYER = 0;
+    constexpr static int FG_LAYER = 1;
+
+    static void SetUpTestCase();
+    static void TearDownTestCase();
+
+    void SetUp() override;
+    void TearDown() override;
+
+    sp<SurfaceComposerClient> mComposerClient;
+    sp<SurfaceControl> mBGSurfaceControl;
+    sp<SurfaceControl> mFGSurfaceControl;
+    std::vector<RenderState> mBaseFrame;
+    uint32_t mDisplayWidth;
+    uint32_t mDisplayHeight;
+
+    static FakeComposerClient* sFakeComposer;
+};
+
+FakeComposerClient* TransactionTest::sFakeComposer;
+
+void TransactionTest::SetUpTestCase() {
+    // TODO: See TODO comment at DisplayTest::SetUp for background on
+    // the lifetime of the FakeComposerClient.
+    sFakeComposer = new FakeComposerClient;
+    sp<ComposerClient> client = new ComposerClient(*sFakeComposer);
+    sFakeComposer->setClient(client.get());
+    sp<IComposer> fakeService = new FakeComposerService(client);
+    fakeService->registerAsService("mock");
+
+    android::hardware::ProcessState::self()->startThreadPool();
+    android::ProcessState::self()->startThreadPool();
+
+    startSurfaceFlinger();
+
+    // Fake composer wants to enable VSync injection
+    sFakeComposer->onSurfaceFlingerStart();
+}
+
+void TransactionTest::TearDownTestCase() {
+    // Fake composer needs to release SurfaceComposerClient before the stop.
+    sFakeComposer->onSurfaceFlingerStop();
+    stopSurfaceFlinger();
+    // TODO: This is deleted when the ComposerClient calls
+    // removeClient. Devise better lifetime control.
+    sFakeComposer = nullptr;
+}
+
+void TransactionTest::SetUp() {
+    ALOGI("TransactionTest::SetUp");
+    mComposerClient = new SurfaceComposerClient;
+    ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+    ALOGI("TransactionTest::SetUp - display");
+    sp<android::IBinder> display(
+            SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+    DisplayInfo info;
+    SurfaceComposerClient::getDisplayInfo(display, &info);
+
+    mDisplayWidth = info.w;
+    mDisplayHeight = info.h;
+
+    // Background surface
+    mBGSurfaceControl = mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth,
+                                                       mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0);
+    ASSERT_TRUE(mBGSurfaceControl != nullptr);
+    ASSERT_TRUE(mBGSurfaceControl->isValid());
+    fillSurfaceRGBA8(mBGSurfaceControl, BLUE);
+
+    // Foreground surface
+    mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
+                                                       PIXEL_FORMAT_RGBA_8888, 0);
+    ASSERT_TRUE(mFGSurfaceControl != nullptr);
+    ASSERT_TRUE(mFGSurfaceControl->isValid());
+
+    fillSurfaceRGBA8(mFGSurfaceControl, RED);
+
+    SurfaceComposerClient::openGlobalTransaction();
+
+    mComposerClient->setDisplayLayerStack(display, 0);
+
+    ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT32_MAX - 2));
+    ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show());
+
+    ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT32_MAX - 1));
+    ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(64, 64));
+    ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show());
+
+    // Synchronous transaction will stop this thread, so we set up a
+    // delayed, off-thread vsync request before closing the
+    // transaction. In the test code this is usually done with
+    // GlobalTransactionScope. Leaving here in the 'vanilla' form for
+    // reference.
+    ASSERT_EQ(0, sFakeComposer->getFrameCount());
+    sFakeComposer->runVSyncAfter(1ms);
+    SurfaceComposerClient::closeGlobalTransaction(true);
+    sFakeComposer->waitUntilFrame(1);
+
+    // Reference data. This is what the HWC should see.
+    static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing");
+    mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight));
+    mBaseFrame[BG_LAYER].mSwapCount = 1;
+    mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
+    mBaseFrame[FG_LAYER].mSwapCount = 1;
+
+    auto frame = sFakeComposer->getFrameRects(0);
+    ASSERT_TRUE(framesAreSame(mBaseFrame, frame));
+}
+
+void TransactionTest::TearDown() {
+    ALOGD("TransactionTest::TearDown");
+
+    mComposerClient->dispose();
+    mBGSurfaceControl = 0;
+    mFGSurfaceControl = 0;
+    mComposerClient = 0;
+
+    sFakeComposer->runVSyncAndWait();
+    mBaseFrame.clear();
+    sFakeComposer->clearFrames();
+    ASSERT_EQ(0, sFakeComposer->getFrameCount());
+
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    std::vector<LayerDebugInfo> layers;
+    status_t result = sf->getLayerDebugInfo(&layers);
+    if (result != NO_ERROR) {
+        ALOGE("Failed to get layers %s %d", strerror(-result), result);
+    } else {
+        // If this fails, the test being torn down leaked layers.
+        EXPECT_EQ(0u, layers.size());
+        if (layers.size() > 0) {
+            for (auto layer = layers.begin(); layer != layers.end(); ++layer) {
+                std::cout << to_string(*layer).c_str();
+            }
+            // To ensure the next test has clean slate, will run the class
+            // tear down and setup here.
+            TearDownTestCase();
+            SetUpTestCase();
+        }
+    }
+    ALOGD("TransactionTest::TearDown - complete");
+}
+
+TEST_F(TransactionTest, LayerMove) {
+    ALOGD("TransactionTest::LayerMove");
+
+    // The scope opens and closes a global transaction and, at the
+    // same time, makes sure the SurfaceFlinger progresses one frame
+    // after the transaction closes. The results of the transaction
+    // should be available in the latest frame stored by the fake
+    // composer.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128));
+        // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls.
+        // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?)
+        //
+        // sFakeComposer->runVSyncAndWait();
+    }
+
+    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+    sFakeComposer->runVSyncAndWait();
+
+    ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
+                                                  // no extra frames.
+
+    // NOTE: Frame 0 is produced in the SetUp.
+    auto frame1Ref = mBaseFrame;
+    frame1Ref[FG_LAYER].mDisplayFrame =
+            hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves.
+    EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
+
+    auto frame2Ref = frame1Ref;
+    frame2Ref[FG_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+}
+
+TEST_F(TransactionTest, LayerResize) {
+    ALOGD("TransactionTest::LayerResize");
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setSize(128, 128));
+    }
+
+    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+    sFakeComposer->runVSyncAndWait();
+
+    ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
+                                                  // no extra frames.
+
+    auto frame1Ref = mBaseFrame;
+    // NOTE: The resize should not be visible for frame 1 as there's no buffer with new size posted.
+    EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
+
+    auto frame2Ref = frame1Ref;
+    frame2Ref[FG_LAYER].mSwapCount++;
+    frame2Ref[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 128, 64 + 128};
+    frame2Ref[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 128.f, 128.f};
+    EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+}
+
+TEST_F(TransactionTest, LayerCrop) {
+    // TODO: Add scaling to confirm that crop happens in buffer space?
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        Rect cropRect(16, 16, 32, 32);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setCrop(cropRect));
+    }
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f};
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerFinalCrop) {
+    // TODO: Add scaling to confirm that crop happens in display space?
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        Rect cropRect(32, 32, 32 + 64, 32 + 64);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setFinalCrop(cropRect));
+    }
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+    // In display space we are cropping with [32, 32, 96, 96] against display rect
+    // [64, 64, 128, 128]. Should yield display rect [64, 64, 96, 96]
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 32, 64 + 32};
+
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerFinalCropEmpty) {
+    // TODO: Add scaling to confirm that crop happens in display space?
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        Rect cropRect(16, 16, 32, 32);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setFinalCrop(cropRect));
+    }
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+    // In display space we are cropping with [16, 16, 32, 32] against display rect
+    // [64, 64, 128, 128]. The intersection is empty and only the background layer is composited.
+    std::vector<RenderState> referenceFrame(1);
+    referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetLayer) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX - 3));
+    }
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+    // The layers will switch order, but both are rendered because the background layer is
+    // transparent (RGBA8888).
+    std::vector<RenderState> referenceFrame(2);
+    referenceFrame[0] = mBaseFrame[FG_LAYER];
+    referenceFrame[1] = mBaseFrame[BG_LAYER];
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetLayerOpaque) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX - 3));
+        ASSERT_EQ(NO_ERROR,
+                  mBGSurfaceControl->setFlags(layer_state_t::eLayerOpaque,
+                                              layer_state_t::eLayerOpaque));
+    }
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+    // The former foreground layer is now covered with opaque layer - it should have disappeared
+    std::vector<RenderState> referenceFrame(1);
+    referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, SetLayerStack) {
+    ALOGD("TransactionTest::SetLayerStack");
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayerStack(1));
+    }
+
+    // Foreground layer should have disappeared.
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+    std::vector<RenderState> refFrame(1);
+    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerShowHide) {
+    ALOGD("TransactionTest::LayerShowHide");
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->hide());
+    }
+
+    // Foreground layer should have disappeared.
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+    std::vector<RenderState> refFrame(1);
+    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show());
+    }
+
+    // Foreground layer should be back
+    ASSERT_EQ(3, sFakeComposer->getFrameCount());
+    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetAlpha) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75f));
+    }
+
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetFlags) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR,
+                  mFGSurfaceControl->setFlags(layer_state_t::eLayerHidden,
+                                              layer_state_t::eLayerHidden));
+    }
+
+    // Foreground layer should have disappeared.
+    ASSERT_EQ(2, sFakeComposer->getFrameCount());
+    std::vector<RenderState> refFrame(1);
+    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, LayerSetMatrix) {
+    struct matrixTestData {
+        float matrix[4];
+        hwc_transform_t expectedTransform;
+        hwc_rect_t expectedDisplayFrame;
+    };
+
+    // The matrix operates on the display frame and is applied before
+    // the position is added. So, the foreground layer rect is (0, 0,
+    // 64, 64) is first transformed, potentially yielding negative
+    // coordinates and then the position (64, 64) is added yielding
+    // the final on-screen rectangles given.
+
+    const matrixTestData MATRIX_TESTS[7] = // clang-format off
+            {{{-1.f, 0.f, 0.f, 1.f},    HWC_TRANSFORM_FLIP_H,           {0, 64, 64, 128}},
+             {{1.f, 0.f, 0.f, -1.f},    HWC_TRANSFORM_FLIP_V,           {64, 0, 128, 64}},
+             {{0.f, 1.f, -1.f, 0.f},    HWC_TRANSFORM_ROT_90,           {0, 64, 64, 128}},
+             {{-1.f, 0.f, 0.f, -1.f},   HWC_TRANSFORM_ROT_180,          {0, 0, 64, 64}},
+             {{0.f, -1.f, 1.f, 0.f},    HWC_TRANSFORM_ROT_270,          {64, 0, 128, 64}},
+             {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_H_ROT_90,    {64, 64, 128, 128}},
+             {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_V_ROT_90,    {64, 64, 128, 128}}};
+    // clang-format on
+    constexpr int TEST_COUNT = sizeof(MATRIX_TESTS)/sizeof(matrixTestData);
+
+    for (int i = 0; i < TEST_COUNT; i++) {
+        // TODO: How to leverage the HWC2 stringifiers?
+        const matrixTestData& xform = MATRIX_TESTS[i];
+        SCOPED_TRACE(i);
+        {
+            GlobalTransactionScope gts(*sFakeComposer);
+            ASSERT_EQ(NO_ERROR,
+                      mFGSurfaceControl->setMatrix(xform.matrix[0], xform.matrix[1],
+                                                   xform.matrix[2], xform.matrix[3]));
+        }
+
+        auto referenceFrame = mBaseFrame;
+        referenceFrame[FG_LAYER].mTransform = xform.expectedTransform;
+        referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame;
+
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+    }
+}
+
+#if 0
+TEST_F(TransactionTest, LayerSetMatrix2) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        // TODO: PLEASE SPEC THE FUNCTION!
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setMatrix(0.11f, 0.123f,
+                                                         -2.33f, 0.22f));
+    }
+    auto referenceFrame = mBaseFrame;
+    // TODO: Is this correct for sure?
+    //referenceFrame[FG_LAYER].mTransform = HWC_TRANSFORM_FLIP_V & HWC_TRANSFORM_ROT_90;
+
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+#endif
+
+TEST_F(TransactionTest, DeferredTransaction) {
+    // Synchronization surface
+    constexpr static int SYNC_LAYER = 2;
+    auto syncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1,
+                                                             PIXEL_FORMAT_RGBA_8888, 0);
+    ASSERT_TRUE(syncSurfaceControl != nullptr);
+    ASSERT_TRUE(syncSurfaceControl->isValid());
+
+    fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, syncSurfaceControl->setLayer(INT32_MAX - 1));
+        ASSERT_EQ(NO_ERROR, syncSurfaceControl->setPosition(mDisplayWidth - 2, mDisplayHeight - 2));
+        ASSERT_EQ(NO_ERROR, syncSurfaceControl->show());
+    }
+    auto referenceFrame = mBaseFrame;
+    referenceFrame.push_back(makeSimpleRect(mDisplayWidth - 2, mDisplayHeight - 2,
+                                            mDisplayWidth - 1, mDisplayHeight - 1));
+    referenceFrame[SYNC_LAYER].mSwapCount = 1;
+    EXPECT_EQ(2, sFakeComposer->getFrameCount());
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    // set up two deferred transactions on different frames - these should not yield composited
+    // frames
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75));
+        mFGSurfaceControl
+                ->deferTransactionUntil(syncSurfaceControl->getHandle(),
+                                        syncSurfaceControl->getSurface()->getNextFrameNumber());
+    }
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128));
+        mFGSurfaceControl
+                ->deferTransactionUntil(syncSurfaceControl->getHandle(),
+                                        syncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
+    }
+    EXPECT_EQ(4, sFakeComposer->getFrameCount());
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    // should trigger the first deferred transaction, but not the second one
+    fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+    sFakeComposer->runVSyncAndWait();
+    EXPECT_EQ(5, sFakeComposer->getFrameCount());
+
+    referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
+    referenceFrame[SYNC_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    // should show up immediately since it's not deferred
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(1.0));
+    }
+    referenceFrame[FG_LAYER].mPlaneAlpha = 1.f;
+    EXPECT_EQ(6, sFakeComposer->getFrameCount());
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    // trigger the second deferred transaction
+    fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+    sFakeComposer->runVSyncAndWait();
+    // TODO: Compute from layer size?
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{128, 128, 128 + 64, 128 + 64};
+    referenceFrame[SYNC_LAYER].mSwapCount++;
+    EXPECT_EQ(7, sFakeComposer->getFrameCount());
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(TransactionTest, SetRelativeLayer) {
+    constexpr int RELATIVE_LAYER = 2;
+    auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64, 64,
+                                                                 PIXEL_FORMAT_RGBA_8888, 0);
+    fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED);
+
+    // Now we stack the surface above the foreground surface and make sure it is visible.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        relativeSurfaceControl->setPosition(64, 64);
+        relativeSurfaceControl->show();
+        relativeSurfaceControl->setRelativeLayer(mFGSurfaceControl->getHandle(), 1);
+    }
+    auto referenceFrame = mBaseFrame;
+    // NOTE: All three layers will be visible as the surfaces are
+    // transparent because of the RGBA format.
+    referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
+    referenceFrame[RELATIVE_LAYER].mSwapCount = 1;
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    // A call to setLayer will override a call to setRelativeLayer
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        relativeSurfaceControl->setLayer(0);
+    }
+
+    // Previous top layer will now appear at the bottom.
+    auto referenceFrame2 = mBaseFrame;
+    referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]);
+    EXPECT_EQ(3, sFakeComposer->getFrameCount());
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+class ChildLayerTest : public TransactionTest {
+protected:
+    constexpr static int CHILD_LAYER = 2;
+
+    void SetUp() override {
+        TransactionTest::SetUp();
+        mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
+                                                PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+        fillSurfaceRGBA8(mChild, LIGHT_GRAY);
+
+        sFakeComposer->runVSyncAndWait();
+        mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
+        mBaseFrame[CHILD_LAYER].mSwapCount = 1;
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+        ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+    }
+    void TearDown() override {
+        mChild = 0;
+        TransactionTest::TearDown();
+    }
+
+    sp<SurfaceControl> mChild;
+};
+
+TEST_F(ChildLayerTest, Positioning) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(10, 10);
+        // Move to the same position as in the original setup.
+        mFGSurfaceControl->setPosition(64, 64);
+    }
+
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+    referenceFrame[CHILD_LAYER].mDisplayFrame =
+            hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(0, 0));
+    }
+
+    auto referenceFrame2 = mBaseFrame;
+    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64};
+    referenceFrame2[CHILD_LAYER].mDisplayFrame =
+            hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10};
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Cropping) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(0, 0);
+        mFGSurfaceControl->setPosition(0, 0);
+        mFGSurfaceControl->setCrop(Rect(0, 0, 5, 5));
+    }
+    // NOTE: The foreground surface would be occluded by the child
+    // now, but is included in the stack because the child is
+    // transparent.
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+    referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, FinalCropping) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(0, 0);
+        mFGSurfaceControl->setPosition(0, 0);
+        mFGSurfaceControl->setFinalCrop(Rect(0, 0, 5, 5));
+    }
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+    referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Constraints) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mFGSurfaceControl->setPosition(0, 0);
+        mChild->setPosition(63, 63);
+    }
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64};
+    referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Scaling) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setPosition(0, 0);
+    }
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setMatrix(2.0, 0, 0, 2.0);
+    }
+
+    auto referenceFrame2 = mBaseFrame;
+    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
+    referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, LayerAlpha) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(0, 0);
+        mFGSurfaceControl->setPosition(0, 0);
+        ASSERT_EQ(NO_ERROR, mChild->setAlpha(0.5));
+    }
+
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+    referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.5));
+    }
+
+    auto referenceFrame2 = referenceFrame;
+    referenceFrame2[FG_LAYER].mPlaneAlpha = 0.5f;
+    referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, ReparentChildren) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(10, 10);
+        mFGSurfaceControl->setPosition(64, 64);
+    }
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+    referenceFrame[CHILD_LAYER].mDisplayFrame =
+            hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->reparentChildren(mBGSurfaceControl->getHandle());
+    }
+
+    auto referenceFrame2 = referenceFrame;
+    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+    referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10};
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, DetachChildren) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(10, 10);
+        mFGSurfaceControl->setPosition(64, 64);
+    }
+
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+    referenceFrame[CHILD_LAYER].mDisplayFrame =
+            hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->detachChildren();
+    }
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->hide();
+    }
+
+    // Nothing should have changed. The child control becomes a no-op
+    // zombie on detach. See comments for detachChildren in the
+    // SurfaceControl.h file.
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, InheritNonTransformScalingFromParent) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(0, 0);
+        mFGSurfaceControl->setPosition(0, 0);
+    }
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setOverrideScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        // We cause scaling by 2.
+        mFGSurfaceControl->setSize(128, 128);
+    }
+
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
+    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f};
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
+    referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+// Regression test for b/37673612
+TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->show();
+        mChild->setPosition(0, 0);
+        mFGSurfaceControl->setPosition(0, 0);
+    }
+
+    // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
+    // the WM specified state size.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 64);
+    }
+
+    sp<Surface> s = mFGSurfaceControl->getSurface();
+    auto anw = static_cast<ANativeWindow*>(s.get());
+    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+    native_window_set_buffers_dimensions(anw, 64, 128);
+    fillSurfaceRGBA8(mFGSurfaceControl, RED);
+    sFakeComposer->runVSyncAndWait();
+
+    // The child should still be in the same place and not have any strange scaling as in
+    // b/37673612.
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 64};
+    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 128.f};
+    referenceFrame[FG_LAYER].mSwapCount++;
+    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(ChildLayerTest, Bug36858924) {
+    // Destroy the child layer
+    mChild.clear();
+
+    // Now recreate it as hidden
+    mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
+                                            PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden,
+                                            mFGSurfaceControl.get());
+
+    // Show the child layer in a deferred transaction
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mChild->deferTransactionUntil(mFGSurfaceControl->getHandle(),
+                                      mFGSurfaceControl->getSurface()->getNextFrameNumber());
+        mChild->show();
+    }
+
+    // Render the foreground surface a few times
+    //
+    // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
+    // frame because SurfaceFlinger would never process the deferred transaction and would therefore
+    // never acquire/release the first buffer
+    ALOGI("Filling 1");
+    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+    sFakeComposer->runVSyncAndWait();
+    ALOGI("Filling 2");
+    fillSurfaceRGBA8(mFGSurfaceControl, BLUE);
+    sFakeComposer->runVSyncAndWait();
+    ALOGI("Filling 3");
+    fillSurfaceRGBA8(mFGSurfaceControl, RED);
+    sFakeComposer->runVSyncAndWait();
+    ALOGI("Filling 4");
+    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+    sFakeComposer->runVSyncAndWait();
+}
+
+class LatchingTest : public TransactionTest {
+protected:
+    void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, RED, false); }
+
+    void unlockFGBuffer() {
+        sp<Surface> s = mFGSurfaceControl->getSurface();
+        ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+        sFakeComposer->runVSyncAndWait();
+    }
+
+    void completeFGResize() {
+        fillSurfaceRGBA8(mFGSurfaceControl, RED);
+        sFakeComposer->runVSyncAndWait();
+    }
+    void restoreInitialState() {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(64, 64);
+        mFGSurfaceControl->setPosition(64, 64);
+        mFGSurfaceControl->setCrop(Rect(0, 0, 64, 64));
+        mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1));
+    }
+};
+
+TEST_F(LatchingTest, SurfacePositionLatching) {
+    // By default position can be updated even while
+    // a resize is pending.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(32, 32);
+        mFGSurfaceControl->setPosition(100, 100);
+    }
+
+    // The size should not have updated as we have not provided a new buffer.
+    auto referenceFrame1 = mBaseFrame;
+    referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 64, 100 + 64};
+    EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+    restoreInitialState();
+
+    // Now we repeat with setGeometryAppliesWithResize
+    // and verify the position DOESN'T latch.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setGeometryAppliesWithResize();
+        mFGSurfaceControl->setSize(32, 32);
+        mFGSurfaceControl->setPosition(100, 100);
+    }
+    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+    completeFGResize();
+
+    auto referenceFrame2 = mBaseFrame;
+    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 32, 100 + 32};
+    referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
+    referenceFrame2[FG_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(LatchingTest, CropLatching) {
+    // Normally the crop applies immediately even while a resize is pending.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 128);
+        mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63));
+    }
+
+    auto referenceFrame1 = mBaseFrame;
+    referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
+    referenceFrame1[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
+    EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+    restoreInitialState();
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 128);
+        mFGSurfaceControl->setGeometryAppliesWithResize();
+        mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63));
+    }
+    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+    completeFGResize();
+
+    auto referenceFrame2 = mBaseFrame;
+    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
+    referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
+    referenceFrame2[FG_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(LatchingTest, FinalCropLatching) {
+    // Normally the crop applies immediately even while a resize is pending.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 128);
+        mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+    }
+
+    auto referenceFrame1 = mBaseFrame;
+    referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+    referenceFrame1[FG_LAYER].mSourceCrop =
+            hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+    EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+    restoreInitialState();
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 128);
+        mFGSurfaceControl->setGeometryAppliesWithResize();
+        mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+    }
+    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+    completeFGResize();
+
+    auto referenceFrame2 = mBaseFrame;
+    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+    referenceFrame2[FG_LAYER].mSourceCrop =
+            hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+    referenceFrame2[FG_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+}
+
+// In this test we ensure that setGeometryAppliesWithResize actually demands
+// a buffer of the new size, and not just any size.
+TEST_F(LatchingTest, FinalCropLatchingBufferOldSize) {
+    // Normally the crop applies immediately even while a resize is pending.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 128);
+        mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+    }
+
+    auto referenceFrame1 = mBaseFrame;
+    referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+    referenceFrame1[FG_LAYER].mSourceCrop =
+            hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+    EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
+
+    restoreInitialState();
+
+    // In order to prepare to submit a buffer at the wrong size, we acquire it prior to
+    // initiating the resize.
+    lockAndFillFGBuffer();
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 128);
+        mFGSurfaceControl->setGeometryAppliesWithResize();
+        mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+    }
+    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+    // We now submit our old buffer, at the old size, and ensure it doesn't
+    // trigger geometry latching.
+    unlockFGBuffer();
+
+    auto referenceFrame2 = mBaseFrame;
+    referenceFrame2[FG_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+
+    completeFGResize();
+    auto referenceFrame3 = referenceFrame2;
+    referenceFrame3[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127};
+    referenceFrame3[FG_LAYER].mSourceCrop =
+            hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)};
+    referenceFrame3[FG_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(referenceFrame3, sFakeComposer->getLatestFrame()));
+}
+
+TEST_F(LatchingTest, FinalCropLatchingRegressionForb37531386) {
+    // In this scenario, we attempt to set the final crop a second time while the resize
+    // is still pending, and ensure we are successful. Success meaning the second crop
+    // is the one which eventually latches and not the first.
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setSize(128, 128);
+        mFGSurfaceControl->setGeometryAppliesWithResize();
+        mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127));
+    }
+
+    {
+        GlobalTransactionScope gts(*sFakeComposer);
+        mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1));
+    }
+    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+
+    completeFGResize();
+
+    auto referenceFrame = mBaseFrame;
+    referenceFrame[FG_LAYER].mSwapCount++;
+    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    sftest::FakeHwcEnvironment* fakeEnvironment = new sftest::FakeHwcEnvironment;
+    ::testing::AddGlobalTestEnvironment(fakeEnvironment);
+    ::testing::InitGoogleMock(&argc, argv);
+    return RUN_ALL_TESTS();
+}