Merge "Add log for when a display mode change finishes." into pi-dev
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 0701450..21d9ace 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -116,7 +116,8 @@
     { "database",   "Database",         ATRACE_TAG_DATABASE, { } },
     { "network",    "Network",          ATRACE_TAG_NETWORK, { } },
     { "adb",        "ADB",              ATRACE_TAG_ADB, { } },
-    { "vibrator",   "Vibrator",         ATRACE_TAG_VIBRATOR, {}},
+    { "vibrator",   "Vibrator",         ATRACE_TAG_VIBRATOR, { } },
+    { "aidl",       "AIDL calls",       ATRACE_TAG_AIDL, { } },
     { k_coreServiceCategory, "Core services", 0, { } },
     { k_pdxServiceCategory, "PDX services", 0, { } },
     { "sched",      "CPU Scheduling",   0, {
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index fd2fccb..9648ede 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1159,6 +1159,10 @@
 static void RunDumpsysProto(const std::string& title, int priority,
                             std::chrono::milliseconds timeout,
                             std::chrono::milliseconds service_timeout) {
+    if (!ds.IsZipping()) {
+        MYLOGD("Not dumping %s because it's not a zipped bugreport\n", title.c_str());
+        return;
+    }
     sp<android::IServiceManager> sm = defaultServiceManager();
     Dumpsys dumpsys(sm.get());
     Vector<String16> args;
@@ -1630,20 +1634,8 @@
     ShowUsageAndExit();
 }
 
-static void sig_handler(int) {
-    _exit(EXIT_FAILURE);
-}
-
 static void register_sig_handler() {
-    struct sigaction sa;
-    sigemptyset(&sa.sa_mask);
-    sa.sa_flags = 0;
-    sa.sa_handler = sig_handler;
-    sigaction(SIGPIPE, &sa, NULL); // broken pipe
-    sigaction(SIGSEGV, &sa, NULL); // segment fault
-    sigaction(SIGINT, &sa, NULL); // ctrl-c
-    sigaction(SIGTERM, &sa, NULL); // killed
-    sigaction(SIGQUIT, &sa, NULL); // quit
+    signal(SIGPIPE, SIG_IGN);
 }
 
 bool Dumpstate::FinishZipFile() {
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index 39fddc5..8e393c0 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -378,8 +378,7 @@
                 }
                 if (findAndBumpVersion(hal, version)) {
                     if (&table != &mImplementationsTable) {
-                        hal->interfaces[interfaceName].name = interfaceName;
-                        hal->interfaces[interfaceName].instances.insert(instanceName);
+                        hal->insertLegacyInstance(interfaceName, instanceName);
                     }
                     hal->transportArch.arch |= arch;
                     done = true;
@@ -389,17 +388,16 @@
             if (done) {
                 continue; // to next TableEntry
             }
-            decltype(vintf::ManifestHal::interfaces) interfaces;
-            if (&table != &mImplementationsTable) {
-                interfaces[interfaceName].name = interfaceName;
-                interfaces[interfaceName].instances.insert(instanceName);
-            }
-            if (!manifest.add(vintf::ManifestHal{
+            vintf::ManifestHal manifestHal{
                     vintf::HalFormat::HIDL,
                     std::string{fqName.package()},
                     {version},
                     {transport, arch},
-                    std::move(interfaces)})) {
+                    {}};
+            if (&table != &mImplementationsTable) {
+                manifestHal.insertLegacyInstance(interfaceName, instanceName);
+            }
+            if (!manifest.add(std::move(manifestHal))) {
                 err() << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
             }
         }
@@ -408,7 +406,7 @@
          << "    This is a skeleton " << manifest.type() << " manifest. Notes: " << std::endl
          << INIT_VINTF_NOTES
          << "-->" << std::endl;
-    out << vintf::gHalManifestConverter(manifest, vintf::SerializeFlag::HALS_ONLY);
+    out << vintf::gHalManifestConverter(manifest, vintf::SerializeFlag::HALS_NO_FQNAME);
 }
 
 std::string ListCommand::INIT_VINTF_NOTES{
diff --git a/headers/media_plugin/media/openmax/OMX_VideoExt.h b/headers/media_plugin/media/openmax/OMX_VideoExt.h
index 4b90765..bbf157b 100644
--- a/headers/media_plugin/media/openmax/OMX_VideoExt.h
+++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h
@@ -427,14 +427,15 @@
  * use cases, corresponding to index OMX_IndexParamVideoAndroidImageGrid.
  *
  * OMX_VIDEO_CodingImageHEIC encoders must handle this param type. When this param is set
- * on the component with bEnabled set to true, nGrid* indicates the desired grid config
- * by the client. The component can use this as a heuristic, but is free to choose any
- * suitable grid configs, and the client shall always get the actual from the component
- * after the param is set. Encoder will receive each input image in full, and shall encode
- * it into tiles in row-major, top-row first, left-to-right order, and send each encoded
- * tile in a separate output buffer. All output buffers for the same input buffer shall
- * carry the same timestamp as the input buffer. If the input buffer is marked EOS,
- * the EOS should only appear on the last output buffer for that input buffer.
+ * on the component with bEnabled set to true, nTileWidth, nTileHeight, nGridRows,
+ * nGridCols indicates the desired grid config by the client. The component can use this
+ * as a heuristic, and is free to choose any suitable grid configs. The client shall
+ * always get the actual from the component after the param is set. Encoder will receive
+ * each input image in full, and shall encode it into tiles in row-major, top-row first,
+ * left-to-right order, and send each encoded tile in a separate output buffer. All output
+ * buffers for the same input buffer shall carry the same timestamp as the input buffer.
+ * If the input buffer is marked EOS, the EOS should only appear on the last output buffer
+ * for that input buffer.
  *
  * OMX_VIDEO_CodingHEVC encoders might also receive this param when it's used for image
  * encoding, although in this case the param only serves as a hint. The encoder will
@@ -446,10 +447,10 @@
  *  nSize                      : Size of the structure in bytes
  *  nVersion                   : OMX specification version information
  *  nPortIndex                 : Port that this structure applies to (output port for encoders)
- *  bEnabled                   : Whether grid is enabled. If true, nGrid* specifies the grid
- *                               config; otherwise nGrid* shall be ignored.
- *  nGridWidth                 : Width of each tile.
- *  nGridHeight                : Height of each tile.
+ *  bEnabled                   : Whether grid is enabled. If true, the other parameters
+ *                               specifies the grid config; otherwise they shall be ignored.
+ *  nTileWidth                 : Width of each tile.
+ *  nTileHeight                : Height of each tile.
  *  nGridRows                  : Number of rows in the grid.
  *  nGridCols                  : Number of cols in the grid.
  */
@@ -458,8 +459,8 @@
     OMX_VERSIONTYPE nVersion;
     OMX_U32 nPortIndex;
     OMX_BOOL bEnabled;
-    OMX_U32 nGridWidth;
-    OMX_U32 nGridHeight;
+    OMX_U32 nTileWidth;
+    OMX_U32 nTileHeight;
     OMX_U32 nGridRows;
     OMX_U32 nGridCols;
 } OMX_VIDEO_PARAM_ANDROID_IMAGEGRIDTYPE;
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index e1cc5da..2728f35 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -80,4 +80,29 @@
     }
 }
 
+bool ActivityManager::isUidActive(const uid_t uid, const String16& callingPackage)
+{
+    sp<IActivityManager> service = getService();
+    if (service != NULL) {
+        return service->isUidActive(uid, callingPackage);
+    }
+    return false;
+}
+
+status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
+    sp<IActivityManager> service = getService();
+    if (service != NULL) {
+        return IInterface::asBinder(service)->linkToDeath(recipient);
+    }
+    return INVALID_OPERATION;
+}
+
+status_t ActivityManager::unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
+    sp<IActivityManager> service = getService();
+    if (service != NULL) {
+        return IInterface::asBinder(service)->unlinkToDeath(recipient);
+    }
+    return INVALID_OPERATION;
+}
+
 }; // namespace android
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index b7a5fd9..428db4d 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -78,6 +78,18 @@
          data.writeStrongBinder(IInterface::asBinder(observer));
          remote()->transact(UNREGISTER_UID_OBSERVER_TRANSACTION, data, &reply);
     }
+
+    virtual bool isUidActive(const uid_t uid, const String16& callingPackage)
+    {
+         Parcel data, reply;
+         data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+         data.writeInt32(uid);
+         data.writeString16(callingPackage);
+         remote()->transact(IS_UID_ACTIVE_TRANSACTION, data, &reply);
+         // fail on exception
+         if (reply.readExceptionCode() != 0) return false;
+         return reply.readInt32() == 1;
+    }
 };
 
 // ------------------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 408c428..3090cae 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -50,6 +50,10 @@
                              const int32_t cutpoint,
                              const String16& callingPackage);
     void unregisterUidObserver(const sp<IUidObserver>& observer);
+    bool isUidActive(const uid_t uid, const String16& callingPackage);
+
+    status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
+    status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
 
 private:
     Mutex mLock;
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index bac2a99..6607c0e 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -35,11 +35,13 @@
                                      const int32_t cutpoint,
                                      const String16& callingPackage) = 0;
     virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
+    virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
 
     enum {
         OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
         REGISTER_UID_OBSERVER_TRANSACTION,
-        UNREGISTER_UID_OBSERVER_TRANSACTION
+        UNREGISTER_UID_OBSERVER_TRANSACTION,
+        IS_UID_ACTIVE_TRANSACTION
     };
 };
 
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index 679f44b..a6890ee 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -47,8 +47,8 @@
     ~BpSurfaceComposerClient() override;
 
     status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
-                           uint32_t flags, const sp<IBinder>& parent, uint32_t windowType,
-                           uint32_t ownerUid, sp<IBinder>* handle,
+                           uint32_t flags, const sp<IBinder>& parent, int32_t windowType,
+                           int32_t ownerUid, sp<IBinder>* handle,
                            sp<IGraphicBufferProducer>* gbp) override {
         return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
                                                                             name, width, height,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 61ed976..3cf49d6 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -609,10 +609,28 @@
         PixelFormat format,
         uint32_t flags,
         SurfaceControl* parent,
-        uint32_t windowType,
-        uint32_t ownerUid)
+        int32_t windowType,
+        int32_t ownerUid)
+{
+    sp<SurfaceControl> s;
+    createSurfaceChecked(name, w, h, format, &s, flags, parent, windowType, ownerUid);
+    return s;
+}
+
+status_t SurfaceComposerClient::createSurfaceChecked(
+        const String8& name,
+        uint32_t w,
+        uint32_t h,
+        PixelFormat format,
+        sp<SurfaceControl>* outSurface,
+        uint32_t flags,
+        SurfaceControl* parent,
+        int32_t windowType,
+        int32_t ownerUid)
 {
     sp<SurfaceControl> sur;
+    status_t err = NO_ERROR;
+
     if (mStatus == NO_ERROR) {
         sp<IBinder> handle;
         sp<IBinder> parentHandle;
@@ -621,14 +639,14 @@
         if (parent != nullptr) {
             parentHandle = parent->getHandle();
         }
-        status_t err = mClient->createSurface(name, w, h, format, flags, parentHandle,
+        err = mClient->createSurface(name, w, h, format, flags, parentHandle,
                 windowType, ownerUid, &handle, &gbp);
         ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
         if (err == NO_ERROR) {
-            sur = new SurfaceControl(this, handle, gbp, true /* owned */);
+            *outSurface = new SurfaceControl(this, handle, gbp, true /* owned */);
         }
     }
-    return sur;
+    return err;
 }
 
 status_t SurfaceComposerClient::destroySurface(const sp<IBinder>& sid) {
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index d5bbef2..8dfc99a 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -49,8 +49,8 @@
      * Requires ACCESS_SURFACE_FLINGER permission
      */
     virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
-                                   uint32_t flags, const sp<IBinder>& parent, uint32_t windowType,
-                                   uint32_t ownerUid, sp<IBinder>* handle,
+                                   uint32_t flags, const sp<IBinder>& parent, int32_t windowType,
+                                   int32_t ownerUid, sp<IBinder>* handle,
                                    sp<IGraphicBufferProducer>* gbp) = 0;
 
     /*
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 162fe6e..b45ce4f 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -111,8 +111,20 @@
             PixelFormat format, // pixel-format desired
             uint32_t flags = 0, // usage flags
             SurfaceControl* parent = nullptr, // parent
-            uint32_t windowType = 0, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
-            uint32_t ownerUid = 0 // UID of the task
+            int32_t windowType = -1, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
+            int32_t ownerUid = -1 // UID of the task
+    );
+
+    status_t createSurfaceChecked(
+            const String8& name,// name of the surface
+            uint32_t w,         // width in pixel
+            uint32_t h,         // height in pixel
+            PixelFormat format, // pixel-format desired
+            sp<SurfaceControl>* outSurface,
+            uint32_t flags = 0, // usage flags
+            SurfaceControl* parent = nullptr, // parent
+            int32_t windowType = -1, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.)
+            int32_t ownerUid = -1 // UID of the task
     );
 
     //! Create a virtual display
diff --git a/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
index b7a6099..660a200 100644
--- a/libs/vr/libbufferhub/buffer_hub-test.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -25,7 +25,9 @@
 using android::dvr::BufferHubDefs::IsBufferAcquired;
 using android::dvr::BufferHubDefs::IsBufferReleased;
 using android::dvr::BufferProducer;
+using android::pdx::LocalChannelHandle;
 using android::pdx::LocalHandle;
+using android::pdx::Status;
 
 const int kWidth = 640;
 const int kHeight = 480;
@@ -717,3 +719,64 @@
   // Producer should be able to gain no matter what.
   EXPECT_EQ(0, p->GainAsync(&meta, &fence));
 }
+
+TEST_F(LibBufferHubTest, TestDetachBufferFromProducer) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(p.get() != nullptr);
+  ASSERT_TRUE(c.get() != nullptr);
+
+  DvrNativeBufferMetadata metadata;
+  LocalHandle invalid_fence;
+
+  // Detach in posted state should fail.
+  EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+  EXPECT_GT(RETRY_EINTR(c->Poll(kPollTimeoutMs)), 0);
+  auto s1 = p->Detach();
+  EXPECT_FALSE(s1);
+
+  // Detach in acquired state should fail.
+  EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
+  s1 = p->Detach();
+  EXPECT_FALSE(s1);
+
+  // Detach in released state should fail.
+  EXPECT_EQ(0, c->ReleaseAsync(&metadata, invalid_fence));
+  EXPECT_GT(RETRY_EINTR(p->Poll(kPollTimeoutMs)), 0);
+  s1 = p->Detach();
+  EXPECT_FALSE(s1);
+
+  // Detach in gained state should succeed.
+  EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
+  s1 = p->Detach();
+  EXPECT_TRUE(s1);
+
+  LocalChannelHandle detached_buffer = s1.take();
+  EXPECT_TRUE(detached_buffer.valid());
+
+  // Both producer and consumer should have hangup.
+  EXPECT_GT(RETRY_EINTR(p->Poll(kPollTimeoutMs)), 0);
+  auto s2 = p->GetEventMask(POLLHUP);
+  EXPECT_TRUE(s2);
+  EXPECT_EQ(s2.get(), POLLHUP);
+
+  EXPECT_GT(RETRY_EINTR(c->Poll(kPollTimeoutMs)), 0);
+  s2 = p->GetEventMask(POLLHUP);
+  EXPECT_TRUE(s2);
+  EXPECT_EQ(s2.get(), POLLHUP);
+
+  auto s3 = p->CreateConsumer();
+  EXPECT_FALSE(s3);
+  // Note that here the expected error code is EOPNOTSUPP as the socket towards
+  // ProducerChannel has been teared down.
+  EXPECT_EQ(s3.error(), EOPNOTSUPP);
+
+  s3 = c->CreateConsumer();
+  EXPECT_FALSE(s3);
+  // Note that here the expected error code is EPIPE returned from
+  // ConsumerChannel::HandleMessage as the socket is still open but the producer
+  // is gone.
+  EXPECT_EQ(s3.error(), EPIPE);
+}
diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp
index 6db09a9..13971eb 100644
--- a/libs/vr/libbufferhub/buffer_hub_client.cpp
+++ b/libs/vr/libbufferhub/buffer_hub_client.cpp
@@ -608,5 +608,23 @@
                        : LocalChannelHandle{nullptr, -status.error()});
 }
 
+Status<LocalChannelHandle> BufferProducer::Detach() {
+  uint64_t buffer_state = buffer_state_->load();
+  if (!BufferHubDefs::IsBufferGained(buffer_state)) {
+    // Can only detach a BufferProducer when it's in gained state.
+    ALOGW("BufferProducer::Detach: The buffer (id=%d, state=0x%" PRIx64
+          ") is not in gained state.",
+          id(), buffer_state);
+    return {};
+  }
+
+  Status<LocalChannelHandle> status =
+      InvokeRemoteMethod<BufferHubRPC::ProducerBufferDetach>();
+  ALOGE_IF(!status,
+           "BufferProducer::Detach: Failed to detach buffer (id=%d): %s.", id(),
+           status.GetErrorMessage().c_str());
+  return status;
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
index c791250..32448a1 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
@@ -217,6 +217,14 @@
   // succeeded, or a negative errno code if local error check fails.
   int GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
 
+  // Detaches a ProducerBuffer from an existing producer/consumer set. Can only
+  // be called when a producer buffer has exclusive access to the buffer (i.e.
+  // in the gain'ed state). On the successful return of the IPC call, a new
+  // LocalChannelHandle representing a detached buffer will be returned and all
+  // existing producer and consumer channels will be closed. Further IPCs
+  // towards those channels will return error.
+  Status<LocalChannelHandle> Detach();
+
  private:
   friend BASE;
 
diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
index c70fffc..fabefd5 100644
--- a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
+++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h
@@ -373,6 +373,10 @@
     kOpConsumerAcquire,
     kOpConsumerRelease,
     kOpConsumerSetIgnore,
+    kOpProducerBufferDetach,
+    kOpConsumerBufferDetach,
+    kOpCreateDetachedBuffer,
+    kOpDetachedBufferPromote,
     kOpCreateProducerQueue,
     kOpCreateConsumerQueue,
     kOpGetQueueInfo,
@@ -400,6 +404,28 @@
   PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease,
                     void(LocalFence release_fence));
   PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, void(bool ignore));
+  PDX_REMOTE_METHOD(ProducerBufferDetach, kOpProducerBufferDetach,
+                    LocalChannelHandle(Void));
+
+  // Detaches a ConsumerBuffer from an existing producer/consumer set. Can only
+  // be called when the consumer is the only consumer and it has exclusive
+  // access to the buffer (i.e. in the acquired'ed state). On the successful
+  // return of the IPC call, a new DetachedBufferChannel handle will be returned
+  // and all existing producer and consumer channels will be closed. Further
+  // IPCs towards those channels will return error.
+  PDX_REMOTE_METHOD(ConsumerBufferDetach, kOpConsumerBufferDetach,
+                    LocalChannelHandle(Void));
+
+  // Creates a standalone DetachedBuffer not associated with any
+  // producer/consumer set.
+  PDX_REMOTE_METHOD(CreateDetachedBuffer, kOpCreateDetachedBuffer,
+                    LocalChannelHandle(Void));
+
+  // Promotes a DetachedBuffer to become a ProducerBuffer. Once promoted the
+  // DetachedBuffer channel will be closed automatically on successful IPC
+  // return. Further IPCs towards this channel will return error.
+  PDX_REMOTE_METHOD(DetachedBufferPromote, kOpDetachedBufferPromote,
+                    LocalChannelHandle(Void));
 
   // Buffer Queue Methods.
   PDX_REMOTE_METHOD(CreateProducerQueue, kOpCreateProducerQueue,
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index d22c021..9200207 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -690,9 +690,9 @@
     // Add this buffer from our internal queue tracker
     { // Autolock scope
         Mutex::Autolock lock(mQueueItemLock);
-        mFlinger->mInterceptor.saveBufferUpdate(this, item.mGraphicBuffer->getWidth(),
-                                                item.mGraphicBuffer->getHeight(),
-                                                item.mFrameNumber);
+        mFlinger->mInterceptor->saveBufferUpdate(this, item.mGraphicBuffer->getWidth(),
+                                                 item.mGraphicBuffer->getHeight(),
+                                                 item.mFrameNumber);
         // Reset the frame number tracker when we receive the first buffer after
         // a frame number reset
         if (item.mFrameNumber == 1) {
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 0c9f0e2..c90024b 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -143,7 +143,7 @@
 status_t Client::createSurface(
         const String8& name,
         uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
-        const sp<IBinder>& parentHandle, uint32_t windowType, uint32_t ownerUid,
+        const sp<IBinder>& parentHandle, int32_t windowType, int32_t ownerUid,
         sp<IBinder>* handle,
         sp<IGraphicBufferProducer>* gbp)
 {
@@ -180,13 +180,13 @@
         PixelFormat format;
         uint32_t flags;
         sp<Layer>* parent;
-        uint32_t windowType;
-        uint32_t ownerUid;
+        int32_t windowType;
+        int32_t ownerUid;
     public:
         MessageCreateLayer(SurfaceFlinger* flinger,
                 const String8& name, Client* client,
                 uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
-                sp<IBinder>* handle, uint32_t windowType, uint32_t ownerUid,
+                sp<IBinder>* handle, int32_t windowType, int32_t ownerUid,
                 sp<IGraphicBufferProducer>* gbp,
                 sp<Layer>* parent)
             : flinger(flinger), client(client),
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 2aab28f..c7df9f7 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -58,7 +58,7 @@
     virtual status_t createSurface(
             const String8& name,
             uint32_t w, uint32_t h,PixelFormat format, uint32_t flags,
-            const sp<IBinder>& parent, uint32_t windowType, uint32_t ownerUid,
+            const sp<IBinder>& parent, int32_t windowType, int32_t ownerUid,
             sp<IBinder>* handle,
             sp<IGraphicBufferProducer>* gbp);
 
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 92d5e21..fe0b30b 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -57,9 +57,6 @@
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
 
-static bool useTripleFramebuffer = getInt64< ISurfaceFlingerConfigs,
-        &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2) >= 3;
-
 /*
  * Initialize the display to the specified values.
  *
@@ -74,72 +71,43 @@
         int32_t hwcId,
         bool isSecure,
         const wp<IBinder>& displayToken,
+        const sp<ANativeWindow>& nativeWindow,
         const sp<DisplaySurface>& displaySurface,
-        const sp<IGraphicBufferProducer>& producer,
+        std::unique_ptr<RE::Surface> renderSurface,
+        int displayWidth,
+        int displayHeight,
         bool supportWideColor,
-        bool supportHdr)
+        bool supportHdr,
+        int initialPowerMode)
     : lastCompositionHadVisibleLayers(false),
       mFlinger(flinger),
       mType(type),
       mHwcDisplayId(hwcId),
       mDisplayToken(displayToken),
+      mNativeWindow(nativeWindow),
       mDisplaySurface(displaySurface),
-      mSurface{flinger->getRenderEngine().createSurface()},
-      mDisplayWidth(),
-      mDisplayHeight(),
-      mPageFlipCount(),
+      mSurface{std::move(renderSurface)},
+      mDisplayWidth(displayWidth),
+      mDisplayHeight(displayHeight),
+      mPageFlipCount(0),
       mIsSecure(isSecure),
       mLayerStack(NO_LAYER_STACK),
       mOrientation(),
-      mPowerMode(HWC_POWER_MODE_OFF),
-      mActiveConfig(0)
+      mViewport(Rect::INVALID_RECT),
+      mFrame(Rect::INVALID_RECT),
+      mPowerMode(initialPowerMode),
+      mActiveConfig(0),
+      mActiveColorMode(ColorMode::NATIVE),
+      mDisplayHasWideColor(supportWideColor),
+      mDisplayHasHdr(supportHdr)
 {
     // clang-format on
-    Surface* surface;
-    mNativeWindow = surface = new Surface(producer, false);
-    ANativeWindow* const window = mNativeWindow.get();
-
-    mActiveColorMode = ColorMode::NATIVE;
-    mDisplayHasWideColor = supportWideColor;
-    mDisplayHasHdr = supportHdr;
-
-    /*
-     * Create our display's surface
-     */
-    mSurface->setCritical(mType == DisplayDevice::DISPLAY_PRIMARY);
-    mSurface->setAsync(mType >= DisplayDevice::DISPLAY_VIRTUAL);
-    mSurface->setNativeWindow(window);
-    mDisplayWidth = mSurface->queryWidth();
-    mDisplayHeight = mSurface->queryHeight();
-
-    // Make sure that composition can never be stalled by a virtual display
-    // consumer that isn't processing buffers fast enough. We have to do this
-    // in two places:
-    // * Here, in case the display is composed entirely by HWC.
-    // * In makeCurrent(), using eglSwapInterval. Some EGL drivers set the
-    //   window's swap interval in eglMakeCurrent, so they'll override the
-    //   interval we set here.
-    if (mType >= DisplayDevice::DISPLAY_VIRTUAL)
-        window->setSwapInterval(window, 0);
-
-    mPageFlipCount = 0;
-    mViewport.makeInvalid();
-    mFrame.makeInvalid();
-
-    // virtual displays are always considered enabled
-    mPowerMode = (mType >= DisplayDevice::DISPLAY_VIRTUAL) ?
-                  HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF;
 
     // initialize the display orientation transform.
     setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);
-
-    if (useTripleFramebuffer) {
-        surface->allocateBuffers();
-    }
 }
 
-DisplayDevice::~DisplayDevice() {
-}
+DisplayDevice::~DisplayDevice() = default;
 
 void DisplayDevice::disconnect(HWComposer& hwc) {
     if (mHwcDisplayId >= 0) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 737971f..fbb0d46 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -77,9 +77,14 @@
             int32_t hwcId,
             bool isSecure,
             const wp<IBinder>& displayToken,
+            const sp<ANativeWindow>& nativeWindow,
             const sp<DisplaySurface>& displaySurface,
-            const sp<IGraphicBufferProducer>& producer,
-            bool supportWideColor, bool supportHdr);
+            std::unique_ptr<RE::Surface> renderSurface,
+            int displayWidth,
+            int displayHeight,
+            bool supportWideColor,
+            bool supportHdr,
+            int initialPowerMode);
     // clang-format on
 
     ~DisplayDevice();
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index b7bf964..0c77aba 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -129,6 +129,7 @@
 
     auto display = std::make_unique<Display>(
             *mComposer.get(), mCapabilities, displayId, DisplayType::Virtual);
+    display->setConnected(true);
     *outDisplay = display.get();
     *format = static_cast<android_pixel_format_t>(intFormat);
     mDisplays.emplace(displayId, std::move(display));
@@ -144,31 +145,32 @@
 
 void Device::onHotplug(hwc2_display_t displayId, Connection connection) {
     if (connection == Connection::Connected) {
-        auto display = getDisplayById(displayId);
-        if (display) {
-            if (display->isConnected()) {
-                ALOGW("Attempt to hotplug connect display %" PRIu64
-                        " , which is already connected.", displayId);
-            } else {
-                display->setConnected(true);
-            }
-        } else {
-            DisplayType displayType;
-            auto intError = mComposer->getDisplayType(displayId,
-                    reinterpret_cast<Hwc2::IComposerClient::DisplayType *>(
-                            &displayType));
-            auto error = static_cast<Error>(intError);
-            if (error != Error::None) {
-                ALOGE("getDisplayType(%" PRIu64 ") failed: %s (%d). "
-                        "Aborting hotplug attempt.",
-                        displayId, to_string(error).c_str(), intError);
-                return;
-            }
-
-            auto newDisplay = std::make_unique<Display>(
-                    *mComposer.get(), mCapabilities, displayId, displayType);
-            mDisplays.emplace(displayId, std::move(newDisplay));
+        // If we get a hotplug connected event for a display we already have,
+        // destroy the display and recreate it. This will force us to requery
+        // the display params and recreate all layers on that display.
+        auto oldDisplay = getDisplayById(displayId);
+        if (oldDisplay != nullptr && oldDisplay->isConnected()) {
+            ALOGI("Hotplug connecting an already connected display."
+                    " Clearing old display state.");
         }
+        mDisplays.erase(displayId);
+
+        DisplayType displayType;
+        auto intError = mComposer->getDisplayType(displayId,
+                reinterpret_cast<Hwc2::IComposerClient::DisplayType *>(
+                        &displayType));
+        auto error = static_cast<Error>(intError);
+        if (error != Error::None) {
+            ALOGE("getDisplayType(%" PRIu64 ") failed: %s (%d). "
+                    "Aborting hotplug attempt.",
+                    displayId, to_string(error).c_str(), intError);
+            return;
+        }
+
+        auto newDisplay = std::make_unique<Display>(
+                *mComposer.get(), mCapabilities, displayId, displayType);
+        newDisplay->setConnected(true);
+        mDisplays.emplace(displayId, std::move(newDisplay));
     } else if (connection == Connection::Disconnected) {
         // The display will later be destroyed by a call to
         // destroyDisplay(). For now we just mark it disconnected.
@@ -209,16 +211,14 @@
 // Display methods
 
 Display::Display(android::Hwc2::Composer& composer,
-                 const std::unordered_set<Capability>& capabilities,
-                 hwc2_display_t id, DisplayType type)
-  : mComposer(composer),
-    mCapabilities(capabilities),
-    mId(id),
-    mIsConnected(false),
-    mType(type)
-{
+                 const std::unordered_set<Capability>& capabilities, hwc2_display_t id,
+                 DisplayType type)
+      : mComposer(composer),
+        mCapabilities(capabilities),
+        mId(id),
+        mIsConnected(false),
+        mType(type) {
     ALOGV("Created display %" PRIu64, id);
-    setConnected(true);
 }
 
 Display::~Display() {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 0fc37ec..5b53b54 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -48,6 +48,8 @@
     namespace Hwc2 {
         class Composer;
     }
+
+    class TestableSurfaceFlinger;
 }
 
 namespace HWC2 {
@@ -126,8 +128,7 @@
 class Display
 {
 public:
-    Display(android::Hwc2::Composer& composer,
-            const std::unordered_set<Capability>& capabilities,
+    Display(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities,
             hwc2_display_t id, DisplayType type);
     ~Display();
 
@@ -263,6 +264,8 @@
     // on this display
     Layer* getLayerById(hwc2_layer_t id) const;
 
+    friend android::TestableSurfaceFlinger;
+
     // Member variables
 
     // These are references to data owned by HWC2::Device, which will outlive
diff --git a/services/surfaceflinger/EventControlThread.cpp b/services/surfaceflinger/EventControlThread.cpp
index ac54059..fb6cff5 100644
--- a/services/surfaceflinger/EventControlThread.cpp
+++ b/services/surfaceflinger/EventControlThread.cpp
@@ -26,6 +26,10 @@
 
 namespace android {
 
+EventControlThread::~EventControlThread() = default;
+
+namespace impl {
+
 EventControlThread::EventControlThread(EventControlThread::SetVSyncEnabledFunction function)
       : mSetVSyncEnabled(function) {
     pthread_setname_np(mThread.native_handle(), "EventControlThread");
@@ -67,4 +71,5 @@
     }
 }
 
+} // namespace impl
 } // namespace android
diff --git a/services/surfaceflinger/EventControlThread.h b/services/surfaceflinger/EventControlThread.h
index 321fb79..9be4e7c 100644
--- a/services/surfaceflinger/EventControlThread.h
+++ b/services/surfaceflinger/EventControlThread.h
@@ -16,8 +16,8 @@
 
 #pragma once
 
-#include <cstddef>
 #include <condition_variable>
+#include <cstddef>
 #include <functional>
 #include <mutex>
 #include <thread>
@@ -30,12 +30,22 @@
 
 class EventControlThread {
 public:
+    virtual ~EventControlThread();
+
+    virtual void setVsyncEnabled(bool enabled) = 0;
+};
+
+namespace impl {
+
+class EventControlThread final : public android::EventControlThread {
+public:
     using SetVSyncEnabledFunction = std::function<void(bool)>;
 
     explicit EventControlThread(SetVSyncEnabledFunction function);
     ~EventControlThread();
 
-    void setVsyncEnabled(bool enabled);
+    // EventControlThread implementation
+    void setVsyncEnabled(bool enabled) override;
 
 private:
     void threadMain();
@@ -51,4 +61,5 @@
     std::thread mThread{&EventControlThread::threadMain, this};
 };
 
+} // namespace impl
 } // namespace android
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index 1f4f5a5..bb9c070 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -217,7 +217,7 @@
             if (timestamp) {
                 // we have a vsync event to dispatch
                 if (mInterceptVSyncs) {
-                    mFlinger.mInterceptor.saveVSyncEvent(timestamp);
+                    mFlinger.mInterceptor->saveVSyncEvent(timestamp);
                 }
                 *event = mVSyncEvent[i];
                 mVSyncEvent[i].header.timestamp = 0;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 44e60ed..2499a98 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -587,8 +587,10 @@
     sp<Layer> parent = mDrawingParent.promote();
     if (parent.get()) {
         auto& parentState = parent->getDrawingState();
-        type = parentState.type;
-        appId = parentState.appId;
+        if (parentState.type >= 0 || parentState.appId >= 0) {
+            type = parentState.type;
+            appId = parentState.appId;
+        }
     }
 
     error = hwcLayer->setInfo(type, appId);
@@ -1313,7 +1315,7 @@
     return true;
 }
 
-void Layer::setInfo(uint32_t type, uint32_t appId) {
+void Layer::setInfo(int32_t type, int32_t appId) {
     mCurrentState.appId = appId;
     mCurrentState.type = type;
     mCurrentState.modified = true;
@@ -1594,7 +1596,7 @@
     return true;
 }
 
-void Layer::reparentChildrenForDrawing(const sp<Layer>& newParent) {
+void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
     for (const sp<Layer>& child : mDrawingChildren) {
         child->mDrawingParent = newParent;
     }
@@ -1919,6 +1921,8 @@
 
     layerInfo->set_queued_frames(getQueuedFrameCount());
     layerInfo->set_refresh_pending(isBufferLatched());
+    layerInfo->set_window_type(state.type);
+    layerInfo->set_app_id(state.appId);
 }
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 996d686..9a9b5e6 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -66,6 +66,10 @@
 class LayerDebugInfo;
 class LayerBE;
 
+namespace impl {
+class SurfaceInterceptor;
+}
+
 // ---------------------------------------------------------------------------
 
 struct CompositionInfo {
@@ -203,8 +207,8 @@
         Region requestedTransparentRegion;
         android_dataspace dataSpace;
 
-        uint32_t appId;
-        uint32_t type;
+        int32_t appId;
+        int32_t type;
 
         // If non-null, a Surface this Surface's Z-order is interpreted relative to.
         wp<Layer> zOrderRelativeOf;
@@ -285,9 +289,9 @@
     void deferTransactionUntil(const sp<IBinder>& barrierHandle, uint64_t frameNumber);
     void deferTransactionUntil(const sp<Layer>& barrierLayer, uint64_t frameNumber);
     bool setOverrideScalingMode(int32_t overrideScalingMode);
-    void setInfo(uint32_t type, uint32_t appId);
+    void setInfo(int32_t type, int32_t appId);
     bool reparentChildren(const sp<IBinder>& layer);
-    void reparentChildrenForDrawing(const sp<Layer>& layer);
+    void setChildrenDrawingParent(const sp<Layer>& layer);
     bool reparent(const sp<IBinder>& newParentHandle);
     bool detachChildren();
 
@@ -593,7 +597,7 @@
 
     virtual void onFirstRef();
 
-    friend class SurfaceInterceptor;
+    friend class impl::SurfaceInterceptor;
 
     void commitTransaction(const State& stateToCommit);
 
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
index 5a6ff4d..056d381 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -24,6 +24,7 @@
 #include <utils/Timers.h>
 #include <utils/threads.h>
 
+#include <gui/DisplayEventReceiver.h>
 #include <gui/IDisplayEventConnection.h>
 
 #include "EventThread.h"
@@ -45,6 +46,12 @@
 
 // ---------------------------------------------------------------------------
 
+MessageQueue::~MessageQueue() = default;
+
+// ---------------------------------------------------------------------------
+
+namespace impl {
+
 void MessageQueue::Handler::dispatchRefresh() {
     if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) {
         mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH));
@@ -72,17 +79,13 @@
 
 // ---------------------------------------------------------------------------
 
-MessageQueue::MessageQueue() {}
-
-MessageQueue::~MessageQueue() {}
-
 void MessageQueue::init(const sp<SurfaceFlinger>& flinger) {
     mFlinger = flinger;
     mLooper = new Looper(true);
     mHandler = new Handler(*this);
 }
 
-void MessageQueue::setEventThread(EventThread* eventThread) {
+void MessageQueue::setEventThread(android::EventThread* eventThread) {
     if (mEventThread == eventThread) {
         return;
     }
@@ -159,4 +162,5 @@
 
 // ---------------------------------------------------------------------------
 
-}; // namespace android
+} // namespace impl
+} // namespace android
diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h
index dcfc716..90d1c72 100644
--- a/services/surfaceflinger/MessageQueue.h
+++ b/services/surfaceflinger/MessageQueue.h
@@ -25,7 +25,7 @@
 #include <utils/Timers.h>
 #include <utils/threads.h>
 
-#include <gui/DisplayEventReceiver.h>
+#include <gui/IDisplayEventConnection.h>
 #include <private/gui/BitTube.h>
 
 #include "Barrier.h"
@@ -34,7 +34,6 @@
 
 namespace android {
 
-class IDisplayEventConnection;
 class EventThread;
 class SurfaceFlinger;
 
@@ -77,6 +76,27 @@
 // ---------------------------------------------------------------------------
 
 class MessageQueue {
+public:
+    enum {
+        INVALIDATE = 0,
+        REFRESH = 1,
+    };
+
+    virtual ~MessageQueue();
+
+    virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
+    virtual void setEventThread(EventThread* events) = 0;
+    virtual void waitMessage() = 0;
+    virtual status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) = 0;
+    virtual void invalidate() = 0;
+    virtual void refresh() = 0;
+};
+
+// ---------------------------------------------------------------------------
+
+namespace impl {
+
+class MessageQueue final : public android::MessageQueue {
     class Handler : public MessageHandler {
         enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 };
         MessageQueue& mQueue;
@@ -93,7 +113,7 @@
 
     sp<SurfaceFlinger> mFlinger;
     sp<Looper> mLooper;
-    EventThread* mEventThread;
+    android::EventThread* mEventThread;
     sp<IDisplayEventConnection> mEvents;
     gui::BitTube mEventTube;
     sp<Handler> mHandler;
@@ -102,27 +122,22 @@
     int eventReceiver(int fd, int events);
 
 public:
-    enum {
-        INVALIDATE = 0,
-        REFRESH = 1,
-    };
+    ~MessageQueue() override = default;
+    void init(const sp<SurfaceFlinger>& flinger) override;
+    void setEventThread(android::EventThread* events) override;
 
-    MessageQueue();
-    ~MessageQueue();
-    void init(const sp<SurfaceFlinger>& flinger);
-    void setEventThread(EventThread* events);
-
-    void waitMessage();
-    status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0);
+    void waitMessage() override;
+    status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) override;
 
     // sends INVALIDATE message at next VSYNC
-    void invalidate();
+    void invalidate() override;
     // sends REFRESH message at next VSYNC
-    void refresh();
+    void refresh() override;
 };
 
 // ---------------------------------------------------------------------------
 
-}; // namespace android
+} // namespace impl
+} // namespace android
 
 #endif /* ANDROID_MESSAGE_QUEUE_H */
diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
index 7a43ea9..6a34981 100644
--- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp
+++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp
@@ -305,15 +305,24 @@
 
         if (needs.hasToneMapping()) {
             fs << R"__SHADER__(
-                float ToneMapChannel(const float color) {
+                float CalculateY(const vec3 color) {
+                    // BT2020 standard uses the unadjusted KR = 0.2627,
+                    // KB = 0.0593 luminance interpretation for RGB conversion.
+                    return color.r * 0.262700 + color.g * 0.677998 +
+                            color.b * 0.059302;
+                }
+                vec3 ToneMap(const vec3 color) {
                     const float maxLumi = 10000.0;
                     const float maxMasteringLumi = 1000.0;
                     const float maxContentLumi = 1000.0;
                     const float maxInLumi = min(maxMasteringLumi, maxContentLumi);
                     const float maxOutLumi = 500.0;
 
+                    // Calculate Y value in XYZ color space.
+                    float colorY = CalculateY(color);
+
                     // convert to nits first
-                    float nits = color * maxLumi;
+                    float nits = colorY * maxLumi;
 
                     // clamp to max input luminance
                     nits = clamp(nits, 0.0, maxInLumi);
@@ -360,12 +369,8 @@
                     }
 
                     // convert back to [0.0, 1.0]
-                    return nits / maxOutLumi;
-                }
-
-                vec3 ToneMap(const vec3 color) {
-                    return vec3(ToneMapChannel(color.r), ToneMapChannel(color.g),
-                                ToneMapChannel(color.b));
+                    float targetY = nits / maxOutLumi;
+                    return color * (targetY / max(1e-6, colorY));
                 }
             )__SHADER__";
         } else {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index dd94868..5c69b98 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -154,6 +154,32 @@
     return std::string(value) == "true";
 }
 
+NativeWindowSurface::~NativeWindowSurface() = default;
+
+namespace impl {
+
+class NativeWindowSurface final : public android::NativeWindowSurface {
+public:
+    static std::unique_ptr<android::NativeWindowSurface> create(
+            const sp<IGraphicBufferProducer>& producer) {
+        return std::make_unique<NativeWindowSurface>(producer);
+    }
+
+    explicit NativeWindowSurface(const sp<IGraphicBufferProducer>& producer)
+          : surface(new Surface(producer, false)) {}
+
+    ~NativeWindowSurface() override = default;
+
+private:
+    sp<ANativeWindow> getNativeWindow() const override { return surface; }
+
+    void preallocateBuffers() override { surface->allocateBuffers(); }
+
+    sp<Surface> surface;
+};
+
+} // namespace impl
+
 SurfaceFlingerBE::SurfaceFlingerBE()
       : mHwcServiceName(getHwcServiceName()),
         mRenderEngine(nullptr),
@@ -163,7 +189,7 @@
         mComposerSequenceId(0) {
 }
 
-SurfaceFlinger::SurfaceFlinger()
+SurfaceFlinger::SurfaceFlinger(SurfaceFlinger::SkipInitializationTag)
       : BnSurfaceComposer(),
         mTransactionFlags(0),
         mTransactionPending(false),
@@ -186,7 +212,6 @@
         mLastTransactionTime(0),
         mBootFinished(false),
         mForceFullDamage(false),
-        mInterceptor(this),
         mPrimaryDispSync("PrimaryDispSync"),
         mPrimaryHWVsyncEnabled(false),
         mHWVsyncAvailable(false),
@@ -195,7 +220,10 @@
         mNumLayers(0),
         mVrFlingerRequestsDisplay(false),
         mMainThreadId(std::this_thread::get_id()),
-        mCreateBufferQueue(&BufferQueue::createBufferQueue) {
+        mCreateBufferQueue(&BufferQueue::createBufferQueue),
+        mCreateNativeWindowSurface(&impl::NativeWindowSurface::create) {}
+
+SurfaceFlinger::SurfaceFlinger() : SurfaceFlinger(SkipInitialization) {
     ALOGI("SurfaceFlinger is starting");
 
     vsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs,
@@ -282,7 +310,7 @@
 
 void SurfaceFlinger::onFirstRef()
 {
-    mEventQueue.init(this);
+    mEventQueue->init(this);
 }
 
 SurfaceFlinger::~SurfaceFlinger()
@@ -348,7 +376,7 @@
     DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure);
     info.displayName = displayName;
     mCurrentState.displays.add(token, info);
-    mInterceptor.saveDisplayCreation(info);
+    mInterceptor->saveDisplayCreation(info);
     return token;
 }
 
@@ -366,7 +394,7 @@
         ALOGE("destroyDisplay called for non-virtual display");
         return;
     }
-    mInterceptor.saveDisplayDeletion(info.displayId);
+    mInterceptor->saveDisplayDeletion(info.displayId);
     mCurrentState.displays.removeItemsAt(idx);
     setTransactionFlags(eDisplayTransactionNeeded);
 }
@@ -588,9 +616,10 @@
     mSfEventThreadSource =
             std::make_unique<DispSyncSource>(&mPrimaryDispSync,
                                              SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf");
+
     mSFEventThread = std::make_unique<impl::EventThread>(mSfEventThreadSource.get(), *this, true,
                                                          "sfEventThread");
-    mEventQueue.setEventThread(mSFEventThread.get());
+    mEventQueue->setEventThread(mSFEventThread.get());
 
     // Get a RenderEngine for the given display / config (can't fail)
     getBE().mRenderEngine =
@@ -637,9 +666,8 @@
         }
     }
 
-    mEventControlThread = std::make_unique<EventControlThread>([this](bool enabled) {
-        setVsyncEnabled(HWC_DISPLAY_PRIMARY, enabled);
-    });
+    mEventControlThread = std::make_unique<impl::EventControlThread>(
+            [this](bool enabled) { setVsyncEnabled(HWC_DISPLAY_PRIMARY, enabled); });
 
     // initialize our drawing state
     mDrawingState = mCurrentState;
@@ -1079,10 +1107,10 @@
                         std::make_unique<impl::EventThread>(mVSyncInjector.get(), *this, false,
                                                             "injEventThread");
             }
-            mEventQueue.setEventThread(mInjectorEventThread.get());
+            mEventQueue->setEventThread(mInjectorEventThread.get());
         } else {
             ALOGV("VSync Injections disabled");
-            mEventQueue.setEventThread(mSFEventThread.get());
+            mEventQueue->setEventThread(mSFEventThread.get());
         }
 
         mInjectVSyncs = enable;
@@ -1147,30 +1175,30 @@
 // ----------------------------------------------------------------------------
 
 void SurfaceFlinger::waitForEvent() {
-    mEventQueue.waitMessage();
+    mEventQueue->waitMessage();
 }
 
 void SurfaceFlinger::signalTransaction() {
-    mEventQueue.invalidate();
+    mEventQueue->invalidate();
 }
 
 void SurfaceFlinger::signalLayerUpdate() {
-    mEventQueue.invalidate();
+    mEventQueue->invalidate();
 }
 
 void SurfaceFlinger::signalRefresh() {
     mRefreshPending = true;
-    mEventQueue.refresh();
+    mEventQueue->refresh();
 }
 
 status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg,
         nsecs_t reltime, uint32_t /* flags */) {
-    return mEventQueue.postMessage(msg, reltime);
+    return mEventQueue->postMessage(msg, reltime);
 }
 
 status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg,
         nsecs_t reltime, uint32_t /* flags */) {
-    status_t res = mEventQueue.postMessage(msg, reltime);
+    status_t res = mEventQueue->postMessage(msg, reltime);
     if (res == NO_ERROR) {
         msg->wait();
     }
@@ -2079,7 +2107,7 @@
 }
 
 DisplayDevice::DisplayType SurfaceFlinger::determineDisplayType(hwc2_display_t display,
-        HWC2::Connection connection) const {
+                                                                HWC2::Connection connection) const {
     // Figure out whether the event is for the primary display or an
     // external display by matching the Hwc display id against one for a
     // connected display. If we did not find a match, we then check what
@@ -2120,23 +2148,23 @@
         getBE().mHwc->onHotplug(event.display, displayType, event.connection);
 
         if (event.connection == HWC2::Connection::Connected) {
-            ALOGV("Creating built in display %d", displayType);
-            ALOGW_IF(mBuiltinDisplays[displayType],
-                    "Overwriting display token for display type %d", displayType);
-            mBuiltinDisplays[displayType] = new BBinder();
-            // All non-virtual displays are currently considered secure.
-            DisplayDeviceState info(displayType, true);
-            info.displayName = displayType == DisplayDevice::DISPLAY_PRIMARY ?
-                    "Built-in Screen" : "External Screen";
-            mCurrentState.displays.add(mBuiltinDisplays[displayType], info);
-            mInterceptor.saveDisplayCreation(info);
+            if (!mBuiltinDisplays[displayType].get()) {
+                ALOGV("Creating built in display %d", displayType);
+                mBuiltinDisplays[displayType] = new BBinder();
+                // All non-virtual displays are currently considered secure.
+                DisplayDeviceState info(displayType, true);
+                info.displayName = displayType == DisplayDevice::DISPLAY_PRIMARY ?
+                        "Built-in Screen" : "External Screen";
+                mCurrentState.displays.add(mBuiltinDisplays[displayType], info);
+                mInterceptor->saveDisplayCreation(info);
+            }
         } else {
             ALOGV("Removing built in display %d", displayType);
 
             ssize_t idx = mCurrentState.displays.indexOfKey(mBuiltinDisplays[displayType]);
             if (idx >= 0) {
                 const DisplayDeviceState& info(mCurrentState.displays.valueAt(idx));
-                mInterceptor.saveDisplayDeletion(info.displayId);
+                mInterceptor->saveDisplayDeletion(info.displayId);
                 mCurrentState.displays.removeItemsAt(idx);
             }
             mBuiltinDisplays[displayType].clear();
@@ -2148,6 +2176,84 @@
     mPendingHotplugEvents.clear();
 }
 
+sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
+        const wp<IBinder>& display, int hwcId, const DisplayDeviceState& state,
+        const sp<DisplaySurface>& dispSurface, const sp<IGraphicBufferProducer>& producer) {
+    bool hasWideColorSupport = false;
+    if (hasWideColorDisplay) {
+        std::vector<ColorMode> modes = getHwComposer().getColorModes(state.type);
+        for (ColorMode colorMode : modes) {
+            switch (colorMode) {
+                case ColorMode::DISPLAY_P3:
+                case ColorMode::ADOBE_RGB:
+                case ColorMode::DCI_P3:
+                    hasWideColorSupport = true;
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    bool hasHdrSupport = false;
+    std::unique_ptr<HdrCapabilities> hdrCapabilities =
+            getHwComposer().getHdrCapabilities(state.type);
+    if (hdrCapabilities) {
+        const std::vector<int32_t> types = hdrCapabilities->getSupportedHdrTypes();
+        auto iter = std::find(types.cbegin(), types.cend(), HAL_HDR_HDR10);
+        hasHdrSupport = iter != types.cend();
+    }
+
+    auto nativeWindowSurface = mCreateNativeWindowSurface(producer);
+    auto nativeWindow = nativeWindowSurface->getNativeWindow();
+
+    /*
+     * Create our display's surface
+     */
+    std::unique_ptr<RE::Surface> renderSurface = getRenderEngine().createSurface();
+    renderSurface->setCritical(state.type == DisplayDevice::DISPLAY_PRIMARY);
+    renderSurface->setAsync(state.type >= DisplayDevice::DISPLAY_VIRTUAL);
+    renderSurface->setNativeWindow(nativeWindow.get());
+    const int displayWidth = renderSurface->queryWidth();
+    const int displayHeight = renderSurface->queryHeight();
+
+    // Make sure that composition can never be stalled by a virtual display
+    // consumer that isn't processing buffers fast enough. We have to do this
+    // in two places:
+    // * Here, in case the display is composed entirely by HWC.
+    // * In makeCurrent(), using eglSwapInterval. Some EGL drivers set the
+    //   window's swap interval in eglMakeCurrent, so they'll override the
+    //   interval we set here.
+    if (state.type >= DisplayDevice::DISPLAY_VIRTUAL) {
+        nativeWindow->setSwapInterval(nativeWindow.get(), 0);
+    }
+
+    // virtual displays are always considered enabled
+    auto initialPowerMode = (state.type >= DisplayDevice::DISPLAY_VIRTUAL) ? HWC_POWER_MODE_NORMAL
+                                                                           : HWC_POWER_MODE_OFF;
+
+    sp<DisplayDevice> hw =
+            new DisplayDevice(this, state.type, hwcId, state.isSecure, display, nativeWindow,
+                              dispSurface, std::move(renderSurface), displayWidth, displayHeight,
+                              hasWideColorSupport, hasHdrSupport, initialPowerMode);
+
+    if (maxFrameBufferAcquiredBuffers >= 3) {
+        nativeWindowSurface->preallocateBuffers();
+    }
+
+    ColorMode defaultColorMode = ColorMode::NATIVE;
+    if (hasWideColorSupport) {
+        defaultColorMode = ColorMode::SRGB;
+    }
+    setActiveColorModeInternal(hw, defaultColorMode);
+    hw->setCompositionDataSpace(HAL_DATASPACE_UNKNOWN);
+    hw->setLayerStack(state.layerStack);
+    hw->setProjection(state.orientation, state.viewport, state.frame);
+    hw->setDisplayName(state.displayName);
+
+    return hw;
+}
+
 void SurfaceFlinger::processDisplayChangesLocked() {
     // here we take advantage of Vector's copy-on-write semantics to
     // improve performance by skipping the transaction entirely when
@@ -2270,50 +2376,10 @@
                 }
 
                 const wp<IBinder>& display(curr.keyAt(i));
-
                 if (dispSurface != nullptr) {
-                    bool hasWideColorSupport = false;
-                    if (hasWideColorDisplay) {
-                        std::vector<ColorMode> modes =
-                                getHwComposer().getColorModes(state.type);
-                        for (ColorMode colorMode : modes) {
-                            switch (colorMode) {
-                                case ColorMode::DISPLAY_P3:
-                                case ColorMode::ADOBE_RGB:
-                                case ColorMode::DCI_P3:
-                                    hasWideColorSupport = true;
-                                    break;
-                                default:
-                                    break;
-                            }
-                        }
-                    }
-
-                    bool hasHdrSupport = false;
-                    std::unique_ptr<HdrCapabilities> hdrCapabilities =
-                        getHwComposer().getHdrCapabilities(state.type);
-                    if (hdrCapabilities) {
-                        const std::vector<int32_t> types = hdrCapabilities->getSupportedHdrTypes();
-                        auto iter = std::find(types.cbegin(), types.cend(), HAL_HDR_HDR10);
-                        hasHdrSupport = iter != types.cend();
-                    }
-
-                    sp<DisplayDevice> hw =
-                            new DisplayDevice(this, state.type, hwcId, state.isSecure, display,
-                                              dispSurface, producer, hasWideColorSupport,
-                                              hasHdrSupport);
-
-                    ColorMode defaultColorMode = ColorMode::NATIVE;
-                    if (hasWideColorSupport) {
-                        defaultColorMode = ColorMode::SRGB;
-                    }
-                    setActiveColorModeInternal(hw, defaultColorMode);
-                    hw->setCompositionDataSpace(HAL_DATASPACE_UNKNOWN);
-                    hw->setLayerStack(state.layerStack);
-                    hw->setProjection(state.orientation, state.viewport, state.frame);
-                    hw->setDisplayName(state.displayName);
-
-                    mDisplays.add(display, hw);
+                    mDisplays.add(display,
+                                  setupNewDisplayDeviceInternal(display, hwcId, state, dispSurface,
+                                                                producer));
                     if (!state.isVirtualDisplay()) {
                         mEventThread->onHotplugReceived(state.type, true);
                     }
@@ -3063,8 +3129,8 @@
     }
 
     if (transactionFlags) {
-        if (mInterceptor.isEnabled()) {
-            mInterceptor.saveTransaction(states, mCurrentState.displays, displays, flags);
+        if (mInterceptor->isEnabled()) {
+            mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags);
         }
 
         // this triggers the transaction
@@ -3318,7 +3384,7 @@
         const String8& name,
         const sp<Client>& client,
         uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
-        uint32_t windowType, uint32_t ownerUid, sp<IBinder>* handle,
+        int32_t windowType, int32_t ownerUid, sp<IBinder>* handle,
         sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent)
 {
     if (int32_t(w|h) < 0) {
@@ -3367,7 +3433,7 @@
     if (result != NO_ERROR) {
         return result;
     }
-    mInterceptor.saveSurfaceCreation(layer);
+    mInterceptor->saveSurfaceCreation(layer);
 
     setTransactionFlags(eTransactionNeeded);
     return result;
@@ -3439,7 +3505,7 @@
     status_t err = NO_ERROR;
     sp<Layer> l(client->getLayerUser(handle));
     if (l != nullptr) {
-        mInterceptor.saveSurfaceDeletion(l);
+        mInterceptor->saveSurfaceDeletion(l);
         err = removeLayer(l);
         ALOGE_IF(err<0 && err != NAME_NOT_FOUND,
                 "error removing layer=%p (%s)", l.get(), strerror(-err));
@@ -3521,14 +3587,14 @@
         return;
     }
 
-    if (mInterceptor.isEnabled()) {
+    if (mInterceptor->isEnabled()) {
         ConditionalLock lock(mStateLock, !stateLockHeld);
         ssize_t idx = mCurrentState.displays.indexOfKey(hw->getDisplayToken());
         if (idx < 0) {
             ALOGW("Surface Interceptor SavePowerMode: invalid display token");
             return;
         }
-        mInterceptor.savePowerModeUpdate(mCurrentState.displays.valueAt(idx).displayId, mode);
+        mInterceptor->savePowerModeUpdate(mCurrentState.displays.valueAt(idx).displayId, mode);
     }
 
     if (currentMode == HWC_POWER_MODE_OFF) {
@@ -3981,7 +4047,7 @@
 
     LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current);
     auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
-    result.append(LayerProtoParser::layersToString(layerTree).c_str());
+    result.append(LayerProtoParser::layersToString(std::move(layerTree)).c_str());
 
     /*
      * Dump Display state
@@ -4333,11 +4399,11 @@
                 n = data.readInt32();
                 if (n) {
                     ALOGV("Interceptor enabled");
-                    mInterceptor.enable(mDrawingState.layersSortedByZ, mDrawingState.displays);
+                    mInterceptor->enable(mDrawingState.layersSortedByZ, mDrawingState.displays);
                 }
                 else{
                     ALOGV("Interceptor disabled");
-                    mInterceptor.disable();
+                    mInterceptor->disable();
                 }
                 return NO_ERROR;
             }
@@ -4461,9 +4527,9 @@
 
             ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent)
                   : oldParent(oldParent), newParent(newParent) {
-                oldParent->reparentChildrenForDrawing(newParent);
+                oldParent->setChildrenDrawingParent(newParent);
             }
-            ~ReparentForDrawing() { newParent->reparentChildrenForDrawing(oldParent); }
+            ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
         };
 
         void render(std::function<void()> drawLayers) override {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 51001e5..42d8112 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -118,6 +118,19 @@
     eTransactionMask          = 0x07
 };
 
+// A thin interface to abstract creating instances of Surface (gui/Surface.h) to
+// use as a NativeWindow.
+class NativeWindowSurface {
+public:
+    virtual ~NativeWindowSurface();
+
+    // Gets the NativeWindow to use for the surface.
+    virtual sp<ANativeWindow> getNativeWindow() const = 0;
+
+    // Indicates that the surface should allocate its buffers now.
+    virtual void preallocateBuffers() = 0;
+};
+
 class SurfaceFlingerBE
 {
 public:
@@ -265,6 +278,9 @@
         return "SurfaceFlinger";
     }
 
+    struct SkipInitializationTag {};
+    static constexpr SkipInitializationTag SkipInitialization;
+    explicit SurfaceFlinger(SkipInitializationTag) ANDROID_API;
     SurfaceFlinger() ANDROID_API;
 
     // must be called before clients can connect
@@ -485,7 +501,7 @@
      */
     status_t createLayer(const String8& name, const sp<Client>& client,
             uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
-            uint32_t windowType, uint32_t ownerUid, sp<IBinder>* handle,
+            int32_t windowType, int32_t ownerUid, sp<IBinder>* handle,
             sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent);
 
     status_t createBufferLayer(const sp<Client>& client, const String8& name,
@@ -643,6 +659,10 @@
      */
     DisplayDevice::DisplayType determineDisplayType(hwc2_display_t display,
             HWC2::Connection connection) const;
+    sp<DisplayDevice> setupNewDisplayDeviceInternal(const wp<IBinder>& display, int hwcId,
+                                                    const DisplayDeviceState& state,
+                                                    const sp<DisplaySurface>& dispSurface,
+                                                    const sp<IGraphicBufferProducer>& producer);
     void processDisplayChangesLocked();
     void processDisplayHotplugEventsLocked();
 
@@ -774,7 +794,8 @@
     bool mBootFinished;
     bool mForceFullDamage;
     bool mPropagateBackpressure = true;
-    SurfaceInterceptor mInterceptor;
+    std::unique_ptr<SurfaceInterceptor> mInterceptor =
+            std::make_unique<impl::SurfaceInterceptor>(this);
     SurfaceTracing mTracing;
     bool mUseHwcVirtualDisplays = false;
 
@@ -782,7 +803,7 @@
     bool mLayerTripleBufferingDisabled = false;
 
     // these are thread safe
-    mutable MessageQueue mEventQueue;
+    mutable std::unique_ptr<MessageQueue> mEventQueue{std::make_unique<impl::MessageQueue>()};
     FrameTracker mAnimFrameTracker;
     DispSync mPrimaryDispSync;
 
@@ -833,6 +854,10 @@
                                bool /* consumerIsSurfaceFlinger */)>;
     CreateBufferQueueFunction mCreateBufferQueue;
 
+    using CreateNativeWindowSurfaceFunction =
+            std::function<std::unique_ptr<NativeWindowSurface>(const sp<IGraphicBufferProducer>&)>;
+    CreateNativeWindowSurfaceFunction mCreateNativeWindowSurface;
+
     SurfaceFlingerBE mBE;
 };
 }; // namespace android
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index eeb4929..4596a21 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -31,6 +31,10 @@
 
 // ----------------------------------------------------------------------------
 
+SurfaceInterceptor::~SurfaceInterceptor() = default;
+
+namespace impl {
+
 SurfaceInterceptor::SurfaceInterceptor(SurfaceFlinger* flinger)
     :   mFlinger(flinger)
 {
@@ -593,5 +597,5 @@
     addPowerModeUpdateLocked(createTraceIncrementLocked(), displayId, mode);
 }
 
-
+} // namespace impl
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 30ebcc6..96defcc 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -21,48 +21,89 @@
 
 #include <mutex>
 
+#include <gui/LayerState.h>
+
+#include <utils/KeyedVector.h>
 #include <utils/SortedVector.h>
+#include <utils/StrongPointer.h>
 #include <utils/Vector.h>
 
+#include "DisplayDevice.h"
+
 namespace android {
 
 class BufferItem;
 class Layer;
 class SurfaceFlinger;
+struct ComposerState;
+struct DisplayDeviceState;
 struct DisplayState;
 struct layer_state_t;
 
 constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
 
+class SurfaceInterceptor {
+public:
+    virtual ~SurfaceInterceptor();
+
+    // Both vectors are used to capture the current state of SF as the initial snapshot in the trace
+    virtual void enable(const SortedVector<sp<Layer>>& layers,
+                        const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays) = 0;
+    virtual void disable() = 0;
+    virtual bool isEnabled() = 0;
+
+    // Intercept display and surface transactions
+    virtual void saveTransaction(
+            const Vector<ComposerState>& stateUpdates,
+            const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
+            const Vector<DisplayState>& changedDisplays, uint32_t flags) = 0;
+
+    // Intercept surface data
+    virtual void saveSurfaceCreation(const sp<const Layer>& layer) = 0;
+    virtual void saveSurfaceDeletion(const sp<const Layer>& layer) = 0;
+    virtual void saveBufferUpdate(const sp<const Layer>& layer, uint32_t width, uint32_t height,
+                                  uint64_t frameNumber) = 0;
+
+    // Intercept display data
+    virtual void saveDisplayCreation(const DisplayDeviceState& info) = 0;
+    virtual void saveDisplayDeletion(int32_t displayId) = 0;
+    virtual void savePowerModeUpdate(int32_t displayId, int32_t mode) = 0;
+    virtual void saveVSyncEvent(nsecs_t timestamp) = 0;
+};
+
+namespace impl {
+
 /*
  * SurfaceInterceptor intercepts and stores incoming streams of window
  * properties on SurfaceFlinger.
  */
-class SurfaceInterceptor {
+class SurfaceInterceptor final : public android::SurfaceInterceptor {
 public:
-    SurfaceInterceptor(SurfaceFlinger* const flinger);
+    explicit SurfaceInterceptor(SurfaceFlinger* const flinger);
+    ~SurfaceInterceptor() override = default;
+
     // Both vectors are used to capture the current state of SF as the initial snapshot in the trace
     void enable(const SortedVector<sp<Layer>>& layers,
-            const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays);
-    void disable();
-    bool isEnabled();
+                const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays) override;
+    void disable() override;
+    bool isEnabled() override;
 
     // Intercept display and surface transactions
     void saveTransaction(const Vector<ComposerState>& stateUpdates,
-            const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
-            const Vector<DisplayState>& changedDisplays, uint32_t flags);
+                         const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
+                         const Vector<DisplayState>& changedDisplays, uint32_t flags) override;
 
     // Intercept surface data
-    void saveSurfaceCreation(const sp<const Layer>& layer);
-    void saveSurfaceDeletion(const sp<const Layer>& layer);
+    void saveSurfaceCreation(const sp<const Layer>& layer) override;
+    void saveSurfaceDeletion(const sp<const Layer>& layer) override;
     void saveBufferUpdate(const sp<const Layer>& layer, uint32_t width, uint32_t height,
-            uint64_t frameNumber);
+                          uint64_t frameNumber) override;
 
     // Intercept display data
-    void saveDisplayCreation(const DisplayDeviceState& info);
-    void saveDisplayDeletion(int32_t displayId);
-    void savePowerModeUpdate(int32_t displayId, int32_t mode);
-    void saveVSyncEvent(nsecs_t timestamp);
+    void saveDisplayCreation(const DisplayDeviceState& info) override;
+    void saveDisplayDeletion(int32_t displayId) override;
+    void savePowerModeUpdate(int32_t displayId, int32_t mode) override;
+    void saveVSyncEvent(nsecs_t timestamp) override;
 
 private:
     // The creation increments of Surfaces and Displays do not contain enough information to capture
@@ -134,6 +175,7 @@
     SurfaceFlinger* const mFlinger;
 };
 
-}
+} // namespace impl
+} // namespace android
 
 #endif // ANDROID_SURFACEINTERCEPTOR_H
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index bf37e1e..47e5d1f 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #include <android-base/stringprintf.h>
 #include <layerproto/LayerProtoParser.h>
 #include <ui/DebugUtils.h>
@@ -24,7 +23,7 @@
 namespace android {
 namespace surfaceflinger {
 
-bool sortLayers(const LayerProtoParser::Layer* lhs, const LayerProtoParser::Layer* rhs) {
+bool sortLayers(LayerProtoParser::Layer* lhs, const LayerProtoParser::Layer* rhs) {
     uint32_t ls = lhs->layerStack;
     uint32_t rs = rhs->layerStack;
     if (ls != rs) return ls < rs;
@@ -38,20 +37,25 @@
     return lhs->id < rhs->id;
 }
 
-std::vector<const LayerProtoParser::Layer*> LayerProtoParser::generateLayerTree(
+bool sortLayerUniquePtrs(const std::unique_ptr<LayerProtoParser::Layer>& lhs,
+                   const std::unique_ptr<LayerProtoParser::Layer>& rhs) {
+    return sortLayers(lhs.get(), rhs.get());
+}
+
+std::vector<std::unique_ptr<LayerProtoParser::Layer>> LayerProtoParser::generateLayerTree(
         const LayersProto& layersProto) {
-    auto layerMap = generateMap(layersProto);
+    std::unordered_map<int32_t, LayerProtoParser::Layer*> layerMap = generateMap(layersProto);
+    std::vector<std::unique_ptr<LayerProtoParser::Layer>> layers;
 
-    std::vector<const Layer*> layers;
-    std::for_each(layerMap.begin(), layerMap.end(),
-                  [&](const std::pair<const int32_t, Layer*>& ref) {
-                      if (ref.second->parent == nullptr) {
-                          // only save top level layers
-                          layers.push_back(ref.second);
-                      }
-                  });
+    for (std::pair<int32_t, Layer*> kv : layerMap) {
+        if (kv.second->parent == nullptr) {
+            // Make unique_ptr for top level layers since they are not children. This ensures there
+            // will only be one unique_ptr made for each layer.
+            layers.push_back(std::unique_ptr<Layer>(kv.second));
+        }
+    }
 
-    std::sort(layers.begin(), layers.end(), sortLayers);
+    std::sort(layers.begin(), layers.end(), sortLayerUniquePtrs);
     return layers;
 }
 
@@ -102,6 +106,8 @@
     layer->activeBuffer = generateActiveBuffer(layerProto.active_buffer());
     layer->queuedFrames = layerProto.queued_frames();
     layer->refreshPending = layerProto.refresh_pending();
+    layer->windowType = layerProto.window_type();
+    layer->appId = layerProto.app_id();
 
     return layer;
 }
@@ -155,63 +161,61 @@
 
     for (int i = 0; i < layerProto.children_size(); i++) {
         if (layerMap.count(layerProto.children(i)) > 0) {
-            auto childLayer = layerMap[layerProto.children(i)];
-            currLayer->children.push_back(childLayer);
+            // Only make unique_ptrs for children since they are guaranteed to be unique, only one
+            // parent per child. This ensures there will only be one unique_ptr made for each layer.
+            currLayer->children.push_back(std::unique_ptr<Layer>(layerMap[layerProto.children(i)]));
         }
     }
 
     for (int i = 0; i < layerProto.relatives_size(); i++) {
         if (layerMap.count(layerProto.relatives(i)) > 0) {
-            auto relativeLayer = layerMap[layerProto.relatives(i)];
-            currLayer->relatives.push_back(relativeLayer);
+            currLayer->relatives.push_back(layerMap[layerProto.relatives(i)]);
         }
     }
 
     if (layerProto.has_parent()) {
         if (layerMap.count(layerProto.parent()) > 0) {
-            auto parentLayer = layerMap[layerProto.parent()];
-            currLayer->parent = parentLayer;
+            currLayer->parent = layerMap[layerProto.parent()];
         }
     }
 
     if (layerProto.has_z_order_relative_of()) {
         if (layerMap.count(layerProto.z_order_relative_of()) > 0) {
-            auto relativeLayer = layerMap[layerProto.z_order_relative_of()];
-            currLayer->zOrderRelativeOf = relativeLayer;
+            currLayer->zOrderRelativeOf = layerMap[layerProto.z_order_relative_of()];
         }
     }
 }
 
 std::string LayerProtoParser::layersToString(
-        const std::vector<const LayerProtoParser::Layer*> layers) {
+        std::vector<std::unique_ptr<LayerProtoParser::Layer>> layers) {
     std::string result;
-    for (const LayerProtoParser::Layer* layer : layers) {
+    for (std::unique_ptr<LayerProtoParser::Layer>& layer : layers) {
         if (layer->zOrderRelativeOf != nullptr) {
             continue;
         }
-        result.append(layerToString(layer).c_str());
+        result.append(layerToString(layer.get()).c_str());
     }
 
     return result;
 }
 
-std::string LayerProtoParser::layerToString(const LayerProtoParser::Layer* layer) {
+std::string LayerProtoParser::layerToString(LayerProtoParser::Layer* layer) {
     std::string result;
 
-    std::vector<const Layer*> traverse(layer->relatives);
-    for (const LayerProtoParser::Layer* child : layer->children) {
+    std::vector<Layer*> traverse(layer->relatives);
+    for (std::unique_ptr<LayerProtoParser::Layer>& child : layer->children) {
         if (child->zOrderRelativeOf != nullptr) {
             continue;
         }
 
-        traverse.push_back(child);
+        traverse.push_back(child.get());
     }
 
     std::sort(traverse.begin(), traverse.end(), sortLayers);
 
     size_t i = 0;
     for (; i < traverse.size(); i++) {
-        const auto& relative = traverse[i];
+        auto& relative = traverse[i];
         if (relative->z >= 0) {
             break;
         }
@@ -220,7 +224,7 @@
     result.append(layer->to_string().c_str());
     result.append("\n");
     for (; i < traverse.size(); i++) {
-        const auto& relative = traverse[i];
+        auto& relative = traverse[i];
         result.append(layerToString(relative).c_str());
     }
 
@@ -279,7 +283,8 @@
     StringAppendF(&result, "      zOrderRelativeOf=%s\n",
                   zOrderRelativeOf == nullptr ? "none" : zOrderRelativeOf->name.c_str());
     StringAppendF(&result, "      activeBuffer=%s,", activeBuffer.to_string().c_str());
-    StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d", queuedFrames, refreshPending);
+    StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d,", queuedFrames, refreshPending);
+    StringAppendF(&result, " windowType=%d, appId=%d", windowType, appId);
 
     return result;
 }
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index 78c6cd1..b56a6fb 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -18,6 +18,7 @@
 
 #include <math/vec4.h>
 
+#include <memory>
 #include <unordered_map>
 #include <vector>
 
@@ -68,8 +69,8 @@
     public:
         int32_t id;
         std::string name;
-        std::vector<const Layer*> children;
-        std::vector<const Layer*> relatives;
+        std::vector<std::unique_ptr<Layer>> children;
+        std::vector<Layer*> relatives;
         std::string type;
         LayerProtoParser::Region transparentRegion;
         LayerProtoParser::Region visibleRegion;
@@ -95,12 +96,14 @@
         LayerProtoParser::ActiveBuffer activeBuffer;
         int32_t queuedFrames;
         bool refreshPending;
+        int32_t windowType;
+        int32_t appId;
 
         std::string to_string() const;
     };
 
-    static std::vector<const Layer*> generateLayerTree(const LayersProto& layersProto);
-    static std::string layersToString(const std::vector<const LayerProtoParser::Layer*> layers);
+    static std::vector<std::unique_ptr<Layer>> generateLayerTree(const LayersProto& layersProto);
+    static std::string layersToString(std::vector<std::unique_ptr<LayerProtoParser::Layer>> layers);
 
 private:
     static std::unordered_map<int32_t, Layer*> generateMap(const LayersProto& layersProto);
@@ -113,7 +116,7 @@
     static void updateChildrenAndRelative(const LayerProto& layerProto,
                                           std::unordered_map<int32_t, Layer*>& layerMap);
 
-    static std::string layerToString(const LayerProtoParser::Layer* layer);
+    static std::string layerToString(LayerProtoParser::Layer* layer);
 };
 
 } // namespace surfaceflinger
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index d27dc9b..f18386b 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -64,6 +64,8 @@
   // The number of frames available.
   optional int32 queued_frames = 28;
   optional bool refresh_pending = 29;
+  optional int32 window_type = 30;
+  optional int32 app_id = 31;
 }
 
 message PositionProto {
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 7d3da32..7523399 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -33,6 +33,7 @@
         "libEGL",
         "libGLESv2",
         "libgui",
+        "liblayers_proto",
         "liblog",
         "libprotobuf-cpp-full",
         "libui",
diff --git a/services/surfaceflinger/tests/Stress_test.cpp b/services/surfaceflinger/tests/Stress_test.cpp
index 33dd2f5..4577153 100644
--- a/services/surfaceflinger/tests/Stress_test.cpp
+++ b/services/surfaceflinger/tests/Stress_test.cpp
@@ -22,7 +22,7 @@
 
 #include <thread>
 #include <functional>
-
+#include <layerproto/LayerProtoParser.h>
 
 namespace android {
 
@@ -47,4 +47,66 @@
     }
 }
 
+surfaceflinger::LayersProto generateLayerProto() {
+    surfaceflinger::LayersProto layersProto;
+    std::array<surfaceflinger::LayerProto*, 10> layers = {};
+    for (size_t i = 0; i < layers.size(); ++i) {
+        layers[i] = layersProto.add_layers();
+        layers[i]->set_id(i);
+    }
+
+    layers[0]->add_children(1);
+    layers[1]->set_parent(0);
+    layers[0]->add_children(2);
+    layers[2]->set_parent(0);
+    layers[0]->add_children(3);
+    layers[3]->set_parent(0);
+    layers[2]->add_children(4);
+    layers[4]->set_parent(2);
+    layers[3]->add_children(5);
+    layers[5]->set_parent(3);
+    layers[5]->add_children(6);
+    layers[6]->set_parent(5);
+    layers[5]->add_children(7);
+    layers[7]->set_parent(5);
+    layers[6]->add_children(8);
+    layers[8]->set_parent(6);
+
+    layers[4]->set_z_order_relative_of(3);
+    layers[3]->add_relatives(4);
+    layers[8]->set_z_order_relative_of(9);
+    layers[9]->add_relatives(8);
+    layers[3]->set_z_order_relative_of(1);
+    layers[1]->add_relatives(3);
+
+/* ----------------------------
+ *       - 0 -      - 9 -
+ *      /  |  \
+ *     1   2   3(1)
+ *         |    |
+ *         4(3) 5
+ *             / \
+ *            6   7
+ *            |
+ *            8(9)
+ * -------------------------- */
+
+    return layersProto;
+}
+
+TEST(LayerProtoStress, mem_info) {
+    std::string cmd = "dumpsys meminfo ";
+    cmd += std::to_string(getpid());
+    system(cmd.c_str());
+    for (int i = 0; i < 100000; i++) {
+        surfaceflinger::LayersProto layersProto = generateLayerProto();
+        auto layerTree = surfaceflinger::LayerProtoParser::generateLayerTree(layersProto);
+        // Allow some layerTrees to just fall out of scope (instead of std::move)
+        if (i % 2) {
+            surfaceflinger::LayerProtoParser::layersToString(std::move(layerTree));
+        }
+    }
+    system(cmd.c_str());
+}
+
 }
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index fd21991..4e9db72 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -2384,6 +2384,17 @@
     verify();
 }
 
+TEST_F(ScreenCaptureChildOnlyTest, RegressionTest76099859) {
+    SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true);
+
+    // Even though the parent is hidden we should still capture the child.
+    verify();
+
+    // Verify everything was properly hidden when rendering the full-screen.
+    screenshot()->expectBGColor(0,0);
+}
+
+
 TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
     auto fgHandle = mFGSurfaceControl->getHandle();
 
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 353b245..8ffc5ad 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -21,10 +21,16 @@
         ":libsurfaceflinger_sources",
         "DisplayTransactionTest.cpp",
         "MockComposer.cpp",
+        "MockDisplaySurface.cpp",
+        "MockEventControlThread.cpp",
         "MockEventThread.cpp",
         "MockGraphicBufferConsumer.cpp",
         "MockGraphicBufferProducer.cpp",
+        "MockMessageQueue.cpp",
+        "MockNativeWindow.cpp",
+        "MockNativeWindowSurface.cpp",
         "MockRenderEngine.cpp",
+        "MockSurfaceInterceptor.cpp",
     ],
     static_libs: [
         "libgmock",
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 21590df..c048c58 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -21,12 +21,19 @@
 #include <gtest/gtest.h>
 
 #include <log/log.h>
+#include "system/window.h"
 
 #include "MockComposer.h"
+#include "MockDisplaySurface.h"
+#include "MockEventControlThread.h"
 #include "MockEventThread.h"
 #include "MockGraphicBufferConsumer.h"
 #include "MockGraphicBufferProducer.h"
+#include "MockMessageQueue.h"
+#include "MockNativeWindow.h"
+#include "MockNativeWindowSurface.h"
 #include "MockRenderEngine.h"
+#include "MockSurfaceInterceptor.h"
 #include "TestableSurfaceFlinger.h"
 
 namespace android {
@@ -39,36 +46,119 @@
 using testing::Return;
 using testing::SetArgPointee;
 
+using android::hardware::graphics::common::V1_0::ColorMode;
 using android::hardware::graphics::common::V1_0::Hdr;
 using android::Hwc2::Error;
 using android::Hwc2::IComposer;
 using android::Hwc2::IComposerClient;
 
+using HWC2Display = TestableSurfaceFlinger::HWC2Display;
+using HotplugEvent = TestableSurfaceFlinger::HotplugEvent;
+
 constexpr int32_t DEFAULT_REFRESH_RATE = 1666666666;
 constexpr int32_t DEFAULT_DPI = 320;
 
+constexpr int DEFAULT_CONFIG_ID = 0;
+
 class DisplayTransactionTest : public testing::Test {
 protected:
     DisplayTransactionTest();
     ~DisplayTransactionTest() override;
 
-    void setupComposer(int virtualDisplayCount);
-    void setupPrimaryDisplay(int width, int height);
+    // --------------------------------------------------------------------
+    // Precondition helpers
 
-    void expectFramebufferQueuePairCreation(int width, int height);
+    void setupComposer(int virtualDisplayCount);
+    void setupFakeHwcDisplay(hwc2_display_t displayId, DisplayDevice::DisplayType type, int width,
+                             int height);
+
+    struct FakeDisplayDeviceFactory {
+    public:
+        FakeDisplayDeviceFactory(TestableSurfaceFlinger& flinger, sp<BBinder>& displayToken,
+                                 DisplayDevice::DisplayType type, int hwcId)
+              : mFlinger(flinger), mDisplayToken(displayToken), mType(type), mHwcId(hwcId) {}
+
+        sp<DisplayDevice> build() {
+            return new DisplayDevice(mFlinger.mFlinger.get(), mType, mHwcId, false, mDisplayToken,
+                                     mNativeWindow, mDisplaySurface, std::move(mRenderSurface), 0,
+                                     0, false, false, HWC_POWER_MODE_NORMAL);
+        }
+
+        FakeDisplayDeviceFactory& setNativeWindow(const sp<ANativeWindow>& nativeWindow) {
+            mNativeWindow = nativeWindow;
+            return *this;
+        }
+
+        FakeDisplayDeviceFactory& setDisplaySurface(const sp<DisplaySurface>& displaySurface) {
+            mDisplaySurface = displaySurface;
+            return *this;
+        }
+
+        FakeDisplayDeviceFactory& setRenderSurface(std::unique_ptr<RE::Surface> renderSurface) {
+            mRenderSurface = std::move(renderSurface);
+            return *this;
+        }
+
+        TestableSurfaceFlinger& mFlinger;
+        sp<BBinder>& mDisplayToken;
+        DisplayDevice::DisplayType mType;
+        int mHwcId;
+        sp<ANativeWindow> mNativeWindow;
+        sp<DisplaySurface> mDisplaySurface;
+        std::unique_ptr<RE::Surface> mRenderSurface;
+    };
+
+    sp<BBinder> setupFakeExistingPhysicalDisplay(hwc2_display_t displayId,
+                                                 DisplayDevice::DisplayType type);
+
+    void setupFakeBufferQueueFactory();
+    void setupFakeNativeWindowSurfaceFactory(int displayWidth, int displayHeight, bool critical,
+                                             bool async);
+    void expectFramebufferUsageSet(int width, int height, int grallocUsage);
+    void expectHwcHotplugCalls(hwc2_display_t displayId, int displayWidth, int displayHeight);
+
+    // --------------------------------------------------------------------
+    // Call expectation helpers
+
+    void expectRESurfaceCreationCalls();
+    void expectPhysicalDisplayDeviceCreationCalls(hwc2_display_t displayId, int displayWidth,
+                                                  int displayHeight, bool critical, bool async);
+
+    // --------------------------------------------------------------------
+    // Postcondition helpers
+
+    bool hasTransactionFlagSet(int flag);
+    bool hasDisplayDevice(sp<IBinder> displayToken);
+    sp<DisplayDevice> getDisplayDevice(sp<IBinder> displayToken);
+    bool hasCurrentDisplayState(sp<IBinder> displayToken);
+    const DisplayDeviceState& getCurrentDisplayState(sp<IBinder> displayToken);
+    bool hasDrawingDisplayState(sp<IBinder> displayToken);
+    const DisplayDeviceState& getDrawingDisplayState(sp<IBinder> displayToken);
+
+    // --------------------------------------------------------------------
+    // Test instances
+
+    std::unordered_set<HWC2::Capability> mCapabilities;
 
     TestableSurfaceFlinger mFlinger;
     mock::EventThread* mEventThread = new mock::EventThread();
+    mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
 
     // These mocks are created by the test, but are destroyed by SurfaceFlinger
     // by virtue of being stored into a std::unique_ptr. However we still need
     // to keep a reference to them for use in setting up call expectations.
     RE::mock::RenderEngine* mRenderEngine = new RE::mock::RenderEngine();
     Hwc2::mock::Composer* mComposer = new Hwc2::mock::Composer();
+    mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+    mock::SurfaceInterceptor* mSurfaceInterceptor = new mock::SurfaceInterceptor();
 
     // These mocks are created only when expected to be created via a factory.
     sp<mock::GraphicBufferConsumer> mConsumer;
     sp<mock::GraphicBufferProducer> mProducer;
+    mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
+    sp<mock::NativeWindow> mNativeWindow;
+    RE::mock::Surface* mRenderSurface = nullptr;
+    std::vector<std::unique_ptr<HWC2Display>> mFakeHwcDisplays;
 };
 
 DisplayTransactionTest::DisplayTransactionTest() {
@@ -80,8 +170,16 @@
         ADD_FAILURE() << "Unexpected request to create a buffer queue.";
     });
 
+    mFlinger.setCreateNativeWindowSurface([](auto) {
+        ADD_FAILURE() << "Unexpected request to create a native window surface.";
+        return nullptr;
+    });
+
+    mFlinger.mutableEventControlThread().reset(mEventControlThread);
     mFlinger.mutableEventThread().reset(mEventThread);
+    mFlinger.mutableEventQueue().reset(mMessageQueue);
     mFlinger.setupRenderEngine(std::unique_ptr<RE::RenderEngine>(mRenderEngine));
+    mFlinger.mutableInterceptor().reset(mSurfaceInterceptor);
 
     setupComposer(0);
 }
@@ -101,40 +199,49 @@
     Mock::VerifyAndClear(mComposer);
 }
 
-void DisplayTransactionTest::setupPrimaryDisplay(int width, int height) {
-    EXPECT_CALL(*mComposer, getDisplayType(DisplayDevice::DISPLAY_PRIMARY, _))
-            .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
-                            Return(Error::NONE)));
-    EXPECT_CALL(*mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
-    EXPECT_CALL(*mComposer, getDisplayConfigs(_, _))
-            .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{0}), Return(Error::NONE)));
-    EXPECT_CALL(*mComposer,
-                getDisplayAttribute(DisplayDevice::DISPLAY_PRIMARY, 0,
-                                    IComposerClient::Attribute::WIDTH, _))
-            .WillOnce(DoAll(SetArgPointee<3>(width), Return(Error::NONE)));
-    EXPECT_CALL(*mComposer,
-                getDisplayAttribute(DisplayDevice::DISPLAY_PRIMARY, 0,
-                                    IComposerClient::Attribute::HEIGHT, _))
-            .WillOnce(DoAll(SetArgPointee<3>(height), Return(Error::NONE)));
-    EXPECT_CALL(*mComposer,
-                getDisplayAttribute(DisplayDevice::DISPLAY_PRIMARY, 0,
-                                    IComposerClient::Attribute::VSYNC_PERIOD, _))
-            .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE)));
-    EXPECT_CALL(*mComposer,
-                getDisplayAttribute(DisplayDevice::DISPLAY_PRIMARY, 0,
-                                    IComposerClient::Attribute::DPI_X, _))
-            .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
-    EXPECT_CALL(*mComposer,
-                getDisplayAttribute(DisplayDevice::DISPLAY_PRIMARY, 0,
-                                    IComposerClient::Attribute::DPI_Y, _))
-            .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+void DisplayTransactionTest::setupFakeHwcDisplay(hwc2_display_t displayId,
+                                                 DisplayDevice::DisplayType type, int width,
+                                                 int height) {
+    auto display = std::make_unique<HWC2Display>(*mComposer, mCapabilities, displayId,
+                                                 HWC2::DisplayType::Physical);
+    display->mutableIsConnected() = true;
+    display->mutableConfigs().emplace(DEFAULT_CONFIG_ID,
+                                      HWC2::Display::Config::Builder(*display, DEFAULT_CONFIG_ID)
+                                              .setWidth(width)
+                                              .setHeight(height)
+                                              .setVsyncPeriod(DEFAULT_REFRESH_RATE)
+                                              .setDpiX(DEFAULT_DPI)
+                                              .setDpiY(DEFAULT_DPI)
+                                              .build());
 
-    mFlinger.setupPrimaryDisplay();
+    mFlinger.mutableHwcDisplayData()[type].reset();
+    mFlinger.mutableHwcDisplayData()[type].hwcDisplay = display.get();
+    mFlinger.mutableHwcDisplaySlots().emplace(displayId, type);
 
-    Mock::VerifyAndClear(mComposer);
+    mFakeHwcDisplays.push_back(std::move(display));
 }
 
-void DisplayTransactionTest::expectFramebufferQueuePairCreation(int width, int height) {
+sp<BBinder> DisplayTransactionTest::setupFakeExistingPhysicalDisplay(
+        hwc2_display_t displayId, DisplayDevice::DisplayType type) {
+    setupFakeHwcDisplay(displayId, type, 0, 0);
+
+    sp<BBinder> displayToken = new BBinder();
+    mFlinger.mutableBuiltinDisplays()[type] = displayToken;
+    mFlinger.mutableDisplays()
+            .add(displayToken,
+                 FakeDisplayDeviceFactory(mFlinger, displayToken, type, type).build());
+
+    DisplayDeviceState state(type, true);
+    mFlinger.mutableCurrentState().displays.add(displayToken, state);
+    mFlinger.mutableDrawingState().displays.add(displayToken, state);
+
+    return displayToken;
+}
+
+void DisplayTransactionTest::setupFakeBufferQueueFactory() {
+    // This setup is only expected once per test.
+    ASSERT_TRUE(mConsumer == nullptr && mProducer == nullptr);
+
     mConsumer = new mock::GraphicBufferConsumer();
     mProducer = new mock::GraphicBufferProducer();
 
@@ -142,64 +249,187 @@
         *outProducer = mProducer;
         *outConsumer = mConsumer;
     });
+}
 
+void DisplayTransactionTest::setupFakeNativeWindowSurfaceFactory(int displayWidth,
+                                                                 int displayHeight, bool critical,
+                                                                 bool async) {
+    // This setup is only expected once per test.
+    ASSERT_TRUE(mNativeWindowSurface == nullptr);
+
+    mNativeWindowSurface = new mock::NativeWindowSurface();
+    mNativeWindow = new mock::NativeWindow();
+
+    mFlinger.setCreateNativeWindowSurface(
+            [this](auto) { return std::unique_ptr<NativeWindowSurface>(mNativeWindowSurface); });
+
+    EXPECT_CALL(*mNativeWindowSurface, getNativeWindow()).WillOnce(Return(mNativeWindow));
+
+    EXPECT_CALL(*mNativeWindow, perform(19)).Times(1);
+
+    EXPECT_CALL(*mRenderSurface, setAsync(async)).Times(1);
+    EXPECT_CALL(*mRenderSurface, setCritical(critical)).Times(1);
+    EXPECT_CALL(*mRenderSurface, setNativeWindow(mNativeWindow.get())).Times(1);
+    EXPECT_CALL(*mRenderSurface, queryWidth()).WillOnce(Return(displayWidth));
+    EXPECT_CALL(*mRenderSurface, queryHeight()).WillOnce(Return(displayHeight));
+}
+
+void DisplayTransactionTest::expectFramebufferUsageSet(int width, int height, int grallocUsage) {
     EXPECT_CALL(*mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mConsumer, setConsumerName(_)).WillRepeatedly(Return(NO_ERROR));
-    EXPECT_CALL(*mConsumer,
-                setConsumerUsageBits(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER |
-                                     GRALLOC_USAGE_HW_FB))
-            .WillRepeatedly(Return(NO_ERROR));
+    EXPECT_CALL(*mConsumer, setConsumerUsageBits(grallocUsage)).WillRepeatedly(Return(NO_ERROR));
     EXPECT_CALL(*mConsumer, setDefaultBufferSize(width, height)).WillRepeatedly(Return(NO_ERROR));
     EXPECT_CALL(*mConsumer, setMaxAcquiredBufferCount(_)).WillRepeatedly(Return(NO_ERROR));
 
     EXPECT_CALL(*mProducer, allocateBuffers(0, 0, 0, 0)).WillRepeatedly(Return());
 }
 
-TEST_F(DisplayTransactionTest, processDisplayChangesLockedProcessesPrimaryDisplayConnected) {
-    using android::hardware::graphics::common::V1_0::ColorMode;
+void DisplayTransactionTest::expectHwcHotplugCalls(hwc2_display_t displayId, int displayWidth,
+                                                   int displayHeight) {
+    EXPECT_CALL(*mComposer, getDisplayType(displayId, _))
+            .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
+                            Return(Error::NONE)));
+    EXPECT_CALL(*mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
+    EXPECT_CALL(*mComposer, getDisplayConfigs(_, _))
+            .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{0}), Return(Error::NONE)));
+    EXPECT_CALL(*mComposer, getDisplayAttribute(displayId, 0, IComposerClient::Attribute::WIDTH, _))
+            .WillOnce(DoAll(SetArgPointee<3>(displayWidth), Return(Error::NONE)));
+    EXPECT_CALL(*mComposer,
+                getDisplayAttribute(displayId, 0, IComposerClient::Attribute::HEIGHT, _))
+            .WillOnce(DoAll(SetArgPointee<3>(displayHeight), Return(Error::NONE)));
+    EXPECT_CALL(*mComposer,
+                getDisplayAttribute(displayId, 0, IComposerClient::Attribute::VSYNC_PERIOD, _))
+            .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE)));
+    EXPECT_CALL(*mComposer, getDisplayAttribute(displayId, 0, IComposerClient::Attribute::DPI_X, _))
+            .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+    EXPECT_CALL(*mComposer, getDisplayAttribute(displayId, 0, IComposerClient::Attribute::DPI_Y, _))
+            .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+}
 
-    setupPrimaryDisplay(1920, 1080);
+void DisplayTransactionTest::expectRESurfaceCreationCalls() {
+    // This setup is only expected once per test.
+    ASSERT_TRUE(mRenderSurface == nullptr);
 
-    sp<BBinder> token = new BBinder();
-    mFlinger.mutableCurrentState().displays.add(token, {DisplayDevice::DISPLAY_PRIMARY, true});
+    mRenderSurface = new RE::mock::Surface();
+    EXPECT_CALL(*mRenderEngine, createSurface())
+            .WillOnce(Return(ByMove(std::unique_ptr<RE::Surface>(mRenderSurface))));
+}
 
-    EXPECT_CALL(*mComposer, getActiveConfig(DisplayDevice::DISPLAY_PRIMARY, _))
-            .WillOnce(DoAll(SetArgPointee<1>(0), Return(Error::NONE)));
-    EXPECT_CALL(*mComposer, getColorModes(DisplayDevice::DISPLAY_PRIMARY, _)).Times(0);
-    EXPECT_CALL(*mComposer, getHdrCapabilities(DisplayDevice::DISPLAY_PRIMARY, _, _, _, _))
+void DisplayTransactionTest::expectPhysicalDisplayDeviceCreationCalls(hwc2_display_t displayId,
+                                                                      int displayWidth,
+                                                                      int displayHeight,
+                                                                      bool critical, bool async) {
+    EXPECT_CALL(*mComposer, getActiveConfig(displayId, _))
+            .WillOnce(DoAll(SetArgPointee<1>(DEFAULT_CONFIG_ID), Return(Error::NONE)));
+    EXPECT_CALL(*mComposer, getColorModes(displayId, _)).Times(0);
+    EXPECT_CALL(*mComposer, getHdrCapabilities(displayId, _, _, _, _))
             .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>()), Return(Error::NONE)));
 
-    expectFramebufferQueuePairCreation(1920, 1080);
+    setupFakeBufferQueueFactory();
+    expectFramebufferUsageSet(displayWidth, displayHeight,
+                              GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER |
+                                      GRALLOC_USAGE_HW_FB);
 
-    auto reSurface = new RE::mock::Surface();
-    EXPECT_CALL(*mRenderEngine, createSurface())
-            .WillOnce(Return(ByMove(std::unique_ptr<RE::Surface>(reSurface))));
-    EXPECT_CALL(*reSurface, setAsync(false)).Times(1);
-    EXPECT_CALL(*reSurface, setCritical(true)).Times(1);
-    EXPECT_CALL(*reSurface, setNativeWindow(_)).Times(1);
-    EXPECT_CALL(*reSurface, queryWidth()).WillOnce(Return(1920));
-    EXPECT_CALL(*reSurface, queryHeight()).WillOnce(Return(1080));
+    setupFakeNativeWindowSurfaceFactory(displayWidth, displayHeight, critical, async);
+}
+
+bool DisplayTransactionTest::hasTransactionFlagSet(int flag) {
+    return mFlinger.mutableTransactionFlags() & flag;
+}
+
+bool DisplayTransactionTest::hasDisplayDevice(sp<IBinder> displayToken) {
+    return mFlinger.mutableDisplays().indexOfKey(displayToken) >= 0;
+}
+
+sp<DisplayDevice> DisplayTransactionTest::getDisplayDevice(sp<IBinder> displayToken) {
+    return mFlinger.mutableDisplays().valueFor(displayToken);
+}
+
+bool DisplayTransactionTest::hasCurrentDisplayState(sp<IBinder> displayToken) {
+    return mFlinger.mutableCurrentState().displays.indexOfKey(displayToken) >= 0;
+}
+
+const DisplayDeviceState& DisplayTransactionTest::getCurrentDisplayState(sp<IBinder> displayToken) {
+    return mFlinger.mutableCurrentState().displays.valueFor(displayToken);
+}
+
+bool DisplayTransactionTest::hasDrawingDisplayState(sp<IBinder> displayToken) {
+    return mFlinger.mutableDrawingState().displays.indexOfKey(displayToken) >= 0;
+}
+
+const DisplayDeviceState& DisplayTransactionTest::getDrawingDisplayState(sp<IBinder> displayToken) {
+    return mFlinger.mutableDrawingState().displays.valueFor(displayToken);
+}
+
+/* ------------------------------------------------------------------------
+ * SurfaceFlinger::handleTransactionLocked(eDisplayTransactionNeeded)
+ */
+
+TEST_F(DisplayTransactionTest, handleTransactionLockedProcessesHotplugConnectPrimary) {
+    constexpr hwc2_display_t externalDisplayId = 102;
+    constexpr hwc2_display_t displayId = 123;
+    constexpr int displayWidth = 1920;
+    constexpr int displayHeight = 1080;
+
+    // --------------------------------------------------------------------
+    // Preconditions
+
+    // An external display may already be set up
+    setupFakeHwcDisplay(externalDisplayId, DisplayDevice::DISPLAY_EXTERNAL, 3840, 2160);
+
+    // A hotplug connect comes in for a new display
+    mFlinger.mutablePendingHotplugEvents().emplace_back(
+            HotplugEvent{displayId, HWC2::Connection::Connected});
+
+    // --------------------------------------------------------------------
+    // Call Expectations
+
+    EXPECT_CALL(*mComposer, isUsingVrComposer()).WillOnce(Return(false));
+    expectHwcHotplugCalls(displayId, displayWidth, displayHeight);
+    expectRESurfaceCreationCalls();
+    expectPhysicalDisplayDeviceCreationCalls(displayId, displayWidth, displayHeight, true, false);
+
+    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
 
     EXPECT_CALL(*mEventThread, onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, true)).Times(1);
 
-    mFlinger.processDisplayChangesLocked();
+    // --------------------------------------------------------------------
+    // Invocation
 
-    ASSERT_TRUE(mFlinger.mutableDisplays().indexOfKey(token) >= 0);
+    mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
 
-    const auto& device = mFlinger.mutableDisplays().valueFor(token);
-    ASSERT_TRUE(device.get());
+    // --------------------------------------------------------------------
+    // Postconditions
+
+    // HWComposer should have an entry for the display
+    EXPECT_TRUE(mFlinger.mutableHwcDisplaySlots().count(displayId) == 1);
+
+    // The display should have set up as a primary built-in display.
+    auto displayToken = mFlinger.mutableBuiltinDisplays()[DisplayDevice::DISPLAY_PRIMARY];
+    ASSERT_TRUE(displayToken != nullptr);
+
+    // The display device should have been set up in the list of displays.
+    ASSERT_TRUE(hasDisplayDevice(displayToken));
+    const auto& device = getDisplayDevice(displayToken);
     EXPECT_TRUE(device->isSecure());
     EXPECT_TRUE(device->isPrimary());
 
-    ssize_t i = mFlinger.mutableDrawingState().displays.indexOfKey(token);
-    ASSERT_GE(0, i);
-    const auto& draw = mFlinger.mutableDrawingState().displays[i];
+    // The display should have been set up in the current display state
+    ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+    const auto& current = getCurrentDisplayState(displayToken);
+    EXPECT_EQ(DisplayDevice::DISPLAY_PRIMARY, current.type);
+
+    // The display should have been set up in the drawing display state
+    ASSERT_TRUE(hasDrawingDisplayState(displayToken));
+    const auto& draw = getDrawingDisplayState(displayToken);
     EXPECT_EQ(DisplayDevice::DISPLAY_PRIMARY, draw.type);
 
-    EXPECT_CALL(*mComposer, setVsyncEnabled(0, IComposerClient::Vsync::DISABLE))
-            .WillOnce(Return(Error::NONE));
+    // --------------------------------------------------------------------
+    // Cleanup conditions
 
-    EXPECT_CALL(*mConsumer, consumerDisconnect()).Times(1);
+    EXPECT_CALL(*mComposer, setVsyncEnabled(displayId, IComposerClient::Vsync::DISABLE))
+            .WillOnce(Return(Error::NONE));
+    EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/MockDisplaySurface.cpp b/services/surfaceflinger/tests/unittests/MockDisplaySurface.cpp
new file mode 100644
index 0000000..507626b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MockDisplaySurface.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MockDisplaySurface.h"
+
+namespace android {
+namespace mock {
+
+// Explicit default instantiation is recommended.
+DisplaySurface::DisplaySurface() = default;
+DisplaySurface::~DisplaySurface() = default;
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/MockDisplaySurface.h b/services/surfaceflinger/tests/unittests/MockDisplaySurface.h
new file mode 100644
index 0000000..d6c9aa4
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MockDisplaySurface.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include <utils/String8.h>
+
+#include "DisplayHardware/DisplaySurface.h"
+
+namespace android {
+namespace mock {
+
+class DisplaySurface : public android::DisplaySurface {
+public:
+    DisplaySurface();
+    ~DisplaySurface() override;
+
+    MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
+    MOCK_METHOD1(prepareFrame, status_t(CompositionType compositionType));
+    MOCK_METHOD0(advanceFrame, status_t());
+    MOCK_METHOD0(onFrameCommitted, void());
+    MOCK_CONST_METHOD1(dumpAsString, void(String8& result));
+    MOCK_METHOD2(resizeBuffers, void(uint32_t, uint32_t));
+    MOCK_CONST_METHOD0(getClientTargetAcquireFence, const sp<Fence>&());
+};
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/MockEventControlThread.cpp b/services/surfaceflinger/tests/unittests/MockEventControlThread.cpp
new file mode 100644
index 0000000..398fd42
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MockEventControlThread.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MockEventControlThread.h"
+
+namespace android {
+namespace mock {
+
+// Explicit default instantiation is recommended.
+EventControlThread::EventControlThread() = default;
+EventControlThread::~EventControlThread() = default;
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/MockEventControlThread.h b/services/surfaceflinger/tests/unittests/MockEventControlThread.h
new file mode 100644
index 0000000..8ac09a9
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MockEventControlThread.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "EventControlThread.h"
+
+namespace android {
+namespace mock {
+
+class EventControlThread : public android::EventControlThread {
+public:
+    EventControlThread();
+    ~EventControlThread() override;
+
+    MOCK_METHOD1(setVsyncEnabled, void(bool));
+};
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/MockMessageQueue.cpp b/services/surfaceflinger/tests/unittests/MockMessageQueue.cpp
new file mode 100644
index 0000000..62f45ed
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MockMessageQueue.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MockMessageQueue.h"
+
+namespace android {
+namespace mock {
+
+// Explicit default instantiation is recommended.
+MessageQueue::MessageQueue() = default;
+MessageQueue::~MessageQueue() = default;
+
+} // namespace mock
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/MockMessageQueue.h
new file mode 100644
index 0000000..cf07cf7
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MockMessageQueue.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "MessageQueue.h"
+
+namespace android {
+namespace mock {
+
+class MessageQueue : public android::MessageQueue {
+public:
+    MessageQueue();
+    ~MessageQueue() override;
+
+    MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
+    MOCK_METHOD1(setEventThread, void(android::EventThread*));
+    MOCK_METHOD0(waitMessage, void());
+    MOCK_METHOD2(postMessage, status_t(const sp<MessageBase>&, nsecs_t));
+    MOCK_METHOD0(invalidate, void());
+    MOCK_METHOD0(refresh, void());
+};
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/MockNativeWindow.cpp b/services/surfaceflinger/tests/unittests/MockNativeWindow.cpp
new file mode 100644
index 0000000..61038f4
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MockNativeWindow.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MockNativeWindow.h"
+
+namespace android {
+namespace mock {
+namespace {
+
+int dispatch_setSwapInterval(struct ANativeWindow* window, int interval) {
+    return static_cast<NativeWindow*>(window)->setSwapInterval(interval);
+}
+
+int dispatch_dequeueBuffer_DEPRECATED(struct ANativeWindow* window,
+                                      struct ANativeWindowBuffer** buffer) {
+    return static_cast<NativeWindow*>(window)->dequeueBuffer_DEPRECATED(buffer);
+}
+
+int dispatch_lockBuffer_DEPRECATED(struct ANativeWindow* window,
+                                   struct ANativeWindowBuffer* buffer) {
+    return static_cast<NativeWindow*>(window)->lockBuffer_DEPRECATED(buffer);
+}
+
+int dispatch_queueBuffer_DEPRECATED(struct ANativeWindow* window,
+                                    struct ANativeWindowBuffer* buffer) {
+    return static_cast<NativeWindow*>(window)->queueBuffer_DEPRECATED(buffer);
+}
+
+int dispatch_query(const struct ANativeWindow* window, int what, int* value) {
+    return static_cast<const NativeWindow*>(window)->query(what, value);
+}
+
+int dispatch_perform(struct ANativeWindow* window, int operation, ...) {
+    // TODO: Handle the various operations and their varargs better.
+    return static_cast<NativeWindow*>(window)->perform(operation);
+}
+
+int dispatch_cancelBuffer_DEPRECATED(struct ANativeWindow* window,
+                                     struct ANativeWindowBuffer* buffer) {
+    return static_cast<NativeWindow*>(window)->cancelBuffer_DEPRECATED(buffer);
+}
+
+int dispatch_dequeueBuffer(struct ANativeWindow* window, struct ANativeWindowBuffer** buffer,
+                           int* fenceFd) {
+    return static_cast<NativeWindow*>(window)->dequeueBuffer(buffer, fenceFd);
+}
+
+int dispatch_queueBuffer(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer,
+                         int fenceFd) {
+    return static_cast<NativeWindow*>(window)->queueBuffer(buffer, fenceFd);
+}
+
+int dispatch_cancelBuffer(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer,
+                          int fenceFd) {
+    return static_cast<NativeWindow*>(window)->cancelBuffer(buffer, fenceFd);
+}
+
+} // namespace
+
+NativeWindow::NativeWindow() {
+    // ANativeWindow is a structure with function pointers and not a C++ class.
+    // Set all the pointers to dispatch functions, which will invoke the mock
+    // interface functions.
+    ANativeWindow::setSwapInterval = &dispatch_setSwapInterval;
+    ANativeWindow::dequeueBuffer_DEPRECATED = &dispatch_dequeueBuffer_DEPRECATED;
+    ANativeWindow::lockBuffer_DEPRECATED = &dispatch_lockBuffer_DEPRECATED;
+    ANativeWindow::queueBuffer_DEPRECATED = &dispatch_queueBuffer_DEPRECATED;
+    ANativeWindow::query = &dispatch_query;
+    ANativeWindow::perform = &dispatch_perform;
+    ANativeWindow::cancelBuffer_DEPRECATED = &dispatch_cancelBuffer_DEPRECATED;
+    ANativeWindow::dequeueBuffer = &dispatch_dequeueBuffer;
+    ANativeWindow::queueBuffer = &dispatch_queueBuffer;
+    ANativeWindow::cancelBuffer = &dispatch_cancelBuffer;
+}
+
+// Explicit default instantiation is recommended.
+NativeWindow::~NativeWindow() = default;
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/MockNativeWindow.h b/services/surfaceflinger/tests/unittests/MockNativeWindow.h
new file mode 100644
index 0000000..561fd58
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MockNativeWindow.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include <system/window.h>
+
+#include <ui/ANativeObjectBase.h>
+
+namespace android {
+namespace mock {
+
+class NativeWindow : public ANativeObjectBase<ANativeWindow, NativeWindow, RefBase> {
+public:
+    NativeWindow();
+    ~NativeWindow();
+
+    MOCK_METHOD1(setSwapInterval, int(int interval));
+    MOCK_METHOD1(dequeueBuffer_DEPRECATED, int(struct ANativeWindowBuffer**));
+    MOCK_METHOD1(lockBuffer_DEPRECATED, int(struct ANativeWindowBuffer*));
+    MOCK_METHOD1(queueBuffer_DEPRECATED, int(struct ANativeWindowBuffer*));
+    MOCK_CONST_METHOD2(query, int(int, int*));
+    MOCK_METHOD1(perform, int(int));
+    MOCK_METHOD1(cancelBuffer_DEPRECATED, int(struct ANativeWindowBuffer*));
+    MOCK_METHOD2(dequeueBuffer, int(struct ANativeWindowBuffer**, int*));
+    MOCK_METHOD2(queueBuffer, int(struct ANativeWindowBuffer*, int));
+    MOCK_METHOD2(cancelBuffer, int(struct ANativeWindowBuffer*, int));
+};
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/MockNativeWindowSurface.cpp b/services/surfaceflinger/tests/unittests/MockNativeWindowSurface.cpp
new file mode 100644
index 0000000..0314568
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MockNativeWindowSurface.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MockNativeWindowSurface.h"
+
+namespace android {
+namespace mock {
+
+// Explicit default instantiation is recommended.
+NativeWindowSurface::NativeWindowSurface() = default;
+NativeWindowSurface::~NativeWindowSurface() = default;
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/MockNativeWindowSurface.h b/services/surfaceflinger/tests/unittests/MockNativeWindowSurface.h
new file mode 100644
index 0000000..88d1a9f
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MockNativeWindowSurface.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include <system/window.h> // for ANativeWindow
+
+#include "SurfaceFlinger.h" // for base NativeWindowSurface
+
+namespace android {
+namespace mock {
+
+class NativeWindowSurface : public android::NativeWindowSurface {
+public:
+    NativeWindowSurface();
+    ~NativeWindowSurface();
+
+    MOCK_CONST_METHOD0(getNativeWindow, sp<ANativeWindow>());
+    MOCK_METHOD0(preallocateBuffers, void());
+};
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/MockSurfaceInterceptor.cpp b/services/surfaceflinger/tests/unittests/MockSurfaceInterceptor.cpp
new file mode 100644
index 0000000..b2ec721
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MockSurfaceInterceptor.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MockSurfaceInterceptor.h"
+
+namespace android {
+namespace mock {
+
+// Explicit default instantiation is recommended.
+SurfaceInterceptor::SurfaceInterceptor() = default;
+SurfaceInterceptor::~SurfaceInterceptor() = default;
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/MockSurfaceInterceptor.h b/services/surfaceflinger/tests/unittests/MockSurfaceInterceptor.h
new file mode 100644
index 0000000..458b2f3
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MockSurfaceInterceptor.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "SurfaceInterceptor.h"
+
+namespace android {
+namespace mock {
+
+class SurfaceInterceptor : public android::SurfaceInterceptor {
+public:
+    SurfaceInterceptor();
+    ~SurfaceInterceptor() override;
+
+    MOCK_METHOD2(enable,
+                 void(const SortedVector<sp<Layer>>&,
+                      const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&));
+    MOCK_METHOD0(disable, void());
+    MOCK_METHOD0(isEnabled, bool());
+    MOCK_METHOD4(saveTransaction,
+                 void(const Vector<ComposerState>&,
+                      const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&,
+                      const Vector<DisplayState>&, uint32_t));
+    MOCK_METHOD1(saveSurfaceCreation, void(const sp<const Layer>&));
+    MOCK_METHOD1(saveSurfaceDeletion, void(const sp<const Layer>&));
+    MOCK_METHOD4(saveBufferUpdate, void(const sp<const Layer>&, uint32_t, uint32_t, uint64_t));
+    MOCK_METHOD1(saveDisplayCreation, void(const DisplayDeviceState&));
+    MOCK_METHOD1(saveDisplayDeletion, void(int32_t));
+    MOCK_METHOD2(savePowerModeUpdate, void(int32_t, int32_t));
+    MOCK_METHOD1(saveVSyncEvent, void(nsecs_t));
+};
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 4895e16..2dd8e5f 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -44,33 +44,44 @@
         mFlinger->getBE().mHwc.reset(new HWComposer(std::move(composer)));
     }
 
-    void setupPrimaryDisplay() {
-        mFlinger->getBE().mHwc->mHwcDevice->onHotplug(0, HWC2::Connection::Connected);
-        mFlinger->getBE().mHwc->onHotplug(0, DisplayDevice::DISPLAY_PRIMARY,
-                                          HWC2::Connection::Connected);
-    }
-
     using CreateBufferQueueFunction = SurfaceFlinger::CreateBufferQueueFunction;
-
     void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
         mFlinger->mCreateBufferQueue = f;
     }
 
+    using CreateNativeWindowSurfaceFunction = SurfaceFlinger::CreateNativeWindowSurfaceFunction;
+    void setCreateNativeWindowSurface(CreateNativeWindowSurfaceFunction f) {
+        mFlinger->mCreateNativeWindowSurface = f;
+    }
+
+    using HotplugEvent = SurfaceFlinger::HotplugEvent;
+
     /* ------------------------------------------------------------------------
      * Forwarding for functions being tested
      */
-    auto processDisplayChangesLocked() { return mFlinger->processDisplayChangesLocked(); }
+
+    auto handleTransactionLocked(uint32_t transactionFlags) {
+        return mFlinger->handleTransactionLocked(transactionFlags);
+    }
 
     /* ------------------------------------------------------------------------
      * Read-write access to private data to set up preconditions and assert
      * post-conditions.
      */
+
     auto& mutableBuiltinDisplays() { return mFlinger->mBuiltinDisplays; }
-    auto& mutableDisplays() { return mFlinger->mDisplays; }
     auto& mutableCurrentState() { return mFlinger->mCurrentState; }
+    auto& mutableDisplays() { return mFlinger->mDisplays; }
     auto& mutableDrawingState() { return mFlinger->mDrawingState; }
-    auto& mutableEventThread() { return mFlinger->mEventThread; }
+    auto& mutableEventControlThread() { return mFlinger->mEventControlThread; }
     auto& mutableEventQueue() { return mFlinger->mEventQueue; }
+    auto& mutableEventThread() { return mFlinger->mEventThread; }
+    auto& mutableInterceptor() { return mFlinger->mInterceptor; }
+    auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
+    auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
+
+    auto& mutableHwcDisplayData() { return mFlinger->getBE().mHwc->mDisplayData; }
+    auto& mutableHwcDisplaySlots() { return mFlinger->getBE().mHwc->mHwcDisplaySlots; }
 
     ~TestableSurfaceFlinger() {
         // All these pointer and container clears help ensure that GMock does
@@ -78,12 +89,33 @@
         // still be referenced by something despite our best efforts to destroy
         // it after each test is done.
         mutableDisplays().clear();
+        mutableEventControlThread().reset();
+        mutableEventQueue().reset();
         mutableEventThread().reset();
+        mutableInterceptor().reset();
         mFlinger->getBE().mHwc.reset();
         mFlinger->getBE().mRenderEngine.reset();
     }
 
-    sp<SurfaceFlinger> mFlinger = new SurfaceFlinger();
+    /* ------------------------------------------------------------------------
+     * Wrapper classes for Read-write access to private data to set up
+     * preconditions and assert post-conditions.
+     */
+    struct HWC2Display : public HWC2::Display {
+        HWC2Display(Hwc2::Composer& composer,
+                    const std::unordered_set<HWC2::Capability>& capabilities, hwc2_display_t id,
+                    HWC2::DisplayType type)
+              : HWC2::Display(composer, capabilities, id, type) {}
+        ~HWC2Display() {
+            // Prevents a call to disable vsyncs.
+            mType = HWC2::DisplayType::Invalid;
+        }
+
+        auto& mutableIsConnected() { return this->mIsConnected; }
+        auto& mutableConfigs() { return this->mConfigs; }
+    };
+
+    sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(SurfaceFlinger::SkipInitialization);
 };
 
 } // namespace android
diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index 6009a95..6122846 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -17,6 +17,7 @@
     "bufferhubd.cpp",
     "consumer_channel.cpp",
     "producer_channel.cpp",
+    "detached_buffer_channel.cpp",
     "consumer_queue_channel.cpp",
     "producer_queue_channel.cpp",
 ]
diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp
index 1eb4ef9..e4e19c7 100644
--- a/services/vr/bufferhubd/buffer_hub.cpp
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -100,6 +100,41 @@
       stream << std::setw(8) << info.index;
       stream << std::endl;
     }
+
+    if (channel->channel_type() == BufferHubChannel::kDetachedBufferType) {
+      BufferHubChannel::BufferInfo info = channel->GetBufferInfo();
+
+      stream << std::right;
+      stream << std::setw(6) << info.id;
+      stream << " ";
+      stream << std::setw(9) << "N/A";
+      stream << " ";
+      if (info.format == HAL_PIXEL_FORMAT_BLOB) {
+        std::string size = std::to_string(info.width) + " B";
+        stream << std::setw(14) << size;
+      } else {
+        std::string dimensions = std::to_string(info.width) + "x" +
+                                 std::to_string(info.height) + "x" +
+                                 std::to_string(info.layer_count);
+        stream << std::setw(14) << dimensions;
+      }
+      stream << " ";
+      stream << std::setw(6) << info.format;
+      stream << " ";
+      stream << "0x" << std::hex << std::setfill('0');
+      stream << std::setw(8) << info.usage;
+      stream << std::dec << std::setfill(' ');
+      stream << " ";
+      stream << std::setw(9) << "N/A";
+      stream << " ";
+      stream << std::hex << std::setfill(' ');
+      stream << std::setw(18) << "Detached";
+      stream << " ";
+      stream << std::setw(18) << "N/A";
+      stream << " ";
+      stream << std::setw(10) << "N/A";
+      stream << std::endl;
+    }
   }
 
   stream << std::endl;
@@ -215,6 +250,15 @@
           *this, &BufferHubService::OnCreateProducerQueue, message);
       return {};
 
+    case BufferHubRPC::ProducerBufferDetach::Opcode:
+      // In addition to the message handler in the ProducerChannel's
+      // HandleMessage method, we also need to invalid the producer channel (and
+      // all associated consumer channels). Note that this has to be done after
+      // HandleMessage returns to make sure the IPC request has went back to the
+      // client first.
+      SetChannel(channel->channel_id(), nullptr);
+      return {};
+
     default:
       return DefaultHandleMessage(message);
   }
diff --git a/services/vr/bufferhubd/buffer_hub.h b/services/vr/bufferhubd/buffer_hub.h
index b487b0b..e04967a 100644
--- a/services/vr/bufferhubd/buffer_hub.h
+++ b/services/vr/bufferhubd/buffer_hub.h
@@ -23,6 +23,7 @@
   enum ChannelType {
     kProducerType,
     kConsumerType,
+    kDetachedBufferType,
     kProducerQueueType,
     kConsumerQueueType,
   };
diff --git a/services/vr/bufferhubd/detached_buffer_channel.cpp b/services/vr/bufferhubd/detached_buffer_channel.cpp
new file mode 100644
index 0000000..edb2111
--- /dev/null
+++ b/services/vr/bufferhubd/detached_buffer_channel.cpp
@@ -0,0 +1,57 @@
+#include "detached_buffer_channel.h"
+
+using android::pdx::ErrorStatus;
+using android::pdx::Message;
+using android::pdx::RemoteChannelHandle;
+using android::pdx::Status;
+using android::pdx::rpc::DispatchRemoteMethod;
+
+namespace android {
+namespace dvr {
+
+DetachedBufferChannel::DetachedBufferChannel(BufferHubService* service,
+                                             int buffer_id, int channel_id,
+                                             IonBuffer buffer,
+                                             IonBuffer metadata_buffer,
+                                             size_t user_metadata_size)
+    : BufferHubChannel(service, buffer_id, channel_id, kDetachedBufferType),
+      buffer_(std::move(buffer)),
+      metadata_buffer_(std::move(metadata_buffer)),
+      user_metadata_size_(user_metadata_size) {}
+
+BufferHubChannel::BufferInfo DetachedBufferChannel::GetBufferInfo() const {
+  return BufferInfo(buffer_id(), /*consumer_count=*/0, buffer_.width(),
+                    buffer_.height(), buffer_.layer_count(), buffer_.format(),
+                    buffer_.usage(), /*pending_count=*/0, /*state=*/0,
+                    /*signaled_mask=*/0, /*index=*/0);
+}
+
+void DetachedBufferChannel::HandleImpulse(Message& /*message*/) {
+  ATRACE_NAME("DetachedBufferChannel::HandleImpulse");
+}
+
+bool DetachedBufferChannel::HandleMessage(Message& message) {
+  ATRACE_NAME("DetachedBufferChannel::HandleMessage");
+  switch (message.GetOp()) {
+    case BufferHubRPC::DetachedBufferPromote::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::DetachedBufferPromote>(
+          *this, &DetachedBufferChannel::OnPromote, message);
+      return true;
+
+    default:
+      return false;
+  }
+}
+
+Status<RemoteChannelHandle> DetachedBufferChannel::OnPromote(
+    Message& /*message*/) {
+  ATRACE_NAME("DetachedBufferChannel::OnPromote");
+  ALOGD_IF(TRACE, "DetachedBufferChannel::OnPromote: buffer_id=%d",
+           buffer_id());
+
+  // TODO(b/69982239): Implement the logic to promote a detached buffer.
+  return ErrorStatus(ENOSYS);
+}
+
+}  // namespace dvr
+}  // namespace android
diff --git a/services/vr/bufferhubd/detached_buffer_channel.h b/services/vr/bufferhubd/detached_buffer_channel.h
new file mode 100644
index 0000000..7ce4aed
--- /dev/null
+++ b/services/vr/bufferhubd/detached_buffer_channel.h
@@ -0,0 +1,43 @@
+#ifndef ANDROID_DVR_BUFFERHUBD_DETACHED_BUFFER_CHANNEL_H_
+#define ANDROID_DVR_BUFFERHUBD_DETACHED_BUFFER_CHANNEL_H_
+
+#include "buffer_hub.h"
+
+// #include <pdx/channel_handle.h>
+// #include <pdx/file_handle.h>
+// #include <pdx/rpc/buffer_wrapper.h>
+// #include <private/dvr/ion_buffer.h>
+
+namespace android {
+namespace dvr {
+
+class DetachedBufferChannel : public BufferHubChannel {
+ public:
+  // Creates a detached buffer.
+  DetachedBufferChannel(BufferHubService* service, int buffer_id,
+                        int channel_id, IonBuffer buffer,
+                        IonBuffer metadata_buffer, size_t user_metadata_size);
+
+  size_t user_metadata_size() const { return user_metadata_size_; }
+
+  // Captures buffer info for use by BufferHubService::DumpState().
+  BufferInfo GetBufferInfo() const override;
+
+  bool HandleMessage(pdx::Message& message) override;
+  void HandleImpulse(pdx::Message& message) override;
+
+ private:
+  pdx::Status<pdx::RemoteChannelHandle> OnPromote(pdx::Message& message);
+
+  // Gralloc buffer handles.
+  IonBuffer buffer_;
+  IonBuffer metadata_buffer_;
+
+  // Size of user requested metadata.
+  const size_t user_metadata_size_;
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFERHUBD_DETACHED_BUFFER_CHANNEL_H_
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index 8160193..c38c12b 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -13,6 +13,7 @@
 
 #include <private/dvr/bufferhub_rpc.h>
 #include "consumer_channel.h"
+#include "detached_buffer_channel.h"
 
 using android::pdx::BorrowedHandle;
 using android::pdx::ErrorStatus;
@@ -131,8 +132,10 @@
            "ProducerChannel::~ProducerChannel: channel_id=%d buffer_id=%d "
            "state=%" PRIx64 ".",
            channel_id(), buffer_id(), buffer_state_->load());
-  for (auto consumer : consumer_channels_)
+  for (auto consumer : consumer_channels_) {
     consumer->OnProducerClosed();
+  }
+  Hangup();
 }
 
 BufferHubChannel::BufferInfo ProducerChannel::GetBufferInfo() const {
@@ -183,6 +186,11 @@
           *this, &ProducerChannel::OnProducerGain, message);
       return true;
 
+    case BufferHubRPC::ProducerBufferDetach::Opcode:
+      DispatchRemoteMethod<BufferHubRPC::ProducerBufferDetach>(
+          *this, &ProducerChannel::OnProducerDetach, message);
+      return true;
+
     default:
       return false;
   }
@@ -337,6 +345,55 @@
   return {std::move(returned_fence_)};
 }
 
+Status<RemoteChannelHandle> ProducerChannel::OnProducerDetach(
+    Message& message) {
+  ATRACE_NAME("ProducerChannel::OnProducerDetach");
+  ALOGD_IF(TRACE, "ProducerChannel::OnProducerDetach: buffer_id=%d",
+           buffer_id());
+
+  uint64_t buffer_state = buffer_state_->load();
+  if (!BufferHubDefs::IsBufferGained(buffer_state)) {
+    // Can only detach a BufferProducer when it's in gained state.
+    ALOGW(
+        "ProducerChannel::OnProducerDetach: The buffer (id=%d, state=0x%" PRIx64
+        ") is not in gained state.",
+        buffer_id(), buffer_state);
+    return {};
+  }
+
+  int channel_id;
+  auto status = message.PushChannel(0, nullptr, &channel_id);
+  if (!status) {
+    ALOGE(
+        "ProducerChannel::OnProducerDetach: Failed to push detached buffer "
+        "channel: %s",
+        status.GetErrorMessage().c_str());
+    return ErrorStatus(ENOMEM);
+  }
+
+  // Make sure we unlock the buffer.
+  if (int ret = metadata_buffer_.Unlock()) {
+    ALOGE("ProducerChannel::OnProducerDetach: Failed to unlock metadata.");
+    return ErrorStatus(-ret);
+  };
+
+  auto channel = std::make_shared<DetachedBufferChannel>(
+      service(), buffer_id(), channel_id, std::move(buffer_),
+      std::move(metadata_buffer_), user_metadata_size_);
+
+  const auto channel_status = service()->SetChannel(channel_id, channel);
+  if (!channel_status) {
+    // Technically, this should never fail, as we just pushed the channel. Note
+    // that LOG_FATAL will be stripped out in non-debug build.
+    LOG_FATAL(
+        "ProducerChannel::OnProducerDetach: Failed to set new detached buffer "
+        "channel: %s.",
+        channel_status.GetErrorMessage().c_str());
+  }
+
+  return status;
+}
+
 Status<LocalFence> ProducerChannel::OnConsumerAcquire(Message& /*message*/) {
   ATRACE_NAME("ProducerChannel::OnConsumerAcquire");
   ALOGD_IF(TRACE, "ProducerChannel::OnConsumerAcquire: buffer_id=%d",
diff --git a/services/vr/bufferhubd/producer_channel.h b/services/vr/bufferhubd/producer_channel.h
index c5dbf0e..de9ff31 100644
--- a/services/vr/bufferhubd/producer_channel.h
+++ b/services/vr/bufferhubd/producer_channel.h
@@ -99,6 +99,7 @@
   pdx::Status<BufferDescription<BorrowedHandle>> OnGetBuffer(Message& message);
   pdx::Status<void> OnProducerPost(Message& message, LocalFence acquire_fence);
   pdx::Status<LocalFence> OnProducerGain(Message& message);
+  pdx::Status<RemoteChannelHandle> OnProducerDetach(Message& message);
 
   ProducerChannel(const ProducerChannel&) = delete;
   void operator=(const ProducerChannel&) = delete;
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
index 313c022..11ffc3f 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -868,7 +868,7 @@
 
   Error status = Error::NONE;
   sp<VrComposerClient> client;
-  if (client_ == nullptr) {
+  if (!client_.promote().get()) {
     client = new VrComposerClient(*this);
   } else {
     ALOGE("Already have a client");