Merge changes Iea02806d,I45af7b49,Id50e0d23

* changes:
  Revert "Bind to FBO when using GPU composition"
  Revert "Remove gl surfaces from DisplayDevice."
  Revert "Delete renderengine::Surface"
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 50a2412..aad9939 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -701,12 +701,13 @@
                             unique_fd&& reference_profile_fd,
                             unique_fd&& apk_fd,
                             const std::string& dex_location) {
-        std::vector<unique_fd> profiles_fd;
-        profiles_fd.push_back(std::move(profile_fd));
-        std::vector<unique_fd> apk_fds;
-        profiles_fd.push_back(std::move(apk_fd));
+        // The fds need to stay open longer than the scope of the function, so put them into a local
+        // variable vector.
+        profiles_fd_.push_back(std::move(profile_fd));
+        apk_fds_.push_back(std::move(apk_fd));
+        reference_profile_fd_ = std::move(reference_profile_fd);
         std::vector<std::string> dex_locations = {dex_location};
-        SetupArgs(profiles_fd, reference_profile_fd, apk_fds, dex_locations,
+        SetupArgs(profiles_fd_, reference_profile_fd_, apk_fds_, dex_locations,
                   /*copy_and_update=*/true);
     }
 
@@ -724,6 +725,11 @@
     void Exec() {
         ExecVHelper::Exec(DexoptReturnCodes::kProfmanExec);
     }
+
+  private:
+    unique_fd reference_profile_fd_;
+    std::vector<unique_fd> profiles_fd_;
+    std::vector<unique_fd> apk_fds_;
 };
 
 
diff --git a/headers/media_plugin/media/drm/DrmAPI.h b/headers/media_plugin/media/drm/DrmAPI.h
index c44a1f6..aa8bd3d 100644
--- a/headers/media_plugin/media/drm/DrmAPI.h
+++ b/headers/media_plugin/media/drm/DrmAPI.h
@@ -167,6 +167,25 @@
             kSecurityLevelHwSecureAll
         };
 
+        // An offline license may be usable or inactive. The keys in a
+        // usable offline license are available for decryption. When
+        // the offline license state is inactive, the keys have been
+        // marked for release using getKeyRequest with
+        // kKeyType_Release but the key response has not been
+        // received. The keys in an inactive offline license are not
+        // usable for decryption.
+
+        enum OfflineLicenseState {
+            // The offline license state is unknown due to an error
+            kOfflineLicenseStateUnknown,
+            // Offline license state is usable, the keys may be used for decryption.
+            kOfflineLicenseStateUsable,
+            // Offline license state is inactive, the keys have been marked for
+            // release using getKeyRequest() with kKeyType_Release but the
+            // key response has not been received.
+            kOfflineLicenseStateInactive
+        };
+
         DrmPlugin() {}
         virtual ~DrmPlugin() {}
 
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 7c284dd..918b9e1 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -168,17 +168,17 @@
     const sp<InputApplicationHandle> inputApplicationHandle;
 
     inline const InputWindowInfo* getInfo() const {
-        return mInfo;
+        return &mInfo;
     }
 
     sp<InputChannel> getInputChannel() const;
 
     inline std::string getName() const {
-        return mInfo ? mInfo->name : "<invalid>";
+        return mInfo.inputChannel ? mInfo.name : "<invalid>";
     }
 
     inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
-        return mInfo ? mInfo->dispatchingTimeout : defaultValue;
+        return mInfo.inputChannel? mInfo.dispatchingTimeout : defaultValue;
     }
 
     /**
@@ -193,16 +193,16 @@
     virtual bool updateInfo() = 0;
 
     /**
-     * Releases the storage used by the associated information when it is
+     * Releases the channel used by the associated information when it is
      * no longer needed.
      */
-    void releaseInfo();
+    void releaseChannel();
 
 protected:
     explicit InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle);
     virtual ~InputWindowHandle();
 
-    InputWindowInfo* mInfo;
+    InputWindowInfo mInfo;
 };
 
 } // namespace android
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 14edcbe..1674516 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -47,7 +47,7 @@
  * (method calls, property get and set) is down through a low-level
  * protocol implemented on top of the transact() API.
  */
-class IBinder : public virtual RefBase
+class [[clang::lto_visibility_public]] IBinder : public virtual RefBase
 {
 public:
     enum {
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index a5842f7..4ee1b67 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -109,11 +109,11 @@
  *
  * \param arrayData some external representation of an array.
  * \param index the index at which a string should be allocated.
- * \param outLength an out parameter for the length of the string (not including the
- * null-terminator)
+ * \param outLength an out parameter for the length of the string at the specified index. This
+ * should not include the length for a null-terminator if there is one.
  *
- * \param a null-terminated buffer of size 'outLength + 1' representing the string at the provided
- * index including the null-terminator.
+ * \param a buffer of size outLength or more representing the string at the provided index. This is
+ * not required to be null-terminated.
  */
 typedef const char* (*AParcel_stringArrayElementGetter)(const void* arrayData, size_t index,
                                                         size_t* outLength);
@@ -382,8 +382,7 @@
  * Writes utf-8 string value to the next location in a non-null parcel.
  *
  * \param parcel the parcel to write to.
- * \param string the null-terminated string to write to the parcel. The buffer including the null
- * terminator should be of size 'length' + 1.
+ * \param string the null-terminated string to write to the parcel, at least of size 'length'.
  * \param length the length of the string to be written.
  *
  * \return STATUS_OK on successful write.
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 98264ac..7677c3a 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -107,6 +107,7 @@
         "IProducerListener.cpp",
         "ISurfaceComposer.cpp",
         "ISurfaceComposerClient.cpp",
+        "ITransactionCompletedListener.cpp",
         "LayerDebugInfo.cpp",
         "LayerState.cpp",
         "OccupancyTracker.cpp",
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index accf72c..69e5379 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -634,10 +634,10 @@
             if (count > data.dataSize()) {
                 return BAD_VALUE;
             }
-            ComposerState s;
             Vector<ComposerState> state;
             state.setCapacity(count);
             for (size_t i = 0; i < count; i++) {
+                ComposerState s;
                 if (s.read(data) == BAD_VALUE) {
                     return BAD_VALUE;
                 }
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
new file mode 100644
index 0000000..95b1038
--- /dev/null
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "ITransactionCompletedListener"
+//#define LOG_NDEBUG 0
+
+#include <gui/ITransactionCompletedListener.h>
+
+namespace android {
+
+namespace { // Anonymous
+
+enum class Tag : uint32_t {
+    ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
+    LAST = ON_TRANSACTION_COMPLETED,
+};
+
+} // Anonymous namespace
+
+status_t SurfaceStats::writeToParcel(Parcel* output) const {
+    status_t err = output->writeStrongBinder(surfaceControl);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = output->writeInt64(acquireTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    return output->writeBool(releasePreviousBuffer);
+}
+
+status_t SurfaceStats::readFromParcel(const Parcel* input) {
+    status_t err = input->readStrongBinder(&surfaceControl);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = input->readInt64(&acquireTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    return input->readBool(&releasePreviousBuffer);
+}
+
+status_t TransactionStats::writeToParcel(Parcel* output) const {
+    status_t err = output->writeInt64(latchTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = output->writeInt64(presentTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    return output->writeParcelableVector(surfaceStats);
+}
+
+status_t TransactionStats::readFromParcel(const Parcel* input) {
+    status_t err = input->readInt64(&latchTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = input->readInt64(&presentTime);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    return input->readParcelableVector(&surfaceStats);
+}
+
+status_t ListenerStats::writeToParcel(Parcel* output) const {
+    status_t err = output->writeInt32(static_cast<int32_t>(transactionStats.size()));
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    for (const auto& [callbackIds, stats] : transactionStats) {
+        err = output->writeParcelable(stats);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        err = output->writeInt64Vector(callbackIds);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t ListenerStats::readFromParcel(const Parcel* input) {
+    int32_t transactionStats_size = input->readInt32();
+
+    for (int i = 0; i < transactionStats_size; i++) {
+        TransactionStats stats;
+        std::vector<CallbackId> callbackIds;
+
+        status_t err = input->readParcelable(&stats);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        err = input->readInt64Vector(&callbackIds);
+        if (err != NO_ERROR) {
+            return err;
+        }
+
+        transactionStats.emplace(callbackIds, stats);
+    }
+    return NO_ERROR;
+}
+
+ListenerStats ListenerStats::createEmpty(const sp<ITransactionCompletedListener>& listener,
+                                         const std::unordered_set<CallbackId>& callbackIds) {
+    ListenerStats listenerStats;
+    listenerStats.listener = listener;
+    TransactionStats transactionStats;
+    listenerStats.transactionStats.emplace(std::piecewise_construct,
+                                           std::forward_as_tuple(callbackIds.begin(),
+                                                                 callbackIds.end()),
+                                           std::forward_as_tuple(transactionStats));
+    return listenerStats;
+}
+
+class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> {
+public:
+    explicit BpTransactionCompletedListener(const sp<IBinder>& impl)
+          : SafeBpInterface<ITransactionCompletedListener>(impl, "BpTransactionCompletedListener") {
+    }
+
+    ~BpTransactionCompletedListener() override;
+
+    void onTransactionCompleted(ListenerStats stats) override {
+        callRemoteAsync<decltype(&ITransactionCompletedListener::
+                                         onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED,
+                                                                  stats);
+    }
+};
+
+// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
+// clang warning -Wweak-vtables)
+BpTransactionCompletedListener::~BpTransactionCompletedListener() = default;
+
+IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener");
+
+status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data,
+                                                    Parcel* reply, uint32_t flags) {
+    if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
+        return BBinder::onTransact(code, data, reply, flags);
+    }
+    auto tag = static_cast<Tag>(code);
+    switch (tag) {
+        case Tag::ON_TRANSACTION_COMPLETED:
+            return callLocalAsync(data, reply,
+                                  &ITransactionCompletedListener::onTransactionCompleted);
+    }
+}
+
+}; // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 2b0a461..7b71b39 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -80,6 +80,13 @@
     memcpy(output.writeInplace(16 * sizeof(float)),
            colorTransform.asArray(), 16 * sizeof(float));
 
+    if (output.writeVectorSize(listenerCallbacks) == NO_ERROR) {
+        for (const auto& [listener, callbackIds] : listenerCallbacks) {
+            output.writeStrongBinder(IInterface::asBinder(listener));
+            output.writeInt64Vector(callbackIds);
+        }
+    }
+
     return NO_ERROR;
 }
 
@@ -135,6 +142,14 @@
 
     colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float))));
 
+    int32_t listenersSize = input.readInt32();
+    for (int32_t i = 0; i < listenersSize; i++) {
+        auto listener = interface_cast<ITransactionCompletedListener>(input.readStrongBinder());
+        std::vector<CallbackId> callbackIds;
+        input.readInt64Vector(&callbackIds);
+        listenerCallbacks.emplace_back(listener, callbackIds);
+    }
+
     return NO_ERROR;
 }
 
@@ -323,6 +338,10 @@
         what |= eColorTransformChanged;
         colorTransform = other.colorTransform;
     }
+    if (other.what & eListenerCallbacksChanged) {
+        what |= eListenerCallbacksChanged;
+        listenerCallbacks = other.listenerCallbacks;
+    }
 
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index e5a2454..e10bda4 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -25,7 +25,9 @@
 #include <utils/String8.h>
 #include <utils/threads.h>
 
+#include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
 
 #include <system/graphics.h>
 
@@ -98,6 +100,61 @@
 
 // ---------------------------------------------------------------------------
 
+// TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs
+// to be able to return a sp<> to its instance to pass to SurfaceFlinger.
+// ANDROID_SINGLETON_STATIC_INSTANCE only allows a reference to an instance.
+
+// 0 is an invalid callback id
+TransactionCompletedListener::TransactionCompletedListener() : mCallbackIdCounter(1) {}
+
+CallbackId TransactionCompletedListener::getNextIdLocked() {
+    return mCallbackIdCounter++;
+}
+
+sp<TransactionCompletedListener> TransactionCompletedListener::getInstance() {
+    static sp<TransactionCompletedListener> sInstance = new TransactionCompletedListener;
+    return sInstance;
+}
+
+sp<ITransactionCompletedListener> TransactionCompletedListener::getIInstance() {
+    return static_cast<sp<ITransactionCompletedListener>>(getInstance());
+}
+
+void TransactionCompletedListener::startListeningLocked() {
+    if (mListening) {
+        return;
+    }
+    ProcessState::self()->startThreadPool();
+    mListening = true;
+}
+
+CallbackId TransactionCompletedListener::addCallback(const TransactionCompletedCallback& callback) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    startListeningLocked();
+
+    CallbackId callbackId = getNextIdLocked();
+    mCallbacks.emplace(callbackId, callback);
+    return callbackId;
+}
+
+void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
+    std::lock_guard lock(mMutex);
+
+    for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
+        for (auto callbackId : callbackIds) {
+            const auto& callback = mCallbacks[callbackId];
+            if (!callback) {
+                ALOGE("cannot call null callback function, skipping");
+                continue;
+            }
+            callback(transactionStats);
+            mCallbacks.erase(callbackId);
+        }
+    }
+}
+
+// ---------------------------------------------------------------------------
+
 SurfaceComposerClient::Transaction::Transaction(const Transaction& other) :
     mForceSynchronous(other.mForceSynchronous),
     mTransactionNestCount(other.mTransactionNestCount),
@@ -127,6 +184,17 @@
     }
     other.mDisplayStates.clear();
 
+    for (const auto& [listener, callbackInfo] : other.mListenerCallbacks) {
+        auto& [callbackIds, surfaceControls] = callbackInfo;
+        mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator(
+                                                                callbackIds.begin()),
+                                                        std::make_move_iterator(callbackIds.end()));
+        mListenerCallbacks[listener]
+                .surfaceControls.insert(std::make_move_iterator(surfaceControls.begin()),
+                                        std::make_move_iterator(surfaceControls.end()));
+    }
+    other.mListenerCallbacks.clear();
+
     return *this;
 }
 
@@ -137,6 +205,32 @@
 
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
 
+    // For every listener with registered callbacks
+    for (const auto& [listener, callbackInfo] : mListenerCallbacks) {
+        auto& [callbackIds, surfaceControls] = callbackInfo;
+        if (callbackIds.empty()) {
+            continue;
+        }
+
+        // If the listener does not have any SurfaceControls set on this Transaction, send the
+        // callback now
+        if (surfaceControls.empty()) {
+            listener->onTransactionCompleted(ListenerStats::createEmpty(listener, callbackIds));
+        }
+
+        // If the listener has any SurfaceControls set on this Transaction update the surface state
+        for (const auto& surfaceControl : surfaceControls) {
+            layer_state_t* s = getLayerState(surfaceControl);
+            if (!s) {
+                ALOGE("failed to get layer state");
+                continue;
+            }
+            s->what |= layer_state_t::eListenerCallbacksChanged;
+            s->listenerCallbacks.emplace_back(listener, std::move(callbackIds));
+        }
+    }
+    mListenerCallbacks.clear();
+
     Vector<ComposerState> composerStates;
     Vector<DisplayState> displayStates;
     uint32_t flags = 0;
@@ -206,6 +300,11 @@
     return &(mComposerStates[sc].state);
 }
 
+void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback(
+        const sp<SurfaceControl>& sc) {
+    mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls.insert(sc);
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition(
         const sp<SurfaceControl>& sc, float x, float y) {
     layer_state_t* s = getLayerState(sc);
@@ -216,6 +315,8 @@
     s->what |= layer_state_t::ePositionChanged;
     s->x = x;
     s->y = y;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -240,6 +341,7 @@
     s->w = w;
     s->h = h;
 
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -252,6 +354,8 @@
     }
     s->what |= layer_state_t::eLayerChanged;
     s->z = z;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -264,6 +368,8 @@
     s->what |= layer_state_t::eRelativeLayerChanged;
     s->relativeLayerHandle = relativeTo;
     s->z = z;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -283,6 +389,8 @@
     s->flags &= ~mask;
     s->flags |= (flags & mask);
     s->mask |= mask;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -296,6 +404,8 @@
     }
     s->what |= layer_state_t::eTransparentRegionChanged;
     s->transparentRegion = transparentRegion;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -308,6 +418,8 @@
     }
     s->what |= layer_state_t::eAlphaChanged;
     s->alpha = alpha;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -320,6 +432,8 @@
     }
     s->what |= layer_state_t::eLayerStackChanged;
     s->layerStack = layerStack;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -338,6 +452,8 @@
     matrix.dsdy = dsdy;
     matrix.dtdy = dtdy;
     s->matrix = matrix;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -350,6 +466,8 @@
     }
     s->what |= layer_state_t::eCropChanged_legacy;
     s->crop_legacy = crop;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -365,6 +483,8 @@
     s->what |= layer_state_t::eDeferTransaction_legacy;
     s->barrierHandle_legacy = handle;
     s->frameNumber_legacy = frameNumber;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -380,6 +500,8 @@
     s->what |= layer_state_t::eDeferTransaction_legacy;
     s->barrierGbp_legacy = barrierSurface->getIGraphicBufferProducer();
     s->frameNumber_legacy = frameNumber;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -393,6 +515,8 @@
     }
     s->what |= layer_state_t::eReparentChildren;
     s->reparentHandle = newParentHandle;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -406,6 +530,8 @@
     }
     s->what |= layer_state_t::eReparent;
     s->parentHandleForChild = newParentHandle;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -419,6 +545,8 @@
     }
     s->what |= layer_state_t::eColorChanged;
     s->color = color;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -431,6 +559,8 @@
     }
     s->what |= layer_state_t::eTransformChanged;
     s->transform = transform;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -444,6 +574,8 @@
     }
     s->what |= layer_state_t::eTransformToDisplayInverseChanged;
     s->transformToDisplayInverse = transformToDisplayInverse;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -456,6 +588,8 @@
     }
     s->what |= layer_state_t::eCropChanged;
     s->crop = crop;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -468,6 +602,8 @@
     }
     s->what |= layer_state_t::eBufferChanged;
     s->buffer = buffer;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -480,6 +616,8 @@
     }
     s->what |= layer_state_t::eAcquireFenceChanged;
     s->acquireFence = fence;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -492,6 +630,8 @@
     }
     s->what |= layer_state_t::eDataspaceChanged;
     s->dataspace = dataspace;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -504,6 +644,8 @@
     }
     s->what |= layer_state_t::eHdrMetadataChanged;
     s->hdrMetadata = hdrMetadata;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -516,6 +658,8 @@
     }
     s->what |= layer_state_t::eSurfaceDamageRegionChanged;
     s->surfaceDamageRegion = surfaceDamageRegion;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -528,6 +672,8 @@
     }
     s->what |= layer_state_t::eApiChanged;
     s->api = api;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -540,6 +686,22 @@
     }
     s->what |= layer_state_t::eSidebandStreamChanged;
     s->sidebandStream = sidebandStream;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
+        TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
+    auto listener = TransactionCompletedListener::getInstance();
+
+    auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1);
+
+    CallbackId callbackId = listener->addCallback(callbackWithContext);
+
+    mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace(
+            callbackId);
     return *this;
 }
 
@@ -550,6 +712,8 @@
         mStatus = BAD_INDEX;
     }
     s->what |= layer_state_t::eDetachChildren;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -577,6 +741,8 @@
 
     s->what |= layer_state_t::eOverrideScalingModeChanged;
     s->overrideScalingMode = overrideScalingMode;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -588,6 +754,8 @@
         return *this;
     }
     s->what |= layer_state_t::eGeometryAppliesWithResize;
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
@@ -611,6 +779,8 @@
     }
     s->what |= layer_state_t::eColorTransformChanged;
     s->colorTransform = mat4(matrix, translation);
+
+    registerSurfaceControlForCallback(sc);
     return *this;
 }
 
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
new file mode 100644
index 0000000..5c41c21
--- /dev/null
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 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 <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/SafeInterface.h>
+
+#include <utils/Timers.h>
+
+#include <cstdint>
+#include <unordered_map>
+#include <unordered_set>
+
+namespace android {
+
+class ITransactionCompletedListener;
+
+using CallbackId = int64_t;
+
+struct CallbackIdsHash {
+    // CallbackId vectors have several properties that let us get away with this simple hash.
+    // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is
+    // empty we can still hash 0.
+    // 2) CallbackId vectors for the same listener either are identical or contain none of the
+    // same members. It is sufficient to just check the first CallbackId in the vectors. If
+    // they match, they are the same. If they do not match, they are not the same.
+    std::size_t operator()(const std::vector<CallbackId> callbackIds) const {
+        return std::hash<CallbackId>{}((callbackIds.size() == 0) ? 0 : callbackIds.front());
+    }
+};
+
+class SurfaceStats : public Parcelable {
+public:
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    SurfaceStats() = default;
+    SurfaceStats(const sp<IBinder>& sc, nsecs_t time, bool releasePrevBuffer)
+          : surfaceControl(sc), acquireTime(time), releasePreviousBuffer(releasePrevBuffer) {}
+
+    sp<IBinder> surfaceControl;
+    nsecs_t acquireTime = -1;
+    bool releasePreviousBuffer = false;
+};
+
+class TransactionStats : public Parcelable {
+public:
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    nsecs_t latchTime = -1;
+    nsecs_t presentTime = -1;
+    std::vector<SurfaceStats> surfaceStats;
+};
+
+class ListenerStats : public Parcelable {
+public:
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    static ListenerStats createEmpty(const sp<ITransactionCompletedListener>& listener,
+                                     const std::unordered_set<CallbackId>& callbackIds);
+
+    sp<ITransactionCompletedListener> listener;
+    std::unordered_map<std::vector<CallbackId>, TransactionStats, CallbackIdsHash> transactionStats;
+};
+
+class ITransactionCompletedListener : public IInterface {
+public:
+    DECLARE_META_INTERFACE(TransactionCompletedListener)
+
+    virtual void onTransactionCompleted(ListenerStats stats) = 0;
+};
+
+class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
+public:
+    BnTransactionCompletedListener()
+          : SafeBnInterface<ITransactionCompletedListener>("BnTransactionCompletedListener") {}
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                        uint32_t flags = 0) override;
+};
+
+class ListenerCallbacks {
+public:
+    ListenerCallbacks(const sp<ITransactionCompletedListener>& listener,
+                      const std::unordered_set<CallbackId>& callbacks)
+          : transactionCompletedListener(listener),
+            callbackIds(callbacks.begin(), callbacks.end()) {}
+
+    ListenerCallbacks(const sp<ITransactionCompletedListener>& listener,
+                      const std::vector<CallbackId>& ids)
+          : transactionCompletedListener(listener), callbackIds(ids) {}
+
+    sp<ITransactionCompletedListener> transactionCompletedListener;
+    std::vector<CallbackId> callbackIds;
+};
+
+} // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index e06e2b1..ddbac7b 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -23,6 +23,7 @@
 #include <utils/Errors.h>
 
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/ITransactionCompletedListener.h>
 #include <math/mat4.h>
 #include <math/vec3.h>
 #include <ui/GraphicTypes.h>
@@ -74,6 +75,7 @@
         eApiChanged = 0x04000000,
         eSidebandStreamChanged = 0x08000000,
         eColorTransformChanged = 0x10000000,
+        eListenerCallbacksChanged = 0x20000000,
     };
 
     layer_state_t()
@@ -154,6 +156,8 @@
     int32_t api;
     sp<NativeHandle> sidebandStream;
     mat4 colorTransform;
+
+    std::vector<ListenerCallbacks> listenerCallbacks;
 };
 
 struct ComposerState {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 8ccee05..1cafb77 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -19,7 +19,9 @@
 
 #include <stdint.h>
 #include <sys/types.h>
+#include <set>
 #include <unordered_map>
+#include <unordered_set>
 
 #include <binder/IBinder.h>
 
@@ -33,9 +35,10 @@
 #include <ui/PixelFormat.h>
 
 #include <gui/CpuConsumer.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <gui/LayerState.h>
 #include <gui/SurfaceControl.h>
 #include <math/vec3.h>
-#include <gui/LayerState.h>
 
 namespace android {
 
@@ -49,6 +52,37 @@
 
 // ---------------------------------------------------------------------------
 
+using TransactionCompletedCallbackTakesContext =
+        std::function<void(void* /*context*/, const TransactionStats&)>;
+using TransactionCompletedCallback = std::function<void(const TransactionStats&)>;
+
+class TransactionCompletedListener : public BnTransactionCompletedListener {
+    TransactionCompletedListener();
+
+    CallbackId getNextIdLocked() REQUIRES(mMutex);
+
+    std::mutex mMutex;
+
+    bool mListening GUARDED_BY(mMutex) = false;
+
+    CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1;
+
+    std::map<CallbackId, TransactionCompletedCallback> mCallbacks GUARDED_BY(mMutex);
+
+public:
+    static sp<TransactionCompletedListener> getInstance();
+    static sp<ITransactionCompletedListener> getIInstance();
+
+    void startListeningLocked() REQUIRES(mMutex);
+
+    CallbackId addCallback(const TransactionCompletedCallback& callback);
+
+    // Overrides BnTransactionCompletedListener's onTransactionCompleted
+    void onTransactionCompleted(ListenerStats stats) override;
+};
+
+// ---------------------------------------------------------------------------
+
 class SurfaceComposerClient : public RefBase
 {
     friend class Composer;
@@ -158,9 +192,27 @@
         }
     };
 
+    struct TCLHash {
+        std::size_t operator()(const sp<ITransactionCompletedListener>& tcl) const {
+            return std::hash<IBinder*>{}((tcl) ? IInterface::asBinder(tcl).get() : nullptr);
+        }
+    };
+
+    struct CallbackInfo {
+        // All the callbacks that have been requested for a TransactionCompletedListener in the
+        // Transaction
+        std::unordered_set<CallbackId> callbackIds;
+        // All the SurfaceControls that have been modified in this TransactionCompletedListener's
+        // process that require a callback if there is one or more callbackIds set.
+        std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls;
+    };
+
     class Transaction {
         std::unordered_map<sp<SurfaceControl>, ComposerState, SCHash> mComposerStates;
         SortedVector<DisplayState > mDisplayStates;
+        std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
+                mListenerCallbacks;
+
         uint32_t                    mForceSynchronous = 0;
         uint32_t                    mTransactionNestCount = 0;
         bool                        mAnimation = false;
@@ -171,6 +223,8 @@
         layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
         DisplayState& getDisplayState(const sp<IBinder>& token);
 
+        void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
+
     public:
         Transaction() = default;
         virtual ~Transaction() = default;
@@ -250,6 +304,9 @@
         Transaction& setSidebandStream(const sp<SurfaceControl>& sc,
                                        const sp<NativeHandle>& sidebandStream);
 
+        Transaction& addTransactionCompletedCallback(
+                TransactionCompletedCallbackTakesContext callback, void* callbackContext);
+
         // Detaches all child surfaces (and their children recursively)
         // from their SurfaceControl.
         // The child SurfaceControls will not throw exceptions or return errors,
@@ -349,6 +406,7 @@
 };
 
 // ---------------------------------------------------------------------------
+
 }; // namespace android
 
 #endif // ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index f94faba..6968661 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -138,22 +138,18 @@
 // --- InputWindowHandle ---
 
 InputWindowHandle::InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle) :
-    inputApplicationHandle(inputApplicationHandle), mInfo(nullptr) {
+    inputApplicationHandle(inputApplicationHandle) {
 }
 
 InputWindowHandle::~InputWindowHandle() {
-    delete mInfo;
 }
 
-void InputWindowHandle::releaseInfo() {
-    if (mInfo) {
-        delete mInfo;
-        mInfo = nullptr;
-    }
+void InputWindowHandle::releaseChannel() {
+    mInfo.inputChannel.clear();
 }
 
 sp<InputChannel> InputWindowHandle::getInputChannel() const {
-    return mInfo ? mInfo->inputChannel : nullptr;
+    return mInfo.inputChannel;
 }
 
 } // namespace android
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 1bbd82b..42d774e 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -327,8 +327,8 @@
         eventTime = event->getHistoricalEventTime(h);
         for (size_t i = 0; i < pointerCount; i++) {
             uint32_t index = pointerIndex[i];
-            positions[index].x = event->getHistoricalRawX(i, h);
-            positions[index].y = event->getHistoricalRawY(i, h);
+            positions[index].x = event->getHistoricalX(i, h);
+            positions[index].y = event->getHistoricalY(i, h);
         }
         addMovement(eventTime, idBits, positions);
     }
@@ -336,8 +336,8 @@
     eventTime = event->getEventTime();
     for (size_t i = 0; i < pointerCount; i++) {
         uint32_t index = pointerIndex[i];
-        positions[index].x = event->getRawX(i);
-        positions[index].y = event->getRawY(i);
+        positions[index].x = event->getX(i);
+        positions[index].y = event->getY(i);
     }
     addMovement(eventTime, idBits, positions);
 }
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 647fe86..fba319d 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -47,6 +47,7 @@
     cflags: [
         "-Wall",
         "-Werror",
+        "-Wno-enum-compare",
         "-Wno-unused-function",
     ],
 
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index 9ca4941..23ac60b 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -15,12 +15,31 @@
  */
 
 /**
- * @addtogroup NativeActivity Native Activity
- * @{
- */
-
-/**
  * @file hardware_buffer.h
+ * @brief API for native hardware buffers.
+ */
+/**
+ * @defgroup AHardwareBuffer Native Hardware Buffer
+ *
+ * AHardwareBuffer objects represent chunks of memory that can be
+ * accessed by various hardware components in the system. It can be
+ * easily converted to the Java counterpart
+ * android.hardware.HardwareBuffer and passed between processes using
+ * Binder. All operations involving AHardwareBuffer and HardwareBuffer
+ * are zero-copy, i.e., passing AHardwareBuffer to another process
+ * creates a shared view of the same region of memory.
+ *
+ * AHardwareBuffers can be bound to EGL/OpenGL and Vulkan primitives.
+ * For EGL, use the extension function eglGetNativeClientBufferANDROID
+ * to obtain an EGLClientBuffer and pass it directly to
+ * eglCreateImageKHR. Refer to the EGL extensions
+ * EGL_ANDROID_get_native_client_buffer and
+ * EGL_ANDROID_image_native_buffer for more information. In Vulkan,
+ * the contents of the AHardwareBuffer can be accessed as external
+ * memory. See the VK_ANDROID_external_memory_android_hardware_buffer
+ * extension for details.
+ *
+ * @{
  */
 
 #ifndef ANDROID_HARDWARE_BUFFER_H
@@ -37,7 +56,7 @@
 /**
  * Buffer pixel formats.
  */
-enum {
+enum AHardwareBuffer_Format {
     /**
      * Corresponding formats:
      *   Vulkan: VK_FORMAT_R8G8B8A8_UNORM
@@ -134,27 +153,47 @@
 /**
  * Buffer usage flags, specifying how the buffer will be accessed.
  */
-enum {
-    /// The buffer will never be read by the CPU.
+enum AHardwareBuffer_UsageFlags {
+    /// The buffer will never be locked for direct CPU reads using the
+    /// AHardwareBuffer_lock() function. Note that reading the buffer
+    /// using OpenGL or Vulkan functions or memory mappings is still
+    /// allowed.
     AHARDWAREBUFFER_USAGE_CPU_READ_NEVER        = 0UL,
-    /// The buffer will sometimes be read by the CPU.
+    /// The buffer will sometimes be locked for direct CPU reads using
+    /// the AHardwareBuffer_lock() function. Note that reading the
+    /// buffer using OpenGL or Vulkan functions or memory mappings
+    /// does not require the presence of this flag.
     AHARDWAREBUFFER_USAGE_CPU_READ_RARELY       = 2UL,
-    /// The buffer will often be read by the CPU.
+    /// The buffer will often be locked for direct CPU reads using
+    /// the AHardwareBuffer_lock() function. Note that reading the
+    /// buffer using OpenGL or Vulkan functions or memory mappings
+    /// does not require the presence of this flag.
     AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN        = 3UL,
     /// CPU read value mask.
     AHARDWAREBUFFER_USAGE_CPU_READ_MASK         = 0xFUL,
 
-    /// The buffer will never be written by the CPU.
+    /// The buffer will never be locked for direct CPU writes using the
+    /// AHardwareBuffer_lock() function. Note that writing the buffer
+    /// using OpenGL or Vulkan functions or memory mappings is still
+    /// allowed.
     AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER       = 0UL << 4,
-    /// The buffer will sometimes be written to by the CPU.
+    /// The buffer will sometimes be locked for direct CPU writes using
+    /// the AHardwareBuffer_lock() function. Note that writing the
+    /// buffer using OpenGL or Vulkan functions or memory mappings
+    /// does not require the presence of this flag.
     AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY      = 2UL << 4,
-    /// The buffer will often be written to by the CPU.
+    /// The buffer will often be locked for direct CPU writes using
+    /// the AHardwareBuffer_lock() function. Note that writing the
+    /// buffer using OpenGL or Vulkan functions or memory mappings
+    /// does not require the presence of this flag.
     AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN       = 3UL << 4,
     /// CPU write value mask.
     AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK        = 0xFUL << 4,
 
     /// The buffer will be read from by the GPU as a texture.
     AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE      = 1UL << 8,
+    /// The buffer will be written to by the GPU as a framebuffer attachment.
+    AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER        = 1UL << 9,
     /**
      * The buffer will be written to by the GPU as a framebuffer attachment.
      * Note that the name of this flag is somewhat misleading: it does not imply
@@ -162,16 +201,33 @@
      * format that will be used as a framebuffer attachment should also have
      * this flag.
      */
-    AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT       = 1UL << 9,
-    /// The buffer must not be used outside of a protected hardware path.
+    AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT       = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER,
+    /**
+     * The buffer is protected from direct CPU access or being read by non-secure
+     * hardware, such as video encoders. This flag is incompatible with CPU
+     * read and write flags. It is mainly used when handling DRM video.
+     * Refer to the EGL extension EGL_EXT_protected_content and GL extension
+     * EXT_protected_textures for more information on how these buffers are expected
+     * to behave.
+     */
     AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT      = 1UL << 14,
     /// The buffer will be read by a hardware video encoder.
     AHARDWAREBUFFER_USAGE_VIDEO_ENCODE           = 1UL << 16,
-    /// The buffer will be used for direct writes from sensors.
+    /**
+     * The buffer will be used for direct writes from sensors.
+     * When this flag is present, the format must be AHARDWAREBUFFER_FORMAT_BLOB.
+     */
     AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA     = 1UL << 23,
-    /// The buffer will be used as a shader storage or uniform buffer object.
+    /**
+     * The buffer will be used as a shader storage or uniform buffer object.
+     * When this flag is present, the format must be AHARDWAREBUFFER_FORMAT_BLOB.
+     */
     AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER        = 1UL << 24,
-    /// The buffer will be used as a cube map texture.
+    /**
+     * The buffer will be used as a cube map texture.
+     * When this flag is present, the buffer must have a layer count that is
+     * a multiple of 6.
+     */
     AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP               = 1UL << 25,
     /// The buffer contains a complete mipmap hierarchy.
     AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE        = 1UL << 26,
@@ -206,20 +262,27 @@
     uint32_t    width;      ///< Width in pixels.
     uint32_t    height;     ///< Height in pixels.
     uint32_t    layers;     ///< Number of images in an image array.
-    uint32_t    format;     ///< One of AHARDWAREBUFFER_FORMAT_*
-    uint64_t    usage;      ///< Combination of AHARDWAREBUFFER_USAGE_*
+    uint32_t    format;     ///< One of AHardwareBuffer_Format.
+    uint64_t    usage;      ///< Combination of AHardwareBuffer_UsageFlags.
     uint32_t    stride;     ///< Row stride in pixels, ignored for AHardwareBuffer_allocate()
     uint32_t    rfu0;       ///< Initialize to zero, reserved for future use.
     uint64_t    rfu1;       ///< Initialize to zero, reserved for future use.
 } AHardwareBuffer_Desc;
 
+/**
+ * Opaque handle for a native hardware buffer.
+ */
 typedef struct AHardwareBuffer AHardwareBuffer;
 
 #if __ANDROID_API__ >= 26
 
 /**
- * Allocates a buffer that backs an AHardwareBuffer using the passed
- * AHardwareBuffer_Desc.
+ * Allocates a buffer that matches the passed AHardwareBuffer_Desc.
+ *
+ * If allocation succeeds, the buffer can be used according to the
+ * usage flags specified in its description. If a buffer is used in ways
+ * not compatible with its usage flags, the results are undefined and
+ * may include program termination.
  *
  * \return 0 on success, or an error number of the allocation fails for
  * any reason. The returned buffer has a reference count of 1.
@@ -234,7 +297,7 @@
 
 /**
  * Remove a reference that was previously acquired with
- * AHardwareBuffer_acquire().
+ * AHardwareBuffer_acquire() or AHardwareBuffer_allocate().
  */
 void AHardwareBuffer_release(AHardwareBuffer* buffer) __INTRODUCED_IN(26);
 
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 6831f91..6730596 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -15,7 +15,13 @@
  */
 
 /**
- * @addtogroup NativeActivity Native Activity
+ * @defgroup ANativeWindow Native Window
+ *
+ * ANativeWindow represents the producer end of an image queue.
+ * It is the C counterpart of the android.view.Surface object in Java,
+ * and can be converted both ways. Depending on the consumer, images
+ * submitted to ANativeWindow can be shown on the display or sent to
+ * other consumers, such as video encoders.
  * @{
  */
 
@@ -41,7 +47,7 @@
  * Legacy window pixel format names, kept for backwards compatibility.
  * New code and APIs should use AHARDWAREBUFFER_FORMAT_*.
  */
-enum {
+enum ANativeWindow_LegacyFormat {
     // NOTE: these values must match the values from graphics/common/x.x/types.hal
 
     /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/
@@ -95,7 +101,7 @@
     /// memory. This may be >= width.
     int32_t stride;
 
-    /// The format of the buffer. One of AHARDWAREBUFFER_FORMAT_*
+    /// The format of the buffer. One of AHardwareBuffer_Format.
     int32_t format;
 
     /// The actual bits.
@@ -151,7 +157,7 @@
  *
  * \param width width of the buffers in pixels.
  * \param height height of the buffers in pixels.
- * \param format one of AHARDWAREBUFFER_FORMAT_* constants.
+ * \param format one of the AHardwareBuffer_Format constants.
  * \return 0 for success, or a negative value on error.
  */
 int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window,
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 5a1ddee..29deb01 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -64,12 +64,11 @@
 {
 }
 
-GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,
-        PixelFormat inFormat, uint32_t inLayerCount, uint64_t usage, std::string requestorName)
-    : GraphicBuffer()
-{
-    mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount,
-            usage, std::move(requestorName));
+GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+                             uint32_t inLayerCount, uint64_t inUsage, std::string requestorName)
+      : GraphicBuffer() {
+    mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount, inUsage,
+                              std::move(requestorName));
 }
 
 // deprecated
@@ -82,15 +81,12 @@
 {
 }
 
-GraphicBuffer::GraphicBuffer(const native_handle_t* handle,
-        HandleWrapMethod method, uint32_t width, uint32_t height,
-        PixelFormat format, uint32_t layerCount,
-        uint64_t usage,
-        uint32_t stride)
-    : GraphicBuffer()
-{
-    mInitCheck = initWithHandle(handle, method, width, height, format,
-            layerCount, usage, stride);
+GraphicBuffer::GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod method,
+                             uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+                             uint32_t inLayerCount, uint64_t inUsage, uint32_t inStride)
+      : GraphicBuffer() {
+    mInitCheck = initWithHandle(inHandle, method, inWidth, inHeight, inFormat, inLayerCount,
+                                inUsage, inStride);
 }
 
 GraphicBuffer::~GraphicBuffer()
@@ -182,26 +178,24 @@
     return err;
 }
 
-status_t GraphicBuffer::initWithHandle(const native_handle_t* handle,
-        HandleWrapMethod method, uint32_t width, uint32_t height,
-        PixelFormat format, uint32_t layerCount, uint64_t usage,
-        uint32_t stride)
-{
-    ANativeWindowBuffer::width  = static_cast<int>(width);
-    ANativeWindowBuffer::height = static_cast<int>(height);
-    ANativeWindowBuffer::stride = static_cast<int>(stride);
-    ANativeWindowBuffer::format = format;
-    ANativeWindowBuffer::usage  = usage;
-    ANativeWindowBuffer::usage_deprecated = int(usage);
+status_t GraphicBuffer::initWithHandle(const native_handle_t* inHandle, HandleWrapMethod method,
+                                       uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+                                       uint32_t inLayerCount, uint64_t inUsage, uint32_t inStride) {
+    ANativeWindowBuffer::width = static_cast<int>(inWidth);
+    ANativeWindowBuffer::height = static_cast<int>(inHeight);
+    ANativeWindowBuffer::stride = static_cast<int>(inStride);
+    ANativeWindowBuffer::format = inFormat;
+    ANativeWindowBuffer::usage = inUsage;
+    ANativeWindowBuffer::usage_deprecated = int(inUsage);
 
-    ANativeWindowBuffer::layerCount = layerCount;
+    ANativeWindowBuffer::layerCount = inLayerCount;
 
     mOwner = (method == WRAP_HANDLE) ? ownNone : ownHandle;
 
     if (method == TAKE_UNREGISTERED_HANDLE || method == CLONE_HANDLE) {
         buffer_handle_t importedHandle;
-        status_t err = mBufferMapper.importBuffer(handle, width, height,
-                layerCount, format, usage, stride, &importedHandle);
+        status_t err = mBufferMapper.importBuffer(inHandle, inWidth, inHeight, inLayerCount,
+                                                  inFormat, inUsage, inStride, &importedHandle);
         if (err != NO_ERROR) {
             initWithHandle(nullptr, WRAP_HANDLE, 0, 0, 0, 0, 0, 0);
 
@@ -209,15 +203,15 @@
         }
 
         if (method == TAKE_UNREGISTERED_HANDLE) {
-            native_handle_close(handle);
-            native_handle_delete(const_cast<native_handle_t*>(handle));
+            native_handle_close(inHandle);
+            native_handle_delete(const_cast<native_handle_t*>(inHandle));
         }
 
-        handle = importedHandle;
-        mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);
+        inHandle = importedHandle;
+        mBufferMapper.getTransportSize(inHandle, &mTransportNumFds, &mTransportNumInts);
     }
 
-    ANativeWindowBuffer::handle = handle;
+    ANativeWindowBuffer::handle = inHandle;
 
     return NO_ERROR;
 }
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index e794462..fe6229a 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -117,18 +117,16 @@
         // cannot be used directly, such as one from hidl_handle.
         CLONE_HANDLE,
     };
-    GraphicBuffer(const native_handle_t* handle, HandleWrapMethod method,
-            uint32_t width, uint32_t height,
-            PixelFormat format, uint32_t layerCount,
-            uint64_t usage, uint32_t stride);
+    GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod method, uint32_t inWidth,
+                  uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage,
+                  uint32_t inStride);
 
     // These functions are deprecated because they only take 32 bits of usage
-    GraphicBuffer(const native_handle_t* handle, HandleWrapMethod method,
-            uint32_t width, uint32_t height,
-            PixelFormat format, uint32_t layerCount,
-            uint32_t usage, uint32_t stride)
-        : GraphicBuffer(handle, method, width, height, format, layerCount,
-                static_cast<uint64_t>(usage), stride) {}
+    GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod method, uint32_t inWidth,
+                  uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage,
+                  uint32_t inStride)
+          : GraphicBuffer(inHandle, method, inWidth, inHeight, inFormat, inLayerCount,
+                          static_cast<uint64_t>(inUsage), inStride) {}
     GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
             uint32_t inLayerCount, uint32_t inUsage, uint32_t inStride,
             native_handle_t* inHandle, bool keepOwnership);
@@ -220,10 +218,9 @@
             PixelFormat inFormat, uint32_t inLayerCount,
             uint64_t inUsage, std::string requestorName);
 
-    status_t initWithHandle(const native_handle_t* handle,
-            HandleWrapMethod method, uint32_t width, uint32_t height,
-            PixelFormat format, uint32_t layerCount,
-            uint64_t usage, uint32_t stride);
+    status_t initWithHandle(const native_handle_t* inHandle, HandleWrapMethod method,
+                            uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
+                            uint32_t inLayerCount, uint64_t inUsage, uint32_t inStride);
 
     void free_handle();
 
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 1521e1d..4c9c176 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -32,7 +32,8 @@
     name: "BufferHubBuffer_test",
     header_libs: [
         "libbufferhub_headers",
-        "libdvr_headers"
+        "libdvr_headers",
+        "libnativewindow_headers",
     ],
     shared_libs: [
         "android.frameworks.bufferhub@1.0",
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
index a247e60..6af8033 100644
--- a/libs/ui/tests/BufferHubBuffer_test.cpp
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -16,7 +16,9 @@
 
 #define LOG_TAG "BufferHubBufferTest"
 
+#include <android/frameworks/bufferhub/1.0/IBufferClient.h>
 #include <android/frameworks/bufferhub/1.0/IBufferHub.h>
+#include <android/hardware_buffer.h>
 #include <gtest/gtest.h>
 #include <hidl/ServiceManagement.h>
 #include <hwbinder/IPCThreadState.h>
@@ -33,13 +35,14 @@
 const int kUsage = 0;
 const size_t kUserMetadataSize = 0;
 
-} // namespace
-
 using dvr::BufferHubDefs::IsBufferGained;
 using dvr::BufferHubDefs::kFirstClientBitMask;
 using dvr::BufferHubDefs::kMetadataHeaderSize;
+using frameworks::bufferhub::V1_0::BufferHubStatus;
+using frameworks::bufferhub::V1_0::IBufferClient;
 using frameworks::bufferhub::V1_0::IBufferHub;
 using hardware::hidl_handle;
+using hardware::graphics::common::V1_2::HardwareBufferDescription;
 using hidl::base::V1_0::IBase;
 using pdx::LocalChannelHandle;
 
@@ -123,14 +126,51 @@
     return;
 }
 
-TEST_F(BufferHubBufferTest, ConnectHidlServer) {
+TEST_F(BufferHubBufferTest, AllocateBuffer) {
+    // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
+    sp<IBufferHub> bufferHub = IBufferHub::getService();
+    ASSERT_NE(nullptr, bufferHub.get());
+
+    // Stride is an output, rfu0 and rfu1 are reserved data slot for future use.
+    AHardwareBuffer_Desc aDesc = {kWidth, kHeight,        kLayerCount,  kFormat,
+                                  kUsage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL};
+    HardwareBufferDescription desc;
+    memcpy(&desc, &aDesc, sizeof(HardwareBufferDescription));
+
+    IBufferHub::allocateBuffer_cb callback = [](const auto& client, const auto& status) {
+        EXPECT_EQ(status, BufferHubStatus::NO_ERROR);
+        EXPECT_NE(nullptr, client.get());
+    };
+    EXPECT_TRUE(bufferHub->allocateBuffer(desc, kUserMetadataSize, callback).isOk());
+}
+
+TEST_F(BufferHubBufferTest, DuplicateBuffer) {
+    // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
     sp<IBufferHub> bufferhub = IBufferHub::getService();
     ASSERT_NE(nullptr, bufferhub.get());
 
-    // TODO(b/116681016): Fill in real test once the interface gets implemented..
-    hidl_handle handle;
-    sp<IBase> interface = bufferhub->importBuffer(handle);
-    EXPECT_EQ(nullptr, interface.get());
+    // Stride is an output, rfu0 and rfu1 are reserved data slot for future use.
+    AHardwareBuffer_Desc aDesc = {kWidth, kHeight,        kLayerCount,  kFormat,
+                                  kUsage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL};
+    HardwareBufferDescription desc;
+    memcpy(&desc, &aDesc, sizeof(HardwareBufferDescription));
+
+    sp<IBufferClient> client;
+    IBufferHub::allocateBuffer_cb alloc_cb = [&](const auto& outClient, const auto& status) {
+        ASSERT_EQ(status, BufferHubStatus::NO_ERROR);
+        ASSERT_NE(nullptr, outClient.get());
+        client = outClient;
+    };
+    ASSERT_TRUE(bufferhub->allocateBuffer(desc, kUserMetadataSize, alloc_cb).isOk());
+
+    IBufferClient::duplicate_cb dup_cb = [](const auto& token, const auto& status) {
+        ASSERT_EQ(status, BufferHubStatus::NO_ERROR);
+        ASSERT_NE(token.getNativeHandle(), nullptr);
+        EXPECT_EQ(token->numInts, 1);
+        EXPECT_EQ(token->numFds, 0);
+    };
+    EXPECT_TRUE(client->duplicate(dup_cb).isOk());
 }
 
+} // namespace
 } // namespace android
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
index b8e2f9d..f7942d0 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -337,8 +337,6 @@
 
 Status<void> BufferHubQueue::Enqueue(Entry entry) {
   if (!is_full()) {
-    available_buffers_.push(std::move(entry));
-
     // Find and remove the enqueued buffer from unavailable_buffers_slot if
     // exist.
     auto enqueued_buffer_iter = std::find_if(
@@ -348,6 +346,8 @@
       unavailable_buffers_slot_.erase(enqueued_buffer_iter);
     }
 
+    available_buffers_.push(std::move(entry));
+
     // Trigger OnBufferAvailable callback if registered.
     if (on_buffer_available_)
       on_buffer_available_();
diff --git a/libs/vr/libpdx/service.cpp b/libs/vr/libpdx/service.cpp
index 68b8dd7..3769162 100644
--- a/libs/vr/libpdx/service.cpp
+++ b/libs/vr/libpdx/service.cpp
@@ -318,13 +318,7 @@
   PDX_TRACE_NAME("Message::ReplyFileHandle");
   auto svc = service_.lock();
   if (!replied_ && svc) {
-    Status<void> ret;
-
-    if (handle)
-      ret = svc->endpoint()->MessageReply(this, handle.Get());
-    else
-      ret = svc->endpoint()->MessageReply(this, handle.Get());
-
+    Status<void> ret = svc->endpoint()->MessageReply(this, handle.Get());
     replied_ = ret.ok();
     return ret;
   } else {
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 0c43b83..e27f233 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -584,7 +584,13 @@
         // The "Developer Options" value wasn't set to force the use of ANGLE.  Need to temporarily
         // load ANGLE and call the updatable opt-in/out logic:
 
-        cnx->featureSo = load_angle_from_namespace("feature_support", ns);
+        // Check if ANGLE is enabled. Workaround for several bugs:
+        // b/119305693 b/119322355 b/119305887
+        // Something is not working correctly in the feature library
+        property_get("debug.angle.enable", prop, "0");
+        if (atoi(prop)) {
+            cnx->featureSo = load_angle_from_namespace("feature_support", ns);
+        }
         if (cnx->featureSo) {
             ALOGV("loaded ANGLE's opt-in/out logic from namespace");
             use_angle = check_angle_rules(cnx->featureSo, app_name);
diff --git a/services/bufferhub/Android.bp b/services/bufferhub/Android.bp
index d03d833..8b43333 100644
--- a/services/bufferhub/Android.bp
+++ b/services/bufferhub/Android.bp
@@ -22,6 +22,7 @@
         "-Wextra",
     ],
     srcs: [
+        "BufferClient.cpp",
         "BufferHubService.cpp",
         "BufferNode.cpp",
     ],
@@ -33,6 +34,7 @@
     ],
     shared_libs: [
         "android.frameworks.bufferhub@1.0",
+        "libcutils",
         "libhidlbase",
         "libhidltransport",
         "libhwbinder",
@@ -51,9 +53,16 @@
     srcs: [
         "main_bufferhub.cpp"
     ],
+    header_libs: [
+        "libbufferhub_headers",
+        "libdvr_headers",
+        "libnativewindow_headers",
+        "libpdx_headers",
+    ],
     shared_libs: [
         "android.frameworks.bufferhub@1.0",
         "libbufferhubservice",
+        "libcutils",
         "libhidltransport",
         "libhwbinder",
         "liblog",
diff --git a/services/bufferhub/BufferClient.cpp b/services/bufferhub/BufferClient.cpp
new file mode 100644
index 0000000..37fd75f
--- /dev/null
+++ b/services/bufferhub/BufferClient.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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 <bufferhub/BufferClient.h>
+#include <bufferhub/BufferHubService.h>
+#include <hidl/HidlSupport.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+using hardware::hidl_handle;
+using hardware::Void;
+
+BufferClient* BufferClient::create(BufferHubService* service,
+                                   const std::shared_ptr<BufferNode>& node) {
+    if (!service) {
+        ALOGE("%s: service cannot be nullptr.", __FUNCTION__);
+        return nullptr;
+    } else if (!node) {
+        ALOGE("%s: node cannot be nullptr.", __FUNCTION__);
+        return nullptr;
+    }
+    return new BufferClient(service, node);
+}
+
+Return<void> BufferClient::duplicate(duplicate_cb _hidl_cb) {
+    if (!mBufferNode) {
+        // Should never happen
+        ALOGE("%s: node is missing.", __FUNCTION__);
+        _hidl_cb(/*token=*/hidl_handle(), /*status=*/BufferHubStatus::BUFFER_FREED);
+        return Void();
+    }
+
+    sp<BufferHubService> service = mService.promote();
+    if (service == nullptr) {
+        // Should never happen. Kill the process.
+        LOG_FATAL("%s: service died.", __FUNCTION__);
+    }
+
+    const hidl_handle token = service->registerToken(this);
+    _hidl_cb(/*token=*/token, /*status=*/BufferHubStatus::NO_ERROR);
+    return Void();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
\ No newline at end of file
diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
index 8be85a5..3bfd9cb 100644
--- a/services/bufferhub/BufferHubService.cpp
+++ b/services/bufferhub/BufferHubService.cpp
@@ -14,7 +14,10 @@
  * limitations under the License.
  */
 
+#include <android/hardware_buffer.h>
 #include <bufferhub/BufferHubService.h>
+#include <cutils/native_handle.h>
+#include <log/log.h>
 
 namespace android {
 namespace frameworks {
@@ -22,16 +25,56 @@
 namespace V1_0 {
 namespace implementation {
 
-using ::android::status_t;
-using ::android::hardware::Void;
+using hardware::Void;
 
-Return<void> BufferHubService::allocateBuffer(const HardwareBufferDescription& /*description*/,
-                                              allocateBuffer_cb /*hidl_cb*/) {
+Return<void> BufferHubService::allocateBuffer(const HardwareBufferDescription& description,
+                                              const uint32_t userMetadataSize,
+                                              allocateBuffer_cb _hidl_cb) {
+    AHardwareBuffer_Desc desc;
+    memcpy(&desc, &description, sizeof(AHardwareBuffer_Desc));
+
+    std::shared_ptr<BufferNode> node =
+            std::make_shared<BufferNode>(desc.width, desc.height, desc.layers, desc.format,
+                                         desc.usage, userMetadataSize);
+    if (node == nullptr || !node->IsValid()) {
+        ALOGE("%s: creating BufferNode failed.", __FUNCTION__);
+        _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::ALLOCATION_FAILED);
+        return Void();
+    }
+
+    sp<BufferClient> client = BufferClient::create(this, node);
+    // Add it to list for bookkeeping and dumpsys.
+    std::lock_guard<std::mutex> lock(mClientListMutex);
+    mClientList.push_back(client);
+
+    _hidl_cb(/*bufferClient=*/client, /*status=*/BufferHubStatus::NO_ERROR);
     return Void();
 }
 
-Return<sp<IBase>> BufferHubService::importBuffer(const hidl_handle& /*nativeHandle*/) {
-    return nullptr;
+Return<void> BufferHubService::importBuffer(const hidl_handle& /*nativeHandle*/,
+                                            importBuffer_cb _hidl_cb) {
+    // TODO(b/118614157): implement buffer import
+    _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::NO_ERROR);
+    return Void();
+}
+
+hidl_handle BufferHubService::registerToken(const BufferClient* client) {
+    uint32_t token;
+    std::lock_guard<std::mutex> lock(mTokenMapMutex);
+    do {
+        token = mTokenEngine();
+    } while (mTokenMap.find(token) != mTokenMap.end());
+
+    // native_handle_t use int[], so here need one slots to fit in uint32_t
+    native_handle_t* handle = native_handle_create(/*numFds=*/0, /*numInts=*/1);
+    handle->data[0] = token;
+
+    // returnToken owns the native_handle_t* thus doing lifecycle management
+    hidl_handle returnToken;
+    returnToken.setTo(handle, /*shoudOwn=*/true);
+
+    mTokenMap.emplace(token, client);
+    return returnToken;
 }
 
 } // namespace implementation
diff --git a/services/bufferhub/BufferNode.cpp b/services/bufferhub/BufferNode.cpp
index 62583a6..53dd702 100644
--- a/services/bufferhub/BufferNode.cpp
+++ b/services/bufferhub/BufferNode.cpp
@@ -35,7 +35,7 @@
                                                      /*requestor=*/"bufferhub");
 
     if (ret != OK || buffer_handle_ == nullptr) {
-        ALOGE("BufferNode::BufferNode: Failed to allocate buffer: %s", strerror(-ret));
+        ALOGE("%s: Failed to allocate buffer: %s", __FUNCTION__, strerror(-ret));
         return;
     }
 
@@ -48,7 +48,7 @@
 
     metadata_ = BufferHubMetadata::Create(user_metadata_size);
     if (!metadata_.IsValid()) {
-        ALOGE("BufferNode::BufferNode: Failed to allocate metadata.");
+        ALOGE("%s: Failed to allocate metadata.", __FUNCTION__);
         return;
     }
     InitializeMetadata();
@@ -59,7 +59,7 @@
     if (buffer_handle_ != nullptr) {
         status_t ret = GraphicBufferAllocator::get().free(buffer_handle_);
         if (ret != OK) {
-            ALOGE("BufferNode::~BufferNode: Failed to free handle; Got error: %d", ret);
+            ALOGE("%s: Failed to free handle; Got error: %d", __FUNCTION__, ret);
         }
     }
 }
@@ -76,8 +76,7 @@
         client_state_mask = dvr::BufferHubDefs::FindNextAvailableClientStateMask(
                 current_active_clients_bit_mask);
         if (client_state_mask == 0ULL) {
-            ALOGE("BufferNode::AddNewActiveClientsBitToMask: reached the maximum "
-                  "mumber of channels per buffer node: 32.");
+            ALOGE("%s: reached the maximum number of channels per buffer node: 32.", __FUNCTION__);
             errno = E2BIG;
             return 0ULL;
         }
diff --git a/services/bufferhub/include/bufferhub/BufferClient.h b/services/bufferhub/include/bufferhub/BufferClient.h
new file mode 100644
index 0000000..5456ec3
--- /dev/null
+++ b/services/bufferhub/include/bufferhub/BufferClient.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_CLIENT_H
+#define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_CLIENT_H
+
+#include <mutex>
+
+#include <android/frameworks/bufferhub/1.0/IBufferClient.h>
+#include <bufferhub/BufferNode.h>
+
+namespace android {
+namespace frameworks {
+namespace bufferhub {
+namespace V1_0 {
+namespace implementation {
+
+using hardware::hidl_handle;
+using hardware::Return;
+
+// Forward declaration to avoid circular dependency
+class BufferHubService;
+
+class BufferClient : public IBufferClient {
+public:
+    // Creates a server-side buffer client from an existing BufferNode. Note that
+    // this funciton takes ownership of the shared_ptr.
+    // Returns a raw pointer to the BufferClient on success, nullptr on failure.
+    static BufferClient* create(BufferHubService* service, const std::shared_ptr<BufferNode>& node);
+
+    Return<void> duplicate(duplicate_cb _hidl_cb) override;
+
+private:
+    BufferClient(wp<BufferHubService> service, const std::shared_ptr<BufferNode>& node)
+          : mService(service), mBufferNode(node){};
+
+    wp<BufferHubService> mService;
+    std::shared_ptr<BufferNode> mBufferNode;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace bufferhub
+} // namespace frameworks
+} // namespace android
+
+#endif
\ No newline at end of file
diff --git a/services/bufferhub/include/bufferhub/BufferHubService.h b/services/bufferhub/include/bufferhub/BufferHubService.h
index b273e5b..dbbee8f 100644
--- a/services/bufferhub/include/bufferhub/BufferHubService.h
+++ b/services/bufferhub/include/bufferhub/BufferHubService.h
@@ -17,8 +17,12 @@
 #ifndef ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H
 #define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H
 
+#include <mutex>
+#include <random>
+
 #include <android/frameworks/bufferhub/1.0/IBufferHub.h>
-#include <android/hardware/graphics/common/1.2/types.h>
+#include <bufferhub/BufferClient.h>
+#include <utils/Mutex.h>
 
 namespace android {
 namespace frameworks {
@@ -26,17 +30,31 @@
 namespace V1_0 {
 namespace implementation {
 
-using ::android::sp;
-using ::android::hardware::hidl_handle;
-using ::android::hardware::Return;
-using ::android::hardware::graphics::common::V1_2::HardwareBufferDescription;
-using ::android::hidl::base::V1_0::IBase;
+using hardware::hidl_handle;
+using hardware::Return;
+using hardware::graphics::common::V1_2::HardwareBufferDescription;
 
 class BufferHubService : public IBufferHub {
 public:
-    Return<void> allocateBuffer(const HardwareBufferDescription& /*description*/,
-                                allocateBuffer_cb /*hidl_cb*/) override;
-    Return<sp<IBase>> importBuffer(const hidl_handle& /*nativeHandle*/) override;
+    Return<void> allocateBuffer(const HardwareBufferDescription& description,
+                                const uint32_t userMetadataSize,
+                                allocateBuffer_cb _hidl_cb) override;
+    Return<void> importBuffer(const hidl_handle& nativeHandle, importBuffer_cb _hidl_cb) override;
+
+    // Non-binder functions
+    // Internal help function for IBufferClient::duplicate.
+    hidl_handle registerToken(const BufferClient* client);
+
+private:
+    // List of active BufferClient for bookkeeping.
+    std::mutex mClientListMutex;
+    std::vector<sp<BufferClient>> mClientList GUARDED_BY(mClientListMutex);
+
+    // TODO(b/118180214): use a more secure implementation
+    std::mt19937 mTokenEngine;
+    // The mapping from token to the client creates it.
+    std::mutex mTokenMapMutex;
+    std::map<uint32_t, const BufferClient*> mTokenMap GUARDED_BY(mTokenMapMutex);
 };
 
 } // namespace implementation
diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp
index a964d29..31057f6 100644
--- a/services/inputflinger/EventHub.cpp
+++ b/services/inputflinger/EventHub.cpp
@@ -147,8 +147,7 @@
         fd(fd), id(id), path(path), identifier(identifier),
         classes(0), configuration(nullptr), virtualKeyMap(nullptr),
         ffEffectPlaying(false), ffEffectId(-1), controllerNumber(0),
-        timestampOverrideSec(0), timestampOverrideUsec(0), enabled(true),
-        isVirtual(fd < 0) {
+        enabled(true), isVirtual(fd < 0) {
     memset(keyBitmask, 0, sizeof(keyBitmask));
     memset(absBitmask, 0, sizeof(absBitmask));
     memset(relBitmask, 0, sizeof(relBitmask));
@@ -193,8 +192,6 @@
 
 // --- EventHub ---
 
-const uint32_t EventHub::EPOLL_ID_INOTIFY;
-const uint32_t EventHub::EPOLL_ID_WAKE;
 const int EventHub::EPOLL_SIZE_HINT;
 const int EventHub::EPOLL_MAX_EVENTS;
 
@@ -217,7 +214,7 @@
     struct epoll_event eventItem;
     memset(&eventItem, 0, sizeof(eventItem));
     eventItem.events = EPOLLIN;
-    eventItem.data.u32 = EPOLL_ID_INOTIFY;
+    eventItem.data.fd = mINotifyFd;
     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
 
@@ -236,7 +233,7 @@
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
             errno);
 
-    eventItem.data.u32 = EPOLL_ID_WAKE;
+    eventItem.data.fd = mWakeReadPipeFd;
     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
             errno);
@@ -735,6 +732,16 @@
     return nullptr;
 }
 
+EventHub::Device* EventHub::getDeviceByFdLocked(int fd) const {
+    for (size_t i = 0; i < mDevices.size(); i++) {
+        Device* device = mDevices.valueAt(i);
+        if (device->fd == fd) {
+            return device;
+        }
+    }
+    return nullptr;
+}
+
 size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
     ALOG_ASSERT(bufferSize >= 1);
 
@@ -811,7 +818,7 @@
         bool deviceChanged = false;
         while (mPendingEventIndex < mPendingEventCount) {
             const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
-            if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
+            if (eventItem.data.fd == mINotifyFd) {
                 if (eventItem.events & EPOLLIN) {
                     mPendingINotify = true;
                 } else {
@@ -820,7 +827,7 @@
                 continue;
             }
 
-            if (eventItem.data.u32 == EPOLL_ID_WAKE) {
+            if (eventItem.data.fd == mWakeReadPipeFd) {
                 if (eventItem.events & EPOLLIN) {
                     ALOGV("awoken after wake()");
                     awoken = true;
@@ -836,14 +843,13 @@
                 continue;
             }
 
-            ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
-            if (deviceIndex < 0) {
-                ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
-                        eventItem.events, eventItem.data.u32);
+            Device* device = getDeviceByFdLocked(eventItem.data.fd);
+            if (device == nullptr) {
+                ALOGW("Received unexpected epoll event 0x%08x for unknown device fd %d.",
+                        eventItem.events, eventItem.data.fd);
                 continue;
             }
 
-            Device* device = mDevices.valueAt(deviceIndex);
             if (eventItem.events & EPOLLIN) {
                 int32_t readSize = read(device->fd, readBuffer,
                         sizeof(struct input_event) * capacity);
@@ -871,31 +877,6 @@
                                 (int) iev.time.tv_sec, (int) iev.time.tv_usec,
                                 iev.type, iev.code, iev.value);
 
-                        // Some input devices may have a better concept of the time
-                        // when an input event was actually generated than the kernel
-                        // which simply timestamps all events on entry to evdev.
-                        // This is a custom Android extension of the input protocol
-                        // mainly intended for use with uinput based device drivers.
-                        if (iev.type == EV_MSC) {
-                            if (iev.code == MSC_ANDROID_TIME_SEC) {
-                                device->timestampOverrideSec = iev.value;
-                                continue;
-                            } else if (iev.code == MSC_ANDROID_TIME_USEC) {
-                                device->timestampOverrideUsec = iev.value;
-                                continue;
-                            }
-                        }
-                        if (device->timestampOverrideSec || device->timestampOverrideUsec) {
-                            iev.time.tv_sec = device->timestampOverrideSec;
-                            iev.time.tv_usec = device->timestampOverrideUsec;
-                            if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
-                                device->timestampOverrideSec = 0;
-                                device->timestampOverrideUsec = 0;
-                            }
-                            ALOGV("applied override time %d.%06d",
-                                    int(iev.time.tv_sec), int(iev.time.tv_usec));
-                        }
-
                         // Use the time specified in the event instead of the current time
                         // so that downstream code can get more accurate estimates of
                         // event dispatch latency from the time the event is enqueued onto
@@ -1089,7 +1070,7 @@
     if (mUsingEpollWakeup) {
         eventItem.events |= EPOLLWAKEUP;
     }
-    eventItem.data.u32 = device->id;
+    eventItem.data.fd = device->fd;
     if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, device->fd, &eventItem)) {
         ALOGE("Could not add device fd to epoll instance.  errno=%d", errno);
         return -errno;
diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/EventHub.h
index ea663b7..e2c7e82 100644
--- a/services/inputflinger/EventHub.h
+++ b/services/inputflinger/EventHub.h
@@ -42,20 +42,6 @@
 #define BTN_FIRST 0x100  // first button code
 #define BTN_LAST 0x15f   // last button code
 
-/*
- * These constants are used privately in Android to pass raw timestamps
- * through evdev from uinput device drivers because there is currently no
- * other way to transfer this information.  The evdev driver automatically
- * timestamps all input events with the time they were posted and clobbers
- * whatever information was passed in.
- *
- * For the purposes of this hack, the timestamp is specified in the
- * CLOCK_MONOTONIC timebase and is split into two EV_MSC events specifying
- * seconds and microseconds.
- */
-#define MSC_ANDROID_TIME_SEC 0x6
-#define MSC_ANDROID_TIME_USEC 0x7
-
 namespace android {
 
 enum {
@@ -371,9 +357,6 @@
 
         int32_t controllerNumber;
 
-        int32_t timestampOverrideSec;
-        int32_t timestampOverrideUsec;
-
         Device(int fd, int32_t id, const std::string& path,
                 const InputDeviceIdentifier& identifier);
         ~Device();
@@ -418,6 +401,7 @@
     Device* getDeviceByDescriptorLocked(const std::string& descriptor) const;
     Device* getDeviceLocked(int32_t deviceId) const;
     Device* getDeviceByPathLocked(const char* devicePath) const;
+    Device* getDeviceByFdLocked(int fd) const;
 
     bool hasKeycodeLocked(Device* device, int keycode) const;
 
@@ -466,10 +450,6 @@
     int mWakeReadPipeFd;
     int mWakeWritePipeFd;
 
-    // Ids used for epoll notifications not associated with devices.
-    static const uint32_t EPOLL_ID_INOTIFY = 0x80000001;
-    static const uint32_t EPOLL_ID_WAKE = 0x80000002;
-
     // Epoll FD list size hint.
     static const int EPOLL_SIZE_HINT = 8;
 
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 2e984d9..3f39afd 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -864,11 +864,11 @@
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
             "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
-            "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
+            "metaState=0x%x, repeatCount=%d, downTime=%" PRId64 ", displayId=%" PRId32,
             prefix,
             entry->eventTime, entry->deviceId, entry->source, entry->displayId, entry->policyFlags,
             entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
-            entry->repeatCount, entry->downTime);
+            entry->repeatCount, entry->downTime, entry->displayId);
 #endif
 }
 
@@ -940,13 +940,13 @@
             ", policyFlags=0x%x, "
             "action=0x%x, actionButton=0x%x, flags=0x%x, "
             "metaState=0x%x, buttonState=0x%x,"
-            "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
+            "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64", displayId=%" PRId32,
             prefix,
             entry->eventTime, entry->deviceId, entry->source, entry->displayId, entry->policyFlags,
             entry->action, entry->actionButton, entry->flags,
             entry->metaState, entry->buttonState,
             entry->edgeFlags, entry->xPrecision, entry->yPrecision,
-            entry->downTime);
+            entry->downTime, entry->displayId);
 
     for (uint32_t i = 0; i < entry->pointerCount; i++) {
         ALOGD("  Pointer %d: id=%d, toolType=%d, "
@@ -1168,7 +1168,8 @@
             goto Unresponsive;
         }
 
-        ALOGI("Dropping event because there is no focused window or focused application.");
+        ALOGI("Dropping event because there is no focused window or focused application in display "
+                "%" PRId32 ".", displayId);
         injectionResult = INPUT_EVENT_INJECTION_FAILED;
         goto Failed;
     }
@@ -1254,7 +1255,8 @@
         bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
         if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) {
 #if DEBUG_FOCUS
-            ALOGD("Dropping event because a pointer for a different device is already down.");
+            ALOGD("Dropping event because a pointer for a different device is already down "
+                    "in display %" PRId32, displayId);
 #endif
             // TODO: test multiple simultaneous input streams.
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
@@ -1270,7 +1272,8 @@
         isSplit = false;
     } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
 #if DEBUG_FOCUS
-        ALOGI("Dropping move event because a pointer for a different device is already active.");
+        ALOGI("Dropping move event because a pointer for a different device is already active "
+                "in display %" PRId32, displayId);
 #endif
         // TODO: test multiple simultaneous input streams.
         injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
@@ -1335,7 +1338,8 @@
             // Try to assign the pointer to the first foreground window we find, if there is one.
             newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle();
             if (newTouchedWindowHandle == nullptr) {
-                ALOGI("Dropping event because there is no touchable window at (%d, %d).", x, y);
+                ALOGI("Dropping event because there is no touchable window at (%d, %d) in display "
+                        "%" PRId32 ".", x, y, displayId);
                 injectionResult = INPUT_EVENT_INJECTION_FAILED;
                 goto Failed;
             }
@@ -1373,7 +1377,7 @@
         if (! mTempTouchState.down) {
 #if DEBUG_FOCUS
             ALOGD("Dropping event because the pointer is not down or we previously "
-                    "dropped the pointer down event.");
+                    "dropped the pointer down event in display %" PRId32, displayId);
 #endif
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             goto Failed;
@@ -1393,9 +1397,10 @@
             if (oldTouchedWindowHandle != newTouchedWindowHandle
                     && newTouchedWindowHandle != nullptr) {
 #if DEBUG_FOCUS
-                ALOGD("Touch is slipping out of window %s into window %s.",
+                ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
                         oldTouchedWindowHandle->getName().c_str(),
-                        newTouchedWindowHandle->getName().c_str());
+                        newTouchedWindowHandle->getName().c_str(),
+                        displayId);
 #endif
                 // Make a slippery exit from the old window.
                 mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
@@ -1464,7 +1469,8 @@
         }
         if (! haveForegroundWindow) {
 #if DEBUG_FOCUS
-            ALOGD("Dropping event because there is no touched foreground window to receive it.");
+            ALOGD("Dropping event because there is no touched foreground window in display %" PRId32
+                    " to receive it.", displayId);
 #endif
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             goto Failed;
@@ -1689,7 +1695,7 @@
     } else {
         // If there is no monitor channel registered or all monitor channel unregistered,
         // the display can't detect the extra system gesture by a copy of input events.
-        ALOGW("There is no monitor channel found in display=%" PRId32, displayId);
+        ALOGW("There is no monitor channel found in display %" PRId32, displayId);
     }
 }
 
@@ -2533,10 +2539,11 @@
 #if DEBUG_INBOUND_EVENT_DETAILS
     ALOGD("notifyKey - eventTime=%" PRId64
             ", deviceId=%d, source=0x%x, displayId=%" PRId32 "policyFlags=0x%x, action=0x%x, "
-            "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
+            "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64
+            ", displayId=%" PRId32,
             args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
             args->action, args->flags, args->keyCode, args->scanCode,
-            args->metaState, args->downTime);
+            args->metaState, args->downTime, args->displayId);
 #endif
     if (!validateKeyEvent(args->action)) {
         return;
@@ -2611,10 +2618,10 @@
     ALOGD("notifyMotion - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
             ", policyFlags=0x%x, "
             "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x,"
-            "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
-            args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
+            "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", displayId=%" PRId32
+            , args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
             args->action, args->actionButton, args->flags, args->metaState, args->buttonState,
-            args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime);
+            args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime, args->displayId);
     for (uint32_t i = 0; i < args->pointerCount; i++) {
         ALOGD("  Pointer %d: id=%d, toolType=%d, "
                 "x=%f, y=%f, pressure=%f, size=%f, "
@@ -3013,9 +3020,10 @@
         for (size_t i = 0; i < numWindows; i++) {
             if (windowHandles.itemAt(i) == windowHandle) {
                 if (windowHandle->getInfo()->displayId != it.first) {
-                    ALOGE("Found window %s in display %d, but it should belong to display %d",
-                        windowHandle->getName().c_str(), it.first,
-                        windowHandle->getInfo()->displayId);
+                    ALOGE("Found window %s in display %" PRId32
+                            ", but it should belong to display %" PRId32,
+                            windowHandle->getName().c_str(), it.first,
+                            windowHandle->getInfo()->displayId);
                 }
                 return true;
             }
@@ -3034,7 +3042,7 @@
 void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle>>& inputWindowHandles,
         int32_t displayId) {
 #if DEBUG_FOCUS
-    ALOGD("setInputWindows");
+    ALOGD("setInputWindows displayId=%" PRId32, displayId);
 #endif
     { // acquire lock
         AutoMutex _l(mLock);
@@ -3085,8 +3093,8 @@
         if (oldFocusedWindowHandle != newFocusedWindowHandle) {
             if (oldFocusedWindowHandle != nullptr) {
 #if DEBUG_FOCUS
-                ALOGD("Focus left window: %s",
-                        oldFocusedWindowHandle->getName().c_str());
+                ALOGD("Focus left window: %s in display %" PRId32,
+                        oldFocusedWindowHandle->getName().c_str(), displayId);
 #endif
                 sp<InputChannel> focusedInputChannel = oldFocusedWindowHandle->getInputChannel();
                 if (focusedInputChannel != nullptr) {
@@ -3099,8 +3107,8 @@
             }
             if (newFocusedWindowHandle != nullptr) {
 #if DEBUG_FOCUS
-                ALOGD("Focus entered window: %s",
-                        newFocusedWindowHandle->getName().c_str());
+                ALOGD("Focus entered window: %s in display %" PRId32,
+                        newFocusedWindowHandle->getName().c_str(), displayId);
 #endif
                 mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
             }
@@ -3113,8 +3121,8 @@
                 TouchedWindow& touchedWindow = state.windows.editItemAt(i);
                 if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
 #if DEBUG_FOCUS
-                    ALOGD("Touched window was removed: %s",
-                            touchedWindow.windowHandle->getName().c_str());
+                    ALOGD("Touched window was removed: %s in display %" PRId32,
+                            touchedWindow.windowHandle->getName().c_str(), displayId);
 #endif
                     sp<InputChannel> touchedInputChannel =
                             touchedWindow.windowHandle->getInputChannel();
@@ -3142,7 +3150,7 @@
 #if DEBUG_FOCUS
                 ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
 #endif
-                oldWindowHandle->releaseInfo();
+                oldWindowHandle->releaseChannel();
             }
         }
     } // release lock
@@ -3154,7 +3162,7 @@
 void InputDispatcher::setFocusedApplication(
         int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) {
 #if DEBUG_FOCUS
-    ALOGD("setFocusedApplication");
+    ALOGD("setFocusedApplication displayId=%" PRId32, displayId);
 #endif
     { // acquire lock
         AutoMutex _l(mLock);
@@ -3469,7 +3477,7 @@
     if (!mWindowHandlesByDisplay.empty()) {
        for (auto& it : mWindowHandlesByDisplay) {
             const Vector<sp<InputWindowHandle>> windowHandles = it.second;
-            dump += StringPrintf(INDENT "Display: %d\n", it.first);
+            dump += StringPrintf(INDENT "Display: %" PRId32 "\n", it.first);
             if (!windowHandles.isEmpty()) {
                 dump += INDENT2 "Windows:\n";
                 for (size_t i = 0; i < windowHandles.size(); i++) {
@@ -3509,7 +3517,7 @@
     if (!mMonitoringChannelsByDisplay.empty()) {
        for (auto& it : mMonitoringChannelsByDisplay) {
             const Vector<sp<InputChannel>>& monitoringChannels = it.second;
-            dump += INDENT "MonitoringChannels in Display %d:\n";
+            dump += StringPrintf(INDENT "MonitoringChannels in display %" PRId32 ":\n", it.first);
             const size_t numChannels = monitoringChannels.size();
             for (size_t i = 0; i < numChannels; i++) {
                 const sp<InputChannel>& channel = monitoringChannels[i];
@@ -3737,6 +3745,10 @@
 }
 
 ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
+    if (!inputChannel) {
+        return -1;
+    }
+
     ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());
     if (connectionIndex >= 0) {
         sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
@@ -3744,7 +3756,6 @@
             return connectionIndex;
         }
     }
-
     return -1;
 }
 
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 869bd71..e85e6ef 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -3480,8 +3480,8 @@
     }
 
     // Raw width and height in the natural orientation.
-    int32_t rawWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
-    int32_t rawHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
+    int32_t rawWidth = mRawPointerAxes.getRawWidth();
+    int32_t rawHeight = mRawPointerAxes.getRawHeight();
 
     // Get associated display dimensions.
     DisplayViewport newViewport;
@@ -3913,8 +3913,8 @@
 
     int32_t touchScreenLeft = mRawPointerAxes.x.minValue;
     int32_t touchScreenTop = mRawPointerAxes.y.minValue;
-    int32_t touchScreenWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
-    int32_t touchScreenHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
+    int32_t touchScreenWidth = mRawPointerAxes.getRawWidth();
+    int32_t touchScreenHeight = mRawPointerAxes.getRawHeight();
 
     for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
         const VirtualKeyDefinition& virtualKeyDefinition =
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 3410bc9..9b0007a 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -772,6 +772,8 @@
     RawAbsoluteAxisInfo slot;
 
     RawPointerAxes();
+    inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; }
+    inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; }
     void clear();
 };
 
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index c6eaf9f..066c143 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -370,30 +370,27 @@
     }
 
     virtual bool updateInfo() {
-        if (!mInfo) {
-            mInfo = new InputWindowInfo();
-        }
-        mInfo->inputChannel = mServerChannel;
-        mInfo->name = mName;
-        mInfo->layoutParamsFlags = 0;
-        mInfo->layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
-        mInfo->dispatchingTimeout = DISPATCHING_TIMEOUT;
-        mInfo->frameLeft = 0;
-        mInfo->frameTop = 0;
-        mInfo->frameRight = WIDTH;
-        mInfo->frameBottom = HEIGHT;
-        mInfo->scaleFactor = 1.0;
-        mInfo->addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
-        mInfo->visible = true;
-        mInfo->canReceiveKeys = true;
-        mInfo->hasFocus = mFocused;
-        mInfo->hasWallpaper = false;
-        mInfo->paused = false;
-        mInfo->layer = 0;
-        mInfo->ownerPid = INJECTOR_PID;
-        mInfo->ownerUid = INJECTOR_UID;
-        mInfo->inputFeatures = 0;
-        mInfo->displayId = mDisplayId;
+        mInfo.inputChannel = mServerChannel;
+        mInfo.name = mName;
+        mInfo.layoutParamsFlags = 0;
+        mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+        mInfo.frameLeft = 0;
+        mInfo.frameTop = 0;
+        mInfo.frameRight = WIDTH;
+        mInfo.frameBottom = HEIGHT;
+        mInfo.scaleFactor = 1.0;
+        mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+        mInfo.visible = true;
+        mInfo.canReceiveKeys = true;
+        mInfo.hasFocus = mFocused;
+        mInfo.hasWallpaper = false;
+        mInfo.paused = false;
+        mInfo.layer = 0;
+        mInfo.ownerPid = INJECTOR_PID;
+        mInfo.ownerUid = INJECTOR_UID;
+        mInfo.inputFeatures = 0;
+        mInfo.displayId = mDisplayId;
 
         return true;
     }
@@ -529,6 +526,34 @@
     windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
 }
 
+TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+
+    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
+            ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
+            ADISPLAY_ID_DEFAULT);
+
+    windowTop->setFocus();
+
+    Vector<sp<InputWindowHandle>> inputWindowHandles;
+    inputWindowHandles.add(windowTop);
+    inputWindowHandles.add(windowSecond);
+
+    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+
+    // Release channel for window is no longer valid.
+    windowTop->releaseChannel();
+
+    // Test inject a motion down, should timeout because of no target channel.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
+            << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT";
+
+    // Top window is invalid, so it should not receive any input event.
+    windowTop->assertNoEvents();
+    windowSecond->assertNoEvents();
+}
+
 /* Test InputDispatcher for MultiDisplay */
 class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
 public:
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index e8092a9..f168db9 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -146,6 +146,7 @@
         "SurfaceInterceptor.cpp",
         "SurfaceTracing.cpp",
         "TimeStats/TimeStats.cpp",
+        "TransactionCompletedThread.cpp",
     ],
 }
 
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 641bd8d..ade62bf 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -231,15 +231,12 @@
 
 void BufferLayer::setPerFrameData(DisplayId displayId, const ui::Transform& transform,
                                   const Rect& viewport, int32_t supportedPerFrameMetadata) {
+    RETURN_IF_NO_HWC_LAYER(displayId);
+
     // Apply this display's projection's viewport to the visible region
     // before giving it to the HWC HAL.
     Region visible = transform.transform(visibleRegion.intersect(viewport));
 
-    if (!hasHwcLayer(displayId)) {
-        ALOGE("[%s] failed to setPerFrameData: no HWC layer found for display %" PRIu64,
-              mName.string(), displayId);
-        return;
-    }
     auto& hwcInfo = getBE().mHwcLayers[displayId];
     auto& hwcLayer = hwcInfo.layer;
     auto error = hwcLayer->setVisibleRegion(visible);
@@ -520,7 +517,7 @@
 }
 
 bool BufferLayer::hasReadyFrame() const {
-    return hasDrawingBuffer() || getSidebandStreamChanged() || getAutoRefresh();
+    return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
 }
 
 uint32_t BufferLayer::getEffectiveScalingMode() const {
@@ -658,13 +655,43 @@
 }
 
 uint64_t BufferLayer::getHeadFrameNumber() const {
-    if (hasDrawingBuffer()) {
+    if (hasFrameUpdate()) {
         return getFrameNumber();
     } else {
         return mCurrentFrameNumber;
     }
 }
 
+Rect BufferLayer::getBufferSize(const State& s) const {
+    // If we have a sideband stream, or we are scaling the buffer then return the layer size since
+    // we cannot determine the buffer size.
+    if ((s.sidebandStream != nullptr) ||
+        (getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE)) {
+        return Rect(getActiveWidth(s), getActiveHeight(s));
+    }
+
+    if (mActiveBuffer == nullptr) {
+        return Rect::INVALID_RECT;
+    }
+
+    uint32_t bufWidth = mActiveBuffer->getWidth();
+    uint32_t bufHeight = mActiveBuffer->getHeight();
+
+    // Undo any transformations on the buffer and return the result.
+    if (mCurrentTransform & ui::Transform::ROT_90) {
+        std::swap(bufWidth, bufHeight);
+    }
+
+    if (getTransformToDisplayInverse()) {
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufWidth, bufHeight);
+        }
+    }
+
+    return Rect(bufWidth, bufHeight);
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index b3ea7e6..690a4e5 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -136,7 +136,7 @@
 
     virtual std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) = 0;
 
-    virtual bool hasDrawingBuffer() const = 0;
+    virtual bool hasFrameUpdate() const = 0;
 
     virtual void setFilteringEnabled(bool enabled) = 0;
 
@@ -188,6 +188,8 @@
     mutable renderengine::Texture mTexture;
 
     bool mRefreshPending{false};
+
+    Rect getBufferSize(const State& s) const override;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index e10548f..78ab23a 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -82,7 +82,7 @@
         return true;
     }
 
-    if (!hasDrawingBuffer()) {
+    if (!hasFrameUpdate()) {
         return false;
     }
 
@@ -110,7 +110,7 @@
         return true;
     }
 
-    if (!hasDrawingBuffer()) {
+    if (!hasFrameUpdate()) {
         return true;
     }
 
@@ -206,7 +206,7 @@
     return {};
 }
 
-bool BufferQueueLayer::hasDrawingBuffer() const {
+bool BufferQueueLayer::hasFrameUpdate() const {
     return mQueuedFrames > 0;
 }
 
@@ -352,6 +352,11 @@
 void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
     // Add this buffer from our internal queue tracker
     { // Autolock scope
+        // Report the timestamp to the Scheduler.
+        if (mFlinger->mUseScheduler) {
+            mFlinger->mScheduler->addNewFrameTimestamp(item.mTimestamp, item.mIsAutoTimestamp);
+        }
+
         Mutex::Autolock lock(mQueueItemLock);
         // Reset the frame number tracker when we receive the first buffer after
         // a frame number reset
@@ -402,7 +407,7 @@
             }
         }
 
-        if (!hasDrawingBuffer()) {
+        if (!hasFrameUpdate()) {
             ALOGE("Can't replace a frame on an empty queue");
             return;
         }
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 74dd21e..ae0b705 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -83,7 +83,7 @@
 
     std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) override;
 
-    bool hasDrawingBuffer() const override;
+    bool hasFrameUpdate() const override;
 
     void setFilteringEnabled(bool enabled) override;
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 8d59841..425f5c7 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -35,15 +35,19 @@
 };
 // clang-format on
 
-BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) : BufferLayer(args) {}
+BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) : BufferLayer(args) {
+    mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+}
 BufferStateLayer::~BufferStateLayer() = default;
 
 // -----------------------------------------------------------------------
 // Interface implementation for Layer
 // -----------------------------------------------------------------------
-void BufferStateLayer::onLayerDisplayed(const sp<Fence>& /*releaseFence*/) {
-    // TODO(marissaw): send the release fence back to buffer owner
-    return;
+void BufferStateLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
+    // The transaction completed callback can only be sent if the release fence from the PREVIOUS
+    // frame has fired. In practice, we should never actually wait on the previous release fence
+    // but we should store it just in case.
+    mPreviousReleaseFence = releaseFence;
 }
 
 void BufferStateLayer::setTransformHint(uint32_t /*orientation*/) const {
@@ -52,7 +56,6 @@
 }
 
 void BufferStateLayer::releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) {
-    // TODO(marissaw): use this to signal the buffer owner
     return;
 }
 
@@ -61,7 +64,13 @@
         return true;
     }
 
-    return hasDrawingBuffer();
+    return hasFrameUpdate();
+}
+
+bool BufferStateLayer::willPresentCurrentTransaction() const {
+    // Returns true if the most recent Transaction applied to CurrentState will be presented.
+    return getSidebandStreamChanged() || getAutoRefresh() ||
+            (mCurrentState.modified && mCurrentState.buffer != nullptr);
 }
 
 bool BufferStateLayer::getTransformToDisplayInverse() const {
@@ -81,6 +90,7 @@
     while (!mPendingStates.empty()) {
         popPendingState(stateToCommit);
     }
+    mCurrentStateModified = stateUpdateAvailable && mCurrentState.modified;
     mCurrentState.modified = false;
     return stateUpdateAvailable;
 }
@@ -118,7 +128,11 @@
     return true;
 }
 
-bool BufferStateLayer::setBuffer(sp<GraphicBuffer> buffer) {
+bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer) {
+    if (mCurrentState.buffer) {
+        mReleasePreviousBuffer = true;
+    }
+
     mCurrentState.sequence++;
     mCurrentState.buffer = buffer;
     mCurrentState.modified = true;
@@ -127,6 +141,9 @@
 }
 
 bool BufferStateLayer::setAcquireFence(const sp<Fence>& fence) {
+    // The acquire fences of BufferStateLayers have already signaled before they are set
+    mCallbackHandleAcquireTime = fence->getSignalTime();
+
     mCurrentState.acquireFence = fence;
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
@@ -182,6 +199,44 @@
     return true;
 }
 
+bool BufferStateLayer::setTransactionCompletedListeners(
+        const std::vector<sp<CallbackHandle>>& handles) {
+    // If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return
+    if (handles.empty()) {
+        mReleasePreviousBuffer = false;
+        return false;
+    }
+
+    const bool willPresent = willPresentCurrentTransaction();
+
+    for (const auto& handle : handles) {
+        // If this transaction set a buffer on this layer, release its previous buffer
+        handle->releasePreviousBuffer = mReleasePreviousBuffer;
+
+        // If this layer will be presented in this frame
+        if (willPresent) {
+            // If this transaction set an acquire fence on this layer, set its acquire time
+            handle->acquireTime = mCallbackHandleAcquireTime;
+
+            // Notify the transaction completed thread that there is a pending latched callback
+            // handle
+            mFlinger->getTransactionCompletedThread().registerPendingLatchedCallbackHandle(handle);
+
+            // Store so latched time and release fence can be set
+            mCurrentState.callbackHandles.push_back(handle);
+
+        } else { // If this layer will NOT need to be relatched and presented this frame
+            // Notify the transaction completed thread this handle is done
+            mFlinger->getTransactionCompletedThread().addUnlatchedCallbackHandle(handle);
+        }
+    }
+
+    mReleasePreviousBuffer = false;
+    mCallbackHandleAcquireTime = -1;
+
+    return willPresent;
+}
+
 bool BufferStateLayer::setSize(uint32_t w, uint32_t h) {
     if (mCurrentState.active.w == w && mCurrentState.active.h == h) return false;
     mCurrentState.active.w = w;
@@ -265,7 +320,7 @@
 }
 
 uint32_t BufferStateLayer::getDrawingScalingMode() const {
-    return NATIVE_WINDOW_SCALING_MODE_FREEZE;
+    return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
 }
 
 Region BufferStateLayer::getDrawingSurfaceDamage() const {
@@ -314,8 +369,8 @@
     return {};
 }
 
-bool BufferStateLayer::hasDrawingBuffer() const {
-    return getDrawingState().buffer != nullptr;
+bool BufferStateLayer::hasFrameUpdate() const {
+    return mCurrentStateModified && getCurrentState().buffer != nullptr;
 }
 
 void BufferStateLayer::setFilteringEnabled(bool enabled) {
@@ -394,7 +449,7 @@
         }
     }
 
-    if (mOverrideScalingMode == NATIVE_WINDOW_SCALING_MODE_FREEZE &&
+    if (getEffectiveScalingMode() == NATIVE_WINDOW_SCALING_MODE_FREEZE &&
         (s.active.w != bufferWidth || s.active.h != bufferHeight)) {
         ALOGE("[%s] rejecting buffer: "
               "bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
@@ -403,6 +458,10 @@
         return BAD_VALUE;
     }
 
+    mFlinger->getTransactionCompletedThread()
+            .addLatchedCallbackHandles(getDrawingState().callbackHandles, latchTime,
+                                       mPreviousReleaseFence);
+
     // Handle sync fences
     if (SyncFeatures::getInstance().useNativeFenceSync() && releaseFence != Fence::NO_FENCE) {
         // TODO(alecmouri): Fail somewhere upstream if the fence is invalid.
@@ -512,6 +571,7 @@
               s.buffer->handle, to_string(error).c_str(), static_cast<int32_t>(error));
     }
 
+    mCurrentStateModified = false;
     mFrameNumber++;
 }
 
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 381cd28..315d5af 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -62,13 +62,14 @@
     bool setTransform(uint32_t transform) override;
     bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
     bool setCrop(const Rect& crop) override;
-    bool setBuffer(sp<GraphicBuffer> buffer) override;
+    bool setBuffer(const sp<GraphicBuffer>& buffer) override;
     bool setAcquireFence(const sp<Fence>& fence) override;
     bool setDataspace(ui::Dataspace dataspace) override;
     bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
     bool setSurfaceDamageRegion(const Region& surfaceDamage) override;
     bool setApi(int32_t api) override;
     bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override;
+    bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
 
     bool setSize(uint32_t w, uint32_t h) override;
     bool setPosition(float x, float y, bool immediate) override;
@@ -110,7 +111,7 @@
 
     std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) override;
 
-    bool hasDrawingBuffer() const override;
+    bool hasFrameUpdate() const override;
 
     void setFilteringEnabled(bool enabled) override;
 
@@ -125,6 +126,7 @@
 
 private:
     void onFirstRef() override;
+    bool willPresentCurrentTransaction() const;
 
     static const std::array<float, 16> IDENTITY_MATRIX;
 
@@ -136,6 +138,12 @@
 
     uint32_t mFrameNumber{0};
 
+    sp<Fence> mPreviousReleaseFence;
+
+    bool mCurrentStateModified = false;
+    bool mReleasePreviousBuffer = false;
+    nsecs_t mCallbackHandleAcquireTime = -1;
+
     // TODO(marissaw): support sticky transform for LEGACY camera mode
 };
 
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index ad716ef..9c34308 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -59,13 +59,10 @@
 
 void ColorLayer::setPerFrameData(DisplayId displayId, const ui::Transform& transform,
                                  const Rect& viewport, int32_t /* supportedPerFrameMetadata */) {
+    RETURN_IF_NO_HWC_LAYER(displayId);
+
     Region visible = transform.transform(visibleRegion.intersect(viewport));
 
-    if (!hasHwcLayer(displayId)) {
-        ALOGE("[%s] failed to setPerFrameData: no HWC layer found for display %" PRIu64,
-              mName.string(), displayId);
-        return;
-    }
     auto& hwcInfo = getBE().mHwcLayers[displayId];
     auto& hwcLayer = hwcInfo.layer;
     auto error = hwcLayer->setVisibleRegion(visible);
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index be19b9f..5342bcf 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -642,7 +642,7 @@
 }
 
 std::string DisplayDevice::getDebugName() const {
-    const auto id = mId ? base::StringPrintf("%" PRIu64 ", ", *mId) : std::string();
+    const auto id = mId ? to_string(*mId) + ", " : std::string();
     return base::StringPrintf("DisplayDevice{%s%s%s\"%s\"}", id.c_str(),
                               isPrimary() ? "primary, " : "", isVirtual() ? "virtual, " : "",
                               mDisplayName.c_str());
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
index ec240f3..ba7818d 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -64,12 +64,16 @@
     return letter < 'A' || letter > 'Z' ? '\0' : letter;
 }
 
-DisplayId getEdidDisplayId(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash) {
-    return (static_cast<DisplayId>(manufacturerId) << 40) |
-            (static_cast<DisplayId>(displayNameHash) << 8) | port;
+} // namespace
+
+uint16_t DisplayId::manufacturerId() const {
+    return static_cast<uint16_t>(value >> 40);
 }
 
-} // namespace
+DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash) {
+    return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(displayNameHash) << 8) |
+            port};
+}
 
 bool isEdid(const DisplayIdentificationData& data) {
     const uint8_t kMagic[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0};
@@ -168,6 +172,10 @@
     return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt;
 }
 
+std::optional<PnpId> getPnpId(DisplayId displayId) {
+    return getPnpId(displayId.manufacturerId());
+}
+
 std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
         uint8_t port, const DisplayIdentificationData& data) {
     if (!isEdid(data)) {
@@ -183,16 +191,16 @@
     // Hash display name instead of using product code or serial number, since the latter have been
     // observed to change on some displays with multiple inputs.
     const auto hash = static_cast<uint32_t>(std::hash<std::string_view>()(edid->displayName));
-    return DisplayIdentificationInfo{getEdidDisplayId(port, edid->manufacturerId, hash),
+    return DisplayIdentificationInfo{DisplayId::fromEdid(port, edid->manufacturerId, hash),
                                      std::string(edid->displayName)};
 }
 
 DisplayId getFallbackDisplayId(uint8_t port) {
-    return getEdidDisplayId(port, kFallbackEdidManufacturerId, 0);
+    return DisplayId::fromEdid(port, kFallbackEdidManufacturerId, 0);
 }
 
 DisplayId getVirtualDisplayId(uint32_t id) {
-    return getEdidDisplayId(0, kVirtualEdidManufacturerId, id);
+    return DisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
index 1f2e789..1599995 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
@@ -25,7 +25,27 @@
 
 namespace android {
 
-using DisplayId = uint64_t;
+struct DisplayId {
+    using Type = uint64_t;
+    Type value;
+
+    uint16_t manufacturerId() const;
+
+    static DisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash);
+};
+
+inline bool operator==(DisplayId lhs, DisplayId rhs) {
+    return lhs.value == rhs.value;
+}
+
+inline bool operator!=(DisplayId lhs, DisplayId rhs) {
+    return !(lhs == rhs);
+}
+
+inline std::string to_string(DisplayId displayId) {
+    return std::to_string(displayId.value);
+}
+
 using DisplayIdentificationData = std::vector<uint8_t>;
 
 struct DisplayIdentificationInfo {
@@ -45,6 +65,7 @@
 bool isEdid(const DisplayIdentificationData&);
 std::optional<Edid> parseEdid(const DisplayIdentificationData&);
 std::optional<PnpId> getPnpId(uint16_t manufacturerId);
+std::optional<PnpId> getPnpId(DisplayId);
 
 std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
         uint8_t port, const DisplayIdentificationData&);
@@ -53,3 +74,14 @@
 DisplayId getVirtualDisplayId(uint32_t id);
 
 } // namespace android
+
+namespace std {
+
+template <>
+struct hash<android::DisplayId> {
+    size_t operator()(android::DisplayId displayId) const {
+        return hash<android::DisplayId::Type>()(displayId.value);
+    }
+};
+
+} // namespace std
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index f3f8d9d..27812f7 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -63,7 +63,7 @@
         mHasPendingRelease(false),
         mPreviousBufferSlot(BufferQueue::INVALID_BUFFER_SLOT),
         mPreviousBuffer() {
-    ALOGV("Creating for display %" PRIu64, displayId);
+    ALOGV("Creating for display %s", to_string(displayId).c_str());
 
     mName = "FramebufferSurface";
     mConsumer->setConsumerName(mName);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 4c472b8..0f25b52 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -58,11 +58,11 @@
     ALOGE("%s failed for HWC display %" PRIu64 ": %s", __FUNCTION__, hwcDisplayId, msg)
 
 #define LOG_DISPLAY_ERROR(displayId, msg) \
-    ALOGE("%s failed for display %" PRIu64 ": %s", __FUNCTION__, displayId, msg)
+    ALOGE("%s failed for display %s: %s", __FUNCTION__, to_string(displayId).c_str(), msg)
 
-#define LOG_HWC_ERROR(what, error, displayId)                                              \
-    ALOGE("%s: %s failed for display %" PRIu64 ": %s (%d)", __FUNCTION__, what, displayId, \
-          to_string(error).c_str(), static_cast<int32_t>(error))
+#define LOG_HWC_ERROR(what, error, displayId)                          \
+    ALOGE("%s: %s failed for display %s: %s (%d)", __FUNCTION__, what, \
+          to_string(displayId).c_str(), to_string(error).c_str(), static_cast<int32_t>(error))
 
 #define RETURN_IF_INVALID_DISPLAY(displayId, ...)            \
     do {                                                     \
@@ -159,9 +159,9 @@
         if (!info) return {};
     }
 
-    ALOGV("%s: %s %s display %" PRIu64 " with HWC ID %" PRIu64, __FUNCTION__,
-          to_string(connection).c_str(),
-          hwcDisplayId == mInternalHwcDisplayId ? "internal" : "external", info->id, hwcDisplayId);
+    ALOGV("%s: %s %s display %s with HWC ID %" PRIu64, __FUNCTION__, to_string(connection).c_str(),
+          hwcDisplayId == mInternalHwcDisplayId ? "internal" : "external",
+          to_string(info->id).c_str(), hwcDisplayId);
 
     mHwcDevice->onHotplug(hwcDisplayId, connection);
 
@@ -198,15 +198,15 @@
         // is a bug in the HWC implementation, but filter the extra events
         // out here so they don't cause havoc downstream.
         if (timestamp == mLastHwVSync[*displayId]) {
-            ALOGW("Ignoring duplicate VSYNC event from HWC for display %" PRIu64 " (t=%" PRId64 ")",
-                  *displayId, timestamp);
+            ALOGW("Ignoring duplicate VSYNC event from HWC for display %s (t=%" PRId64 ")",
+                  to_string(*displayId).c_str(), timestamp);
             return false;
         }
 
         mLastHwVSync[*displayId] = timestamp;
     }
 
-    const auto tag = "HW_VSYNC_" + std::to_string(*displayId);
+    const auto tag = "HW_VSYNC_" + to_string(*displayId);
     ATRACE_INT(tag.c_str(), ++mVSyncCounts[*displayId] & 1);
 
     return true;
@@ -383,7 +383,7 @@
 
         displayData.vsyncEnabled = enabled;
 
-        const auto tag = "HW_VSYNC_ON_" + std::to_string(displayId);
+        const auto tag = "HW_VSYNC_ON_" + to_string(displayId);
         ATRACE_INT(tag.c_str(), enabled == HWC2::Vsync::Enable ? 1 : 0);
     }
 }
@@ -393,7 +393,7 @@
                                      ui::Dataspace dataspace) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
-    ALOGV("setClientTarget for display %" PRIu64, displayId);
+    ALOGV("%s for display %s", __FUNCTION__, to_string(displayId).c_str());
     auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
     auto error = hwcDisplay->setClientTarget(slot, target, acquireFence, dataspace);
     RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 5074c2c..b78433d 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -233,7 +233,7 @@
     cb_context* mCBContext = nullptr;
     std::unordered_map<DisplayId, size_t> mVSyncCounts;
 
-    std::unordered_set<uint32_t> mFreeVirtualDisplayIds;
+    std::unordered_set<DisplayId> mFreeVirtualDisplayIds;
     uint32_t mNextVirtualDisplayId = 0;
     uint32_t mRemainingHwcVirtualDisplays{mHwcDevice->getMaxVirtualDisplayCount()};
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index f41a753..038bc58 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -197,8 +197,8 @@
 // ---------------------------------------------------------------------------
 
 bool Layer::createHwcLayer(HWComposer* hwc, DisplayId displayId) {
-    LOG_ALWAYS_FATAL_IF(getBE().mHwcLayers.count(displayId) != 0,
-                        "Already have a layer for display %" PRIu64, displayId);
+    LOG_ALWAYS_FATAL_IF(hasHwcLayer(displayId), "Already have a layer for display %s",
+                        to_string(displayId).c_str());
     auto layer = std::shared_ptr<HWC2::Layer>(
             hwc->createLayer(displayId),
             [hwc, displayId](HWC2::Layer* layer) {
@@ -215,7 +215,7 @@
 }
 
 bool Layer::destroyHwcLayer(DisplayId displayId) {
-    if (getBE().mHwcLayers.count(displayId) == 0) {
+    if (!hasHwcLayer(displayId)) {
         return false;
     }
     auto& hwcInfo = getBE().mHwcLayers[displayId];
@@ -275,39 +275,12 @@
     return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect();
 }
 
-Rect Layer::computeScreenBounds(bool reduceTransparentRegion) const {
-    const State& s(getDrawingState());
-    Rect win(getActiveWidth(s), getActiveHeight(s));
-
-    Rect crop = getCrop(s);
-    if (!crop.isEmpty()) {
-        win.intersect(crop, &win);
-    }
-
+Rect Layer::computeScreenBounds() const {
+    FloatRect bounds = computeBounds();
     ui::Transform t = getTransform();
-    win = t.transform(win);
-
-    const sp<Layer>& p = mDrawingParent.promote();
-    // Now we need to calculate the parent bounds, so we can clip ourselves to those.
-    // When calculating the parent bounds for purposes of clipping,
-    // we don't need to constrain the parent to its transparent region.
-    // The transparent region is an optimization based on the
-    // buffer contents of the layer, but does not affect the space allocated to
-    // it by policy, and thus children should be allowed to extend into the
-    // parent's transparent region. In fact one of the main uses, is to reduce
-    // buffer allocation size in cases where a child window sits behind a main window
-    // (by marking the hole in the parent window as a transparent region)
-    if (p != nullptr) {
-        Rect bounds = p->computeScreenBounds(false);
-        bounds.intersect(win, &win);
-    }
-
-    if (reduceTransparentRegion) {
-        auto const screenTransparentRegion = t.transform(getActiveTransparentRegion(s));
-        win = reduce(win, screenTransparentRegion);
-    }
-
-    return win;
+    // Transform to screen space.
+    bounds = t.transform(bounds);
+    return Rect{bounds};
 }
 
 FloatRect Layer::computeBounds() const {
@@ -317,32 +290,72 @@
 
 FloatRect Layer::computeBounds(const Region& activeTransparentRegion) const {
     const State& s(getDrawingState());
-    Rect win(getActiveWidth(s), getActiveHeight(s));
+    Rect bounds = getCroppedBufferSize(s);
+    FloatRect floatBounds = bounds.toFloatRect();
+    if (bounds.isValid()) {
+        // Layer has bounds. Pass in our bounds as a special case. Then pass on to our parents so
+        // that they can clip it.
+        floatBounds = cropChildBounds(floatBounds);
+    } else {
+        // Layer does not have bounds, so we fill to our parent bounds. This is done by getting our
+        // parent bounds and inverting the transform to get the maximum bounds we can have that
+        // will fit within our parent bounds.
+        const auto& p = mDrawingParent.promote();
+        if (p != nullptr) {
+            ui::Transform t = s.active_legacy.transform;
+            // When calculating the parent bounds for purposes of clipping, we don't need to
+            // constrain the parent to its transparent region. The transparent region is an
+            // optimization based on the buffer contents of the layer, but does not affect the
+            // space allocated to it by policy, and thus children should be allowed to extend into
+            // the parent's transparent region.
+            // One of the main uses is a parent window with a child sitting behind the parent
+            // window, marked by a transparent region. When computing the parent bounds from the
+            // parent's perspective we pass in the transparent region to reduce buffer allocation
+            // size. When computing the parent bounds from the child's perspective, we pass in an
+            // empty transparent region in order to extend into the the parent bounds.
+            floatBounds = p->computeBounds(Region());
+            // Transform back to layer space.
+            floatBounds = t.inverse().transform(floatBounds);
+        }
+    }
 
-    Rect crop = getCrop(s);
-    if (!crop.isEmpty()) {
-        win.intersect(crop, &win);
+    // Subtract the transparent region and snap to the bounds.
+    return reduce(floatBounds, activeTransparentRegion);
+}
+
+FloatRect Layer::cropChildBounds(const FloatRect& childBounds) const {
+    const State& s(getDrawingState());
+    Rect bounds = getCroppedBufferSize(s);
+    FloatRect croppedBounds = childBounds;
+
+    // If the layer has bounds, then crop the passed in child bounds and pass
+    // it to our parents so they can crop it as well. If the layer has no bounds,
+    // then pass on the child bounds.
+    if (bounds.isValid()) {
+        croppedBounds = croppedBounds.intersect(bounds.toFloatRect());
     }
 
     const auto& p = mDrawingParent.promote();
-    FloatRect floatWin = win.toFloatRect();
-    FloatRect parentBounds = floatWin;
     if (p != nullptr) {
-        // We pass an empty Region here for reasons mirroring that of the case described in
-        // the computeScreenBounds reduceTransparentRegion=false case.
-        parentBounds = p->computeBounds(Region());
+        // Transform to parent space and allow parent layer to crop the
+        // child bounds as well.
+        ui::Transform t = s.active_legacy.transform;
+        croppedBounds = t.transform(croppedBounds);
+        croppedBounds = p->cropChildBounds(croppedBounds);
+        croppedBounds = t.inverse().transform(croppedBounds);
     }
+    return croppedBounds;
+}
 
-    ui::Transform t = s.active_legacy.transform;
-
-    if (p != nullptr) {
-        floatWin = t.transform(floatWin);
-        floatWin = floatWin.intersect(parentBounds);
-        floatWin = t.inverse().transform(floatWin);
+Rect Layer::getCroppedBufferSize(const State& s) const {
+    Rect size = getBufferSize(s);
+    Rect crop = getCrop(s);
+    if (!crop.isEmpty() && size.isValid()) {
+        size.intersect(crop, &size);
+    } else if (!crop.isEmpty()) {
+        size = crop;
     }
-
-    // subtract the transparent region and snap to the bounds
-    return reduce(floatWin, activeTransparentRegion);
+    return size;
 }
 
 Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& display) const {
@@ -462,11 +475,7 @@
 void Layer::setGeometry(const sp<const DisplayDevice>& display, uint32_t z) {
     const auto displayId = display->getId();
     LOG_ALWAYS_FATAL_IF(!displayId);
-    if (!hasHwcLayer(*displayId)) {
-        ALOGE("[%s] failed to setGeometry: no HWC layer found for display %" PRIu64, mName.string(),
-              *displayId);
-        return;
-    }
+    RETURN_IF_NO_HWC_LAYER(*displayId);
     auto& hwcInfo = getBE().mHwcLayers[*displayId];
 
     // enable this layer
@@ -634,27 +643,19 @@
 }
 
 void Layer::forceClientComposition(DisplayId displayId) {
-    if (getBE().mHwcLayers.count(displayId) == 0) {
-        ALOGE("forceClientComposition: no HWC layer found (display %" PRIu64 ")", displayId);
-        return;
-    }
-
+    RETURN_IF_NO_HWC_LAYER(displayId);
     getBE().mHwcLayers[displayId].forceClientComposition = true;
 }
 
 bool Layer::getForceClientComposition(DisplayId displayId) {
-    if (getBE().mHwcLayers.count(displayId) == 0) {
-        ALOGE("getForceClientComposition: no HWC layer found (display %" PRIu64 ")", displayId);
-        return false;
-    }
-
+    RETURN_IF_NO_HWC_LAYER(displayId, false);
     return getBE().mHwcLayers[displayId].forceClientComposition;
 }
 
 void Layer::updateCursorPosition(const sp<const DisplayDevice>& display) {
     const auto displayId = display->getId();
-    if (getBE().mHwcLayers.count(*displayId) == 0 ||
-        getCompositionType(displayId) != HWC2::Composition::Cursor) {
+    LOG_ALWAYS_FATAL_IF(!displayId);
+    if (!hasHwcLayer(*displayId) || getCompositionType(displayId) != HWC2::Composition::Cursor) {
         return;
     }
 
@@ -1054,6 +1055,7 @@
 
     // Commit the transaction
     commitTransaction(c);
+    mCurrentState.callbackHandles = {};
     return flags;
 }
 
@@ -1422,7 +1424,7 @@
 }
 
 void Layer::miniDump(String8& result, DisplayId displayId) const {
-    if (getBE().mHwcLayers.count(displayId) == 0) {
+    if (!hasHwcLayer(displayId)) {
         return;
     }
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index ced6532..40ebe1e 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -48,6 +48,7 @@
 #include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
 #include "TimeStats/TimeStats.h"
+#include "TransactionCompletedThread.h"
 
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/HWComposerBufferCache.h"
@@ -184,6 +185,10 @@
         sp<NativeHandle> sidebandStream;
         mat4 colorTransform;
         bool hasColorTransform;
+
+        // The deque of callback handles for this frame. The back of the deque contains the most
+        // recent callback handle.
+        std::deque<sp<CallbackHandle>> callbackHandles;
     };
 
     explicit Layer(const LayerCreationArgs& args);
@@ -266,13 +271,17 @@
     virtual bool setTransform(uint32_t /*transform*/) { return false; };
     virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
     virtual bool setCrop(const Rect& /*crop*/) { return false; };
-    virtual bool setBuffer(sp<GraphicBuffer> /*buffer*/) { return false; };
+    virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/) { return false; };
     virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
     virtual bool setDataspace(ui::Dataspace /*dataspace*/) { return false; };
     virtual bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/) { return false; };
     virtual bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/) { return false; };
     virtual bool setApi(int32_t /*api*/) { return false; };
     virtual bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/) { return false; };
+    virtual bool setTransactionCompletedListeners(
+            const std::vector<sp<CallbackHandle>>& /*handles*/) {
+        return false;
+    };
 
     ui::Dataspace getDataSpace() const { return mCurrentDataSpace; }
 
@@ -499,7 +508,7 @@
     bool destroyHwcLayer(DisplayId displayId);
     void destroyAllHwcLayers();
 
-    bool hasHwcLayer(DisplayId displayId) { return getBE().mHwcLayers.count(displayId) > 0; }
+    bool hasHwcLayer(DisplayId displayId) const { return getBE().mHwcLayers.count(displayId) > 0; }
 
     HWC2::Layer* getHwcLayer(DisplayId displayId) {
         if (!hasHwcLayer(displayId)) {
@@ -570,7 +579,7 @@
     ssize_t removeChild(const sp<Layer>& layer);
     sp<Layer> getParent() const { return mCurrentParent.promote(); }
     bool hasParent() const { return getParent() != nullptr; }
-    Rect computeScreenBounds(bool reduceTransparentRegion = true) const;
+    Rect computeScreenBounds() const;
     bool setChildLayer(const sp<Layer>& childLayer, int32_t z);
     bool setChildRelativeLayer(const sp<Layer>& childLayer,
             const sp<IBinder>& relativeToHandle, int32_t relativeZ);
@@ -782,10 +791,38 @@
                                        const LayerVector::Visitor& visitor);
     LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
                                           const std::vector<Layer*>& layersInTree);
+
+    /**
+     * Retuns the child bounds in layer space cropped to its bounds as well all its parent bounds.
+     * The cropped bounds must be transformed back from parent layer space to child layer space by
+     * applying the inverse of the child's transformation.
+     */
+    FloatRect cropChildBounds(const FloatRect& childBounds) const;
+
+    /**
+     * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
+     * INVALID_RECT if the layer has no buffer and no crop.
+     * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
+     * bounds are constrained by its parent bounds.
+     */
+    Rect getCroppedBufferSize(const Layer::State& s) const;
+
+    /**
+     * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
+     * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
+     */
+    virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
 };
 
-// ---------------------------------------------------------------------------
+} // namespace android
 
-}; // namespace android
+#define RETURN_IF_NO_HWC_LAYER(displayId, ...)                                         \
+    do {                                                                               \
+        if (!hasHwcLayer(displayId)) {                                                 \
+            ALOGE("[%s] %s failed: no HWC layer found for display %s", mName.string(), \
+                  __FUNCTION__, to_string(displayId).c_str());                         \
+            return __VA_ARGS__;                                                        \
+        }                                                                              \
+    } while (false)
 
 #endif // ANDROID_LAYER_H
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 18a8bb1..4286cc9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
 #include "Scheduler.h"
 
 #include <cinttypes>
 #include <cstdint>
 #include <memory>
+#include <numeric>
 
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
@@ -27,6 +30,7 @@
 
 #include <gui/ISurfaceComposer.h>
 #include <ui/DisplayStatInfo.h>
+#include <utils/Trace.h>
 
 #include "DispSync.h"
 #include "DispSyncSource.h"
@@ -196,4 +200,58 @@
     mPrimaryDispSync->setIgnorePresentFences(ignore);
 }
 
+void Scheduler::makeHWSyncAvailable(bool makeAvailable) {
+    std::lock_guard<std::mutex> lock(mHWVsyncLock);
+    mHWVsyncAvailable = makeAvailable;
+}
+
+void Scheduler::addNewFrameTimestamp(const nsecs_t newFrameTimestamp, bool isAutoTimestamp) {
+    ATRACE_INT("AutoTimestamp", isAutoTimestamp);
+    // Video does not have timestamp automatically set, so we discard timestamps that are
+    // coming in from other sources for now.
+    if (isAutoTimestamp) {
+        return;
+    }
+    int64_t differenceMs = (newFrameTimestamp - mPreviousFrameTimestamp) / 1000000;
+    mPreviousFrameTimestamp = newFrameTimestamp;
+
+    if (differenceMs < 10 || differenceMs > 100) {
+        // Dismiss noise.
+        return;
+    }
+    ATRACE_INT("TimestampDiff", differenceMs);
+
+    mTimeDifferences[mCounter % ARRAY_SIZE] = differenceMs;
+    mCounter++;
+    nsecs_t average = calculateAverage();
+    ATRACE_INT("TimestampAverage", average);
+
+    // TODO(b/113612090): This are current numbers from trial and error while running videos
+    // from YouTube at 24, 30, and 60 fps.
+    if (average > 14 && average < 18) {
+        ATRACE_INT("FPS", 60);
+    } else if (average > 31 && average < 34) {
+        ATRACE_INT("FPS", 30);
+        updateFrameSkipping(1);
+        return;
+    } else if (average > 39 && average < 42) {
+        ATRACE_INT("FPS", 24);
+    }
+    updateFrameSkipping(0);
+}
+
+nsecs_t Scheduler::calculateAverage() const {
+    nsecs_t sum = std::accumulate(mTimeDifferences.begin(), mTimeDifferences.end(), 0);
+    return (sum / ARRAY_SIZE);
+}
+
+void Scheduler::updateFrameSkipping(const int64_t skipCount) {
+    ATRACE_INT("FrameSkipCount", skipCount);
+    if (mSkipCount != skipCount) {
+        // Only update DispSync if it hasn't been updated yet.
+        mPrimaryDispSync->setRefreshSkipCount(skipCount);
+        mSkipCount = skipCount;
+    }
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index fdafe58..dd1f24b 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -102,6 +102,8 @@
     void addResyncSample(const nsecs_t timestamp);
     void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
     void setIgnorePresentFences(bool ignore);
+    void makeHWSyncAvailable(bool makeAvailable);
+    void addNewFrameTimestamp(const nsecs_t newFrameTimestamp, bool isAutoTimestamp);
 
 protected:
     virtual std::unique_ptr<EventThread> makeEventThread(
@@ -110,6 +112,9 @@
             impl::EventThread::InterceptVSyncsCallback interceptCallback);
 
 private:
+    nsecs_t calculateAverage() const;
+    void updateFrameSkipping(const int64_t skipCount);
+
     // TODO(b/113612090): Instead of letting BufferQueueLayer to access mDispSync directly, it
     // should make request to Scheduler to compute next refresh.
     friend class BufferQueueLayer;
@@ -133,6 +138,18 @@
 
     std::unique_ptr<DispSync> mPrimaryDispSync;
     std::unique_ptr<EventControlThread> mEventControlThread;
+
+    // TODO(b/113612090): The following set of variables needs to be revised. For now, this is
+    // a proof of concept. We turn on frame skipping if the difference between the timestamps
+    // is between 32 and 34ms. We expect this currently for 30fps videos, so we render them at 30Hz.
+    nsecs_t mPreviousFrameTimestamp = 0;
+    // Keeping track of whether we are skipping the refresh count. If we want to
+    // simulate 30Hz rendering, we skip every other frame, and this variable is set
+    // to 1.
+    int64_t mSkipCount = 0;
+    static constexpr size_t ARRAY_SIZE = 30;
+    std::array<int64_t, ARRAY_SIZE> mTimeDifferences;
+    size_t mCounter = 0;
 };
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9d16958..25c60fe 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1043,9 +1043,9 @@
     LOG_ALWAYS_FATAL_IF(!displayId);
     getHwComposer().setActiveColorMode(*displayId, mode, renderIntent);
 
-    ALOGV("Set active color mode: %s (%d), active render intent: %s (%d), display=%" PRIu64,
+    ALOGV("Set active color mode: %s (%d), active render intent: %s (%d), display=%s",
           decodeColorMode(mode).c_str(), mode, decodeRenderIntent(renderIntent).c_str(),
-          renderIntent, *displayId);
+          renderIntent, to_string(*displayId).c_str());
 }
 
 status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
@@ -1255,6 +1255,10 @@
 
     if (makeAvailable) {
         mHWVsyncAvailable = true;
+        // TODO(b/113612090): This is silly, but necessary evil until we turn on the flag for good.
+        if (mUseScheduler) {
+            mScheduler->makeHWSyncAvailable(true);
+        }
     } else if (!mHWVsyncAvailable) {
         // Hardware vsync is not currently available, so abort the resync
         // attempt for now
@@ -1636,8 +1640,8 @@
             display->setColorTransform(mDrawingState.colorMatrix);
             status_t result =
                     getHwComposer().setColorTransform(*displayId, mDrawingState.colorMatrix);
-            ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display %" PRIu64 ": %d",
-                     *displayId, result);
+            ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display %s: %d",
+                     to_string(*displayId).c_str(), result);
         }
         for (auto& layer : display->getVisibleLayersSortedByZ()) {
             if (layer->isHdrY410()) {
@@ -1955,6 +1959,9 @@
             ATRACE_INT("TexturePoolSize", mTexturePool.size());
         }
     }
+
+    mTransactionCompletedThread.addPresentFence(mPreviousPresentFence);
+    mTransactionCompletedThread.sendCallbacks();
 }
 
 void SurfaceFlinger::rebuildLayerStacks() {
@@ -2271,7 +2278,7 @@
 
         if (event.connection == HWC2::Connection::Connected) {
             if (!mPhysicalDisplayTokens.count(info->id)) {
-                ALOGV("Creating display %" PRIu64, info->id);
+                ALOGV("Creating display %s", to_string(info->id).c_str());
                 mPhysicalDisplayTokens[info->id] = new BBinder();
                 DisplayDeviceState state;
                 state.displayId = info->id;
@@ -2281,7 +2288,7 @@
                 mInterceptor->saveDisplayCreation(state);
             }
         } else {
-            ALOGV("Removing display %" PRIu64, info->id);
+            ALOGV("Removing display %s", to_string(info->id).c_str());
 
             ssize_t index = mCurrentState.displays.indexOfKey(mPhysicalDisplayTokens[info->id]);
             if (index >= 0) {
@@ -2724,7 +2731,11 @@
             // abandon the buffer queue.
             if (l->isRemovedFromCurrentState()) {
                 l->destroyAllHwcLayers();
-                l->releasePendingBuffer(systemTime());
+                // destroyAllHwcLayers traverses to children, but releasePendingBuffer
+                // doesn't in other scenarios. So we have to traverse explicitly here.
+                l->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* child) {
+                    child->releasePendingBuffer(systemTime());
+                });
             }
         }
         mLayersPendingRemoval.clear();
@@ -2968,8 +2979,7 @@
     // 1) It is being handled by hardware composer, which may need this to
     //    keep its virtual display state machine in sync, or
     // 2) There is work to be done (the dirty region isn't empty)
-    bool isHwcDisplay = display->getId() >= 0;
-    if (!isHwcDisplay && inDirtyRegion.isEmpty()) {
+    if (!display->getId() && inDirtyRegion.isEmpty()) {
         ALOGV("Skipping display composition");
         return;
     }
@@ -3284,9 +3294,15 @@
         transactionFlags |= setDisplayStateLocked(display);
     }
 
+    uint32_t clientStateFlags = 0;
     for (const ComposerState& state : states) {
-        transactionFlags |= setClientStateLocked(state);
+        clientStateFlags |= setClientStateLocked(state);
     }
+    // If the state doesn't require a traversal and there are callbacks, send them now
+    if (!(clientStateFlags & eTraversalNeeded)) {
+        mTransactionCompletedThread.sendCallbacks();
+    }
+    transactionFlags |= clientStateFlags;
 
     // Iterate through all layers again to determine if any need to be destroyed. Marking layers
     // as destroyed should only occur after setting all other states. This is to allow for a
@@ -3595,6 +3611,17 @@
     if (what & layer_state_t::eSidebandStreamChanged) {
         if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
     }
+
+    std::vector<sp<CallbackHandle>> callbackHandles;
+    if ((what & layer_state_t::eListenerCallbacksChanged) && (!s.listenerCallbacks.empty())) {
+        mTransactionCompletedThread.run();
+        for (const auto& [listener, callbackIds] : s.listenerCallbacks) {
+            callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
+        }
+    }
+    if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
+    // Do not put anything that updates layer state or modifies flags after
+    // setTransactionCompletedListener
     return flags;
 }
 
@@ -3844,7 +3871,7 @@
     const auto displayId = display->getId();
     LOG_ALWAYS_FATAL_IF(!displayId);
 
-    ALOGD("Setting power mode %d on display %" PRIu64, mode, *displayId);
+    ALOGD("Setting power mode %d on display %s", mode, to_string(*displayId).c_str());
 
     int currentMode = display->getPowerMode();
     if (mode == currentMode) {
@@ -3943,7 +3970,7 @@
         mTimeStats.setPowerMode(mode);
     }
 
-    ALOGD("Finished setting power mode %d on display %" PRIu64, mode, *displayId);
+    ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
 }
 
 void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
@@ -4274,7 +4301,7 @@
             continue;
         }
 
-        result.appendFormat("Display %" PRIu64 " (HWC display %" PRIu64 "): ", *displayId,
+        result.appendFormat("Display %s (HWC display %" PRIu64 "): ", to_string(*displayId).c_str(),
                             *hwcDisplayId);
         uint8_t port;
         DisplayIdentificationData data;
@@ -4322,7 +4349,7 @@
             continue;
         }
 
-        result.appendFormat("Display %" PRIu64 " color modes:\n", *displayId);
+        result.appendFormat("Display %s color modes:\n", to_string(*displayId).c_str());
         std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId);
         for (auto&& mode : modes) {
             result.appendFormat("    %s (%d)\n", decodeColorMode(mode).c_str(), mode);
@@ -4441,15 +4468,15 @@
     if (const auto displayId = getInternalDisplayId();
         displayId && getHwComposer().isConnected(*displayId)) {
         const auto activeConfig = getHwComposer().getActiveConfig(*displayId);
-        result.appendFormat("Display %" PRIu64 ": app phase %" PRId64 " ns, "
+        result.appendFormat("Display %s: app phase %" PRId64 " ns, "
                             "sf phase %" PRId64 " ns, "
                             "early app phase %" PRId64 " ns, "
                             "early sf phase %" PRId64 " ns, "
                             "early app gl phase %" PRId64 " ns, "
                             "early sf gl phase %" PRId64 " ns, "
                             "present offset %" PRId64 " ns (refresh %" PRId64 " ns)",
-                            *displayId, vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, appEarlyOffset,
-                            sfEarlyOffset, appEarlyGlOffset, sfEarlyGlOffset,
+                            to_string(*displayId).c_str(), vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs,
+                            appEarlyOffset, sfEarlyOffset, appEarlyGlOffset, sfEarlyGlOffset,
                             dispSyncPresentTimeOffset, activeConfig->getVsyncPeriod());
     }
     result.append("\n");
@@ -4553,7 +4580,7 @@
             continue;
         }
 
-        result.appendFormat("Display %" PRIu64 " HWC layers:\n", *displayId);
+        result.appendFormat("Display %s HWC layers:\n", to_string(*displayId).c_str());
         Layer::miniDumpHeader(result);
         mCurrentState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, *displayId); });
         result.append("\n");
@@ -4594,7 +4621,7 @@
         }
     }
 
-    ALOGE("%s: Invalid display %" PRIu64, __FUNCTION__, displayId);
+    ALOGE("%s: Invalid display %s", __FUNCTION__, to_string(displayId).c_str());
     static const Vector<sp<Layer>> empty;
     return empty;
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e449e20..6ffe025 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -62,6 +62,7 @@
 #include "SurfaceFlingerFactory.h"
 #include "SurfaceInterceptor.h"
 #include "SurfaceTracing.h"
+#include "TransactionCompletedThread.h"
 
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/HWComposer.h"
@@ -368,6 +369,10 @@
     inline void onLayerCreated() { mNumLayers++; }
     inline void onLayerDestroyed() { mNumLayers--; }
 
+    TransactionCompletedThread& getTransactionCompletedThread() {
+        return mTransactionCompletedThread;
+    }
+
 private:
     friend class Client;
     friend class DisplayEventConnection;
@@ -919,6 +924,8 @@
     bool mUseHwcVirtualDisplays = false;
     std::atomic<uint32_t> mFrameMissedCount{0};
 
+    TransactionCompletedThread mTransactionCompletedThread;
+
     // Restrict layers to use two buffers in their bufferqueues.
     bool mLayerTripleBufferingDisabled = false;
 
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 7faaff6..55cfa4b 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -497,7 +497,7 @@
     creation->set_name(info.displayName);
     creation->set_is_secure(info.isSecure);
     if (info.displayId) {
-        creation->set_display_id(*info.displayId);
+        creation->set_display_id(info.displayId->value);
     }
 }
 
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index ace7c1b..9730e8c 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -173,8 +173,8 @@
         ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerID,
               timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime);
 
+        const std::string& layerName = layerRecord.layerName;
         if (prevTimeRecord.ready) {
-            const std::string& layerName = layerRecord.layerName;
             if (!mTimeStats.stats.count(layerName)) {
                 mTimeStats.stats[layerName].layerName = layerName;
                 mTimeStats.stats[layerName].packageName = getPackageName(layerName);
@@ -220,6 +220,18 @@
                   timeRecords[0].frameTime.frameNumber, presentToPresentMs);
             timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
         }
+
+        // Output additional trace points to track frame time.
+        ATRACE_INT64(("TimeStats-Post - " + layerName).c_str(), timeRecords[0].frameTime.postTime);
+        ATRACE_INT64(("TimeStats-Acquire - " + layerName).c_str(),
+                     timeRecords[0].frameTime.acquireTime);
+        ATRACE_INT64(("TimeStats-Latch - " + layerName).c_str(),
+                     timeRecords[0].frameTime.latchTime);
+        ATRACE_INT64(("TimeStats-Desired - " + layerName).c_str(),
+                     timeRecords[0].frameTime.desiredTime);
+        ATRACE_INT64(("TimeStats-Present - " + layerName).c_str(),
+                     timeRecords[0].frameTime.presentTime);
+
         prevTimeRecord = timeRecords[0];
         timeRecords.pop_front();
         layerRecord.waitData--;
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
new file mode 100644
index 0000000..9b9dc57
--- /dev/null
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright 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.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "TransactionCompletedThread"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "TransactionCompletedThread.h"
+
+#include <cinttypes>
+
+#include <binder/IInterface.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+TransactionCompletedThread::~TransactionCompletedThread() {
+    {
+        std::lock_guard lock(mMutex);
+        mKeepRunning = false;
+        mConditionVariable.notify_all();
+    }
+
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+
+    {
+        std::lock_guard lock(mMutex);
+        for (const auto& [listener, listenerStats] : mListenerStats) {
+            listener->unlinkToDeath(mDeathRecipient);
+        }
+    }
+}
+
+void TransactionCompletedThread::run() {
+    std::lock_guard lock(mMutex);
+    if (mRunning) {
+        return;
+    }
+    mDeathRecipient = new ThreadDeathRecipient();
+    mRunning = true;
+    mThread = std::thread(&TransactionCompletedThread::threadMain, this);
+}
+
+void TransactionCompletedThread::registerPendingLatchedCallbackHandle(
+        const sp<CallbackHandle>& handle) {
+    std::lock_guard lock(mMutex);
+
+    sp<IBinder> listener = IInterface::asBinder(handle->listener);
+    const auto& callbackIds = handle->callbackIds;
+
+    mPendingTransactions[listener][callbackIds]++;
+}
+
+void TransactionCompletedThread::addLatchedCallbackHandles(
+        const std::deque<sp<CallbackHandle>>& handles, nsecs_t latchTime,
+        const sp<Fence>& previousReleaseFence) {
+    std::lock_guard lock(mMutex);
+
+    // If the previous release fences have not signaled, something as probably gone wrong.
+    // Store the fences and check them again before sending a callback.
+    if (previousReleaseFence &&
+        previousReleaseFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+        ALOGD("release fence from the previous frame has not signaled");
+        mPreviousReleaseFences.push_back(previousReleaseFence);
+    }
+
+    for (const auto& handle : handles) {
+        auto listener = mPendingTransactions.find(IInterface::asBinder(handle->listener));
+        auto& pendingCallbacks = listener->second;
+        auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
+
+        if (pendingCallback != pendingCallbacks.end()) {
+            auto& pendingCount = pendingCallback->second;
+
+            // Decrease the pending count for this listener
+            if (--pendingCount == 0) {
+                pendingCallbacks.erase(pendingCallback);
+            }
+        } else {
+            ALOGE("there are more latched callbacks than there were registered callbacks");
+        }
+
+        addCallbackHandle(handle, latchTime);
+    }
+}
+
+void TransactionCompletedThread::addUnlatchedCallbackHandle(const sp<CallbackHandle>& handle) {
+    std::lock_guard lock(mMutex);
+    addCallbackHandle(handle);
+}
+
+void TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle,
+                                                   nsecs_t latchTime) {
+    const sp<IBinder> listener = IInterface::asBinder(handle->listener);
+
+    // If we don't already have a reference to this listener, linkToDeath so we get a notification
+    // if it dies.
+    if (mListenerStats.count(listener) == 0) {
+        status_t error = listener->linkToDeath(mDeathRecipient);
+        if (error != NO_ERROR) {
+            ALOGE("cannot add callback handle because linkToDeath failed, err: %d", error);
+            return;
+        }
+    }
+
+    auto& listenerStats = mListenerStats[listener];
+    listenerStats.listener = handle->listener;
+
+    auto& transactionStats = listenerStats.transactionStats[handle->callbackIds];
+    transactionStats.latchTime = latchTime;
+    transactionStats.surfaceStats.emplace_back(handle->surfaceControl, handle->acquireTime,
+                                               handle->releasePreviousBuffer);
+}
+
+void TransactionCompletedThread::addPresentFence(const sp<Fence>& presentFence) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mPresentFence = presentFence;
+}
+
+void TransactionCompletedThread::sendCallbacks() {
+    std::lock_guard lock(mMutex);
+    if (mRunning) {
+        mConditionVariable.notify_all();
+    }
+}
+
+void TransactionCompletedThread::threadMain() {
+    std::lock_guard lock(mMutex);
+
+    while (mKeepRunning) {
+        mConditionVariable.wait(mMutex);
+
+        // Present fence should fire almost immediately. If the fence has not signaled in 100ms,
+        // there is a major problem and it will probably never fire.
+        nsecs_t presentTime = -1;
+        if (mPresentFence) {
+            status_t status = mPresentFence->wait(100);
+            if (status == NO_ERROR) {
+                presentTime = mPresentFence->getSignalTime();
+            } else {
+                ALOGE("present fence has not signaled, err %d", status);
+            }
+        }
+
+        // We should never hit this case. The release fences from the previous frame should have
+        // signaled long before the current frame is presented.
+        for (const auto& fence : mPreviousReleaseFences) {
+            status_t status = fence->wait(100);
+            if (status != NO_ERROR) {
+                ALOGE("previous release fence has not signaled, err %d", status);
+            }
+        }
+
+        // For each listener
+        auto it = mListenerStats.begin();
+        while (it != mListenerStats.end()) {
+            auto& [listener, listenerStats] = *it;
+
+            // For each transaction
+            bool sendCallback = true;
+            for (auto& [callbackIds, transactionStats] : listenerStats.transactionStats) {
+                // If we are still waiting on the callback handles for this transaction, skip it
+                if (mPendingTransactions[listener].count(callbackIds) != 0) {
+                    sendCallback = false;
+                    break;
+                }
+
+                // If the transaction has been latched
+                if (transactionStats.latchTime >= 0) {
+                    // If the present time is < 0, this transaction has been latched but not
+                    // presented. Skip it for now. This can happen when a new transaction comes
+                    // in between the latch and present steps. sendCallbacks is called by
+                    // SurfaceFlinger when the transaction is received to ensure that if the
+                    // transaction that didn't update state it still got a callback.
+                    if (presentTime < 0) {
+                        sendCallback = false;
+                        break;
+                    }
+
+                    transactionStats.presentTime = presentTime;
+                }
+            }
+            // If the listener has no pending transactions and all latched transactions have been
+            // presented
+            if (sendCallback) {
+                // If the listener is still alive
+                if (listener->isBinderAlive()) {
+                    // Send callback
+                    listenerStats.listener->onTransactionCompleted(listenerStats);
+                    listener->unlinkToDeath(mDeathRecipient);
+                }
+                it = mListenerStats.erase(it);
+            } else {
+                it++;
+            }
+        }
+
+        if (mPresentFence) {
+            mPresentFence.clear();
+            mPreviousReleaseFences.clear();
+        }
+    }
+}
+
+// -----------------------------------------------------------------------
+
+CallbackHandle::CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener,
+                               const std::vector<CallbackId>& ids, const sp<IBinder>& sc)
+      : listener(transactionListener), callbackIds(ids), surfaceControl(sc) {}
+
+} // namespace android
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
new file mode 100644
index 0000000..5af4097
--- /dev/null
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright 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 <condition_variable>
+#include <deque>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+
+#include <android-base/thread_annotations.h>
+
+#include <binder/IBinder.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <ui/Fence.h>
+
+namespace android {
+
+class CallbackHandle : public RefBase {
+public:
+    CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener,
+                   const std::vector<CallbackId>& ids, const sp<IBinder>& sc);
+
+    sp<ITransactionCompletedListener> listener;
+    std::vector<CallbackId> callbackIds;
+    sp<IBinder> surfaceControl;
+
+    bool releasePreviousBuffer = false;
+    nsecs_t acquireTime = -1;
+};
+
+class TransactionCompletedThread {
+public:
+    ~TransactionCompletedThread();
+
+    void run();
+
+    // Informs the TransactionCompletedThread that there is a Transaction with a CallbackHandle
+    // that needs to be latched and presented this frame. This function should be called once the
+    // layer has received the CallbackHandle so the TransactionCompletedThread knows not to send
+    // a callback for that Listener/Transaction pair until that CallbackHandle has been latched and
+    // presented.
+    void registerPendingLatchedCallbackHandle(const sp<CallbackHandle>& handle);
+    // Notifies the TransactionCompletedThread that a pending CallbackHandle has been latched.
+    void addLatchedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles, nsecs_t latchTime,
+                                   const sp<Fence>& previousReleaseFence);
+
+    // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
+    // presented this frame.
+    void addUnlatchedCallbackHandle(const sp<CallbackHandle>& handle);
+
+    void addPresentFence(const sp<Fence>& presentFence);
+
+    void sendCallbacks();
+
+private:
+    void threadMain();
+
+    void addCallbackHandle(const sp<CallbackHandle>& handle, nsecs_t latchTime = -1)
+            REQUIRES(mMutex);
+
+    class ThreadDeathRecipient : public IBinder::DeathRecipient {
+    public:
+        // This function is a no-op. isBinderAlive needs a linked DeathRecipient to work.
+        // Death recipients needs a binderDied function.
+        //
+        // (isBinderAlive checks if BpBinder's mAlive is 0. mAlive is only set to 0 in sendObituary.
+        // sendObituary is only called if linkToDeath was called with a DeathRecipient.)
+        void binderDied(const wp<IBinder>& /*who*/) override {}
+    };
+    sp<ThreadDeathRecipient> mDeathRecipient;
+
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& strongPointer) const {
+            return std::hash<IBinder*>{}(strongPointer.get());
+        }
+    };
+
+    std::thread mThread;
+
+    std::mutex mMutex;
+    std::condition_variable_any mConditionVariable;
+
+    std::unordered_map<
+            sp<IBinder /*listener*/>,
+            std::unordered_map<std::vector<CallbackId>, uint32_t /*count*/, CallbackIdsHash>,
+            IBinderHash>
+            mPendingTransactions GUARDED_BY(mMutex);
+    std::unordered_map<sp<IBinder /*listener*/>, ListenerStats, IBinderHash> mListenerStats
+            GUARDED_BY(mMutex);
+
+    bool mRunning GUARDED_BY(mMutex) = false;
+    bool mKeepRunning GUARDED_BY(mMutex) = true;
+
+    sp<Fence> mPresentFence GUARDED_BY(mMutex);
+    std::vector<sp<Fence>> mPreviousReleaseFences GUARDED_BY(mMutex);
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index 8bd5fcb..91999ae 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
 {
         "presubmit": {
-            "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*"
+            "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*"
         }
 }
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 94b33ac..988454a 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -15,9 +15,12 @@
  */
 
 #include <algorithm>
+#include <chrono>
+#include <cinttypes>
 #include <functional>
 #include <limits>
 #include <ostream>
+#include <thread>
 
 #include <gtest/gtest.h>
 
@@ -64,6 +67,7 @@
 const Color Color::TRANSPARENT{0, 0, 0, 0};
 
 using android::hardware::graphics::common::V1_1::BufferUsage;
+using namespace std::chrono_literals;
 
 std::ostream& operator<<(std::ostream& os, const Color& color) {
     os << int(color.r) << ", " << int(color.g) << ", " << int(color.b) << ", " << int(color.a);
@@ -327,16 +331,21 @@
         mClient = 0;
     }
 
-    virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
+    virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
+                                           const char* name, uint32_t width, uint32_t height,
                                            uint32_t flags = 0) {
         auto layer =
-                mClient->createSurface(String8(name), width, height, PIXEL_FORMAT_RGBA_8888, flags);
+                client->createSurface(String8(name), width, height, PIXEL_FORMAT_RGBA_8888, flags);
         EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
 
-        status_t error = Transaction()
-                                 .setLayerStack(layer, mDisplayLayerStack)
-                                 .setLayer(layer, mLayerZBase)
-                                 .apply();
+        Transaction t;
+        t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase);
+        // If we are creating a color layer, set its crop since its size will be ignored.
+        if (flags == ISurfaceComposerClient::eFXSurfaceColor) {
+            t.setCrop_legacy(layer, Rect(0, 0, width, height));
+        }
+
+        status_t error = t.apply();
         if (error != NO_ERROR) {
             ADD_FAILURE() << "failed to initialize SurfaceControl";
             layer.clear();
@@ -345,6 +354,11 @@
         return layer;
     }
 
+    virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
+                                           uint32_t flags = 0) {
+        return createLayer(mClient, name, width, height, flags);
+    }
+
     ANativeWindow_Buffer getBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
         // wait for previous transactions (such as setSize) to complete
         Transaction().apply(true);
@@ -502,6 +516,7 @@
         // set layer stack (b/68888219)
         Transaction t;
         t.setDisplayLayerStack(mDisplay, mDisplayLayerStack);
+        t.setCrop_legacy(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
         t.setLayerStack(mBlackBgSurface, mDisplayLayerStack);
         t.setColor(mBlackBgSurface, half3{0, 0, 0});
         t.setLayer(mBlackBgSurface, mLayerZBase);
@@ -1953,7 +1968,7 @@
                               "test");
     fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
 
-    sp<Fence> fence = new Fence(-1);
+    sp<Fence> fence = Fence::NO_FENCE;
 
     Transaction()
             .setBuffer(layer, buffer)
@@ -2156,6 +2171,627 @@
     }
 }
 
+class ExpectedResult {
+public:
+    enum Transaction {
+        NOT_PRESENTED = 0,
+        PRESENTED,
+    };
+
+    enum Buffer {
+        NOT_ACQUIRED = 0,
+        ACQUIRED,
+    };
+
+    enum PreviousBuffer {
+        NOT_RELEASED = 0,
+        RELEASED,
+    };
+
+    void reset() {
+        mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
+        mExpectedSurfaceResults.clear();
+    }
+
+    void addSurface(ExpectedResult::Transaction transactionResult, const sp<SurfaceControl>& layer,
+                    ExpectedResult::Buffer bufferResult = NOT_ACQUIRED,
+                    ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
+        mTransactionResult = transactionResult;
+        mExpectedSurfaceResults.emplace(std::piecewise_construct,
+                                        std::forward_as_tuple(layer->getHandle()),
+                                        std::forward_as_tuple(bufferResult, previousBufferResult));
+    }
+
+    void addSurfaces(ExpectedResult::Transaction transactionResult,
+                     const std::vector<sp<SurfaceControl>>& layers,
+                     ExpectedResult::Buffer bufferResult = NOT_ACQUIRED,
+                     ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
+        for (const auto& layer : layers) {
+            addSurface(transactionResult, layer, bufferResult, previousBufferResult);
+        }
+    }
+
+    void verifyTransactionStats(const TransactionStats& transactionStats) const {
+        const auto& [latchTime, presentTime, surfaceStats] = transactionStats;
+        if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
+            ASSERT_GE(latchTime, 0) << "bad latch time";
+            ASSERT_GE(presentTime, 0) << "bad present time";
+        } else {
+            ASSERT_EQ(presentTime, -1) << "transaction shouldn't have been presented";
+            ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched";
+        }
+
+        ASSERT_EQ(surfaceStats.size(), mExpectedSurfaceResults.size())
+                << "wrong number of surfaces";
+
+        for (const auto& stats : surfaceStats) {
+            const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl);
+            ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end())
+                    << "unexpected surface control";
+            expectedSurfaceResult->second.verifySurfaceStats(stats, latchTime);
+        }
+    }
+
+private:
+    class ExpectedSurfaceResult {
+    public:
+        ExpectedSurfaceResult(ExpectedResult::Buffer bufferResult,
+                              ExpectedResult::PreviousBuffer previousBufferResult)
+              : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {}
+
+        void verifySurfaceStats(const SurfaceStats& surfaceStats, nsecs_t latchTime) const {
+            const auto& [surfaceControl, acquireTime, releasePreviousBuffer] = surfaceStats;
+
+            ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
+                    << "bad acquire time";
+            ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time";
+            ASSERT_EQ(releasePreviousBuffer,
+                      mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED)
+                    << "bad previous buffer released";
+        }
+
+    private:
+        ExpectedResult::Buffer mBufferResult;
+        ExpectedResult::PreviousBuffer mPreviousBufferResult;
+    };
+
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& strongPointer) const {
+            return std::hash<IBinder*>{}(strongPointer.get());
+        }
+    };
+    ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
+    std::unordered_map<sp<IBinder>, ExpectedSurfaceResult, IBinderHash> mExpectedSurfaceResults;
+};
+
+class CallbackHelper {
+public:
+    static void function(void* callbackContext, const TransactionStats& transactionStats) {
+        if (!callbackContext) {
+            ALOGE("failed to get callback context");
+        }
+        CallbackHelper* helper = static_cast<CallbackHelper*>(callbackContext);
+        std::lock_guard lock(helper->mMutex);
+        helper->mTransactionStatsQueue.push(transactionStats);
+        helper->mConditionVariable.notify_all();
+    }
+
+    void getTransactionStats(TransactionStats* outStats) {
+        std::unique_lock lock(mMutex);
+
+        if (mTransactionStatsQueue.empty()) {
+            ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)),
+                      std::cv_status::timeout)
+                    << "did not receive callback";
+        }
+
+        *outStats = std::move(mTransactionStatsQueue.front());
+        mTransactionStatsQueue.pop();
+    }
+
+    void verifyFinalState() {
+        // Wait to see if there are extra callbacks
+        std::this_thread::sleep_for(500ms);
+
+        std::lock_guard lock(mMutex);
+        EXPECT_EQ(mTransactionStatsQueue.size(), 0) << "extra callbacks received";
+        mTransactionStatsQueue = {};
+    }
+
+    void* getContext() { return static_cast<void*>(this); }
+
+    std::mutex mMutex;
+    std::condition_variable mConditionVariable;
+    std::queue<TransactionStats> mTransactionStatsQueue;
+};
+
+class LayerCallbackTest : public LayerTransactionTest {
+protected:
+    virtual sp<SurfaceControl> createBufferStateLayer() {
+        return createLayer(mClient, "test", mWidth, mHeight,
+                           ISurfaceComposerClient::eFXSurfaceBufferState);
+    }
+
+    virtual void fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper,
+                                 const sp<SurfaceControl>& layer = nullptr) {
+        if (layer) {
+            sp<GraphicBuffer> buffer =
+                    new GraphicBuffer(mWidth, mHeight, PIXEL_FORMAT_RGBA_8888, 1,
+                                      BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                              BufferUsage::COMPOSER_OVERLAY |
+                                              BufferUsage::GPU_TEXTURE,
+                                      "test");
+            fillGraphicBufferColor(buffer, Rect(0, 0, mWidth, mHeight), Color::RED);
+
+            sp<Fence> fence = new Fence(-1);
+
+            transaction.setBuffer(layer, buffer)
+                    .setAcquireFence(layer, fence)
+                    .setSize(layer, mWidth, mHeight);
+        }
+
+        transaction.addTransactionCompletedCallback(callbackHelper->function,
+                                                    callbackHelper->getContext());
+    }
+
+    void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
+                         bool finalState = false) {
+        TransactionStats transactionStats;
+        ASSERT_NO_FATAL_FAILURE(helper.getTransactionStats(&transactionStats));
+        EXPECT_NO_FATAL_FAILURE(expectedResult.verifyTransactionStats(transactionStats));
+
+        if (finalState) {
+            ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+        }
+    }
+
+    void waitForCallbacks(CallbackHelper& helper,
+                          const std::vector<ExpectedResult>& expectedResults,
+                          bool finalState = false) {
+        for (const auto& expectedResult : expectedResults) {
+            waitForCallback(helper, expectedResult);
+        }
+        if (finalState) {
+            ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+        }
+    }
+
+    uint32_t mWidth = 32;
+    uint32_t mHeight = 32;
+};
+
+TEST_F(LayerCallbackTest, Basic) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    fillTransaction(transaction, &callback, layer);
+
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoBuffer) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    fillTransaction(transaction, &callback);
+
+    transaction.setPosition(layer, mWidth, mHeight).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoStateChange) {
+    Transaction transaction;
+    CallbackHelper callback;
+    fillTransaction(transaction, &callback);
+
+    transaction.apply();
+
+    ExpectedResult expected;
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, OffScreen) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    fillTransaction(transaction, &callback, layer);
+
+    transaction.setPosition(layer, -100, -100).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    fillTransaction(transaction1, &callback1, layer1);
+    fillTransaction(transaction2, &callback2, layer2);
+
+    transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_SameCallback) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback;
+    fillTransaction(transaction1, &callback, layer1);
+    fillTransaction(transaction2, &callback, layer2);
+
+    transaction2.merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_SameLayer) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    fillTransaction(transaction1, &callback1, layer);
+    fillTransaction(transaction2, &callback2, layer);
+
+    transaction2.merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_SingleBuffer) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    fillTransaction(transaction1, &callback1, layer1);
+    fillTransaction(transaction2, &callback2);
+
+    transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_DifferentClients) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    fillTransaction(transaction1, &callback1, layer1);
+    fillTransaction(transaction2, &callback2, layer2);
+
+    transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    for (size_t i = 0; i < 10; i++) {
+        fillTransaction(transaction, &callback, layer);
+
+        transaction.apply();
+
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED,
+                            (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+                                     : ExpectedResult::PreviousBuffer::RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_NoStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    for (size_t i = 0; i < 10; i++) {
+        ExpectedResult expected;
+
+        if (i == 0) {
+            fillTransaction(transaction, &callback, layer);
+            expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+        } else {
+            fillTransaction(transaction, &callback);
+        }
+
+        transaction.apply();
+
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SameStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    for (size_t i = 0; i < 10; i++) {
+        if (i == 0) {
+            fillTransaction(transaction, &callback, layer);
+        } else {
+            fillTransaction(transaction, &callback);
+        }
+
+        transaction.setPosition(layer, mWidth, mHeight).apply();
+
+        ExpectedResult expected;
+        expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED
+                                     : ExpectedResult::Transaction::NOT_PRESENTED,
+                            layer);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, i == 0));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    for (size_t i = 0; i < 10; i++) {
+        fillTransaction(transaction1, &callback1, layer1);
+        fillTransaction(transaction2, &callback2, layer2);
+
+        transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+        ExpectedResult expected;
+        expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+                             ExpectedResult::Buffer::NOT_ACQUIRED,
+                             (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+                                      : ExpectedResult::PreviousBuffer::RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
+    ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    for (size_t i = 0; i < 10; i++) {
+        fillTransaction(transaction1, &callback1, layer1);
+        fillTransaction(transaction2, &callback2, layer2);
+
+        transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+        ExpectedResult expected;
+        expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+                             ExpectedResult::Buffer::NOT_ACQUIRED,
+                             (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+                                      : ExpectedResult::PreviousBuffer::RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
+    ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_NoStateChange) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+
+    // Normal call to set up test
+    fillTransaction(transaction1, &callback1, layer1);
+    fillTransaction(transaction2, &callback2, layer2);
+
+    transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+    expected.reset();
+
+    // Test
+    fillTransaction(transaction1, &callback1);
+    fillTransaction(transaction2, &callback2);
+
+    transaction2.merge(std::move(transaction1)).apply();
+
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_SameStateChange) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", mWidth, mHeight,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+
+    // Normal call to set up test
+    fillTransaction(transaction1, &callback1, layer1);
+    fillTransaction(transaction2, &callback2, layer2);
+
+    transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+    expected.reset();
+
+    // Test
+    fillTransaction(transaction1, &callback1);
+    fillTransaction(transaction2, &callback2);
+
+    transaction2.setPosition(layer2, mWidth, mHeight).merge(std::move(transaction1)).apply();
+
+    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    std::vector<ExpectedResult> expectedResults(50);
+    ExpectedResult::PreviousBuffer previousBufferResult =
+            ExpectedResult::PreviousBuffer::NOT_RELEASED;
+    for (auto& expected : expectedResults) {
+        expected.reset();
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED, previousBufferResult);
+        previousBufferResult = ExpectedResult::PreviousBuffer::RELEASED;
+
+        fillTransaction(transaction, &callback, layer);
+
+        transaction.apply();
+        std::this_thread::sleep_for(200ms);
+    }
+    EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    std::vector<ExpectedResult> expectedResults(50);
+    bool first = true;
+    for (auto& expected : expectedResults) {
+        expected.reset();
+
+        if (first) {
+            fillTransaction(transaction, &callback, layer);
+            expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+            first = false;
+        } else {
+            fillTransaction(transaction, &callback);
+        }
+
+        transaction.apply();
+        std::this_thread::sleep_for(200ms);
+    }
+    EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_SameStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    // Normal call to set up test
+    Transaction transaction;
+    CallbackHelper callback;
+    fillTransaction(transaction, &callback, layer);
+
+    transaction.setPosition(layer, mWidth, mHeight).apply();
+
+    ExpectedResult expectedResult;
+    expectedResult.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expectedResult, true));
+
+    // Test
+    std::vector<ExpectedResult> expectedResults(50);
+    for (auto& expected : expectedResults) {
+        expected.reset();
+        expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer);
+
+        fillTransaction(transaction, &callback);
+
+        transaction.setPosition(layer, mWidth, mHeight).apply();
+
+        std::this_thread::sleep_for(200ms);
+    }
+    EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
 class LayerUpdateTest : public LayerTransactionTest {
 protected:
     virtual void SetUp() {
@@ -2394,17 +3030,19 @@
             mClient->createSurface(String8("Buffered child"), 20, 20,
                                            PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
     fillSurfaceRGBA8(childBuffer, 200, 200, 200);
-
-    SurfaceComposerClient::Transaction{}.show(childNoBuffer).show(childBuffer).apply(true);
-
+    SurfaceComposerClient::Transaction{}
+            .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10))
+            .show(childNoBuffer)
+            .show(childBuffer)
+            .apply(true);
     {
         ScreenCapture::captureScreen(&sc);
         sc->expectChildColor(73, 73);
         sc->expectFGColor(74, 74);
     }
-
-    SurfaceComposerClient::Transaction{}.setSize(childNoBuffer, 20, 20).apply(true);
-
+    SurfaceComposerClient::Transaction{}
+            .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20))
+            .apply(true);
     {
         ScreenCapture::captureScreen(&sc);
         sc->expectChildColor(73, 73);
@@ -2939,6 +3577,221 @@
         mCapture->checkPixel(10, 10, 255, 255, 255);
     }
 }
+class BoundlessLayerTest : public LayerUpdateTest {
+protected:
+    std::unique_ptr<ScreenCapture> mCapture;
+};
+
+// Verify setting a size on a buffer layer has no effect.
+TEST_F(BoundlessLayerTest, BufferLayerIgnoresSize) {
+    sp<SurfaceControl> bufferLayer =
+            mClient->createSurface(String8("BufferLayer"), 45, 45, PIXEL_FORMAT_RGBA_8888, 0,
+                                   mFGSurfaceControl.get());
+    ASSERT_TRUE(bufferLayer != nullptr);
+    ASSERT_TRUE(bufferLayer->isValid());
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::BLACK, 30, 30));
+    asTransaction([&](Transaction& t) { t.show(bufferLayer); });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 94, 94), Color::BLACK);
+        // Buffer layer should not extend past buffer bounds
+        mCapture->expectFGColor(95, 95);
+    }
+}
+
+// Verify a boundless color layer will fill its parent bounds. The parent has a buffer size
+// which will crop the color layer.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentBufferBounds) {
+    sp<SurfaceControl> colorLayer =
+            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceColor,
+                                   mFGSurfaceControl.get());
+    ASSERT_TRUE(colorLayer != nullptr);
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify a boundless color layer will fill its parent bounds. The parent has no buffer but has
+// a crop which will be used to crop the color layer.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentCropBounds) {
+    sp<SurfaceControl> cropLayer =
+            mClient->createSurface(String8("CropLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   0 /* flags */, mFGSurfaceControl.get());
+    ASSERT_TRUE(cropLayer != nullptr);
+    ASSERT_TRUE(cropLayer->isValid());
+    sp<SurfaceControl> colorLayer =
+            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceColor, cropLayer.get());
+    ASSERT_TRUE(colorLayer != nullptr);
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10));
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(cropLayer);
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // 5 pixels from the foreground we should see the child surface
+        mCapture->expectColor(Rect(69, 69, 74, 74), Color::BLACK);
+        // 10 pixels from the foreground we should be back to the foreground surface
+        mCapture->expectFGColor(74, 74);
+    }
+}
+
+// Verify for boundless layer with no children, their transforms have no effect.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerTransformHasNoEffect) {
+    sp<SurfaceControl> colorLayer =
+            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceColor,
+                                   mFGSurfaceControl.get());
+    ASSERT_TRUE(colorLayer != nullptr);
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setPosition(colorLayer, 320, 320);
+        t.setMatrix(colorLayer, 2, 0, 0, 2);
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify for boundless layer with children, their transforms have an effect.
+TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerCanSetTransform) {
+    sp<SurfaceControl> boundlessLayerRightShift =
+            mClient->createSurface(String8("BoundlessLayerRightShift"), 0, 0,
+                                   PIXEL_FORMAT_RGBA_8888, 0 /* flags */, mFGSurfaceControl.get());
+    ASSERT_TRUE(boundlessLayerRightShift != nullptr);
+    ASSERT_TRUE(boundlessLayerRightShift->isValid());
+    sp<SurfaceControl> boundlessLayerDownShift =
+            mClient->createSurface(String8("BoundlessLayerLeftShift"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   0 /* flags */, boundlessLayerRightShift.get());
+    ASSERT_TRUE(boundlessLayerDownShift != nullptr);
+    ASSERT_TRUE(boundlessLayerDownShift->isValid());
+    sp<SurfaceControl> colorLayer =
+            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceColor,
+                                   boundlessLayerDownShift.get());
+    ASSERT_TRUE(colorLayer != nullptr);
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setPosition(boundlessLayerRightShift, 32, 0);
+        t.show(boundlessLayerRightShift);
+        t.setPosition(boundlessLayerDownShift, 0, 32);
+        t.show(boundlessLayerDownShift);
+        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(96, 96, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify child layers do not get clipped if they temporarily move into the negative
+// coordinate space as the result of an intermediate transformation.
+TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerDoNotCrop) {
+    sp<SurfaceControl> boundlessLayer =
+            mClient->createSurface(String8("BoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   0 /* flags */, mFGSurfaceControl.get());
+    ASSERT_TRUE(boundlessLayer != nullptr);
+    ASSERT_TRUE(boundlessLayer->isValid());
+    sp<SurfaceControl> colorLayer =
+            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceColor, boundlessLayer.get());
+    ASSERT_TRUE(colorLayer != nullptr);
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        // shift child layer off bounds. If this layer was not boundless, we will
+        // expect the child layer to be cropped.
+        t.setPosition(boundlessLayer, 32, 32);
+        t.show(boundlessLayer);
+        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        // undo shift by parent
+        t.setPosition(colorLayer, -32, -32);
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify for boundless root layers with children, their transforms have an effect.
+TEST_F(BoundlessLayerTest, RootBoundlessLayerCanSetTransform) {
+    sp<SurfaceControl> rootBoundlessLayer =
+            mClient->createSurface(String8("RootBoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   0 /* flags */);
+    ASSERT_TRUE(rootBoundlessLayer != nullptr);
+    ASSERT_TRUE(rootBoundlessLayer->isValid());
+    sp<SurfaceControl> colorLayer =
+            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceColor,
+                                   rootBoundlessLayer.get());
+    ASSERT_TRUE(colorLayer != nullptr);
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setLayer(rootBoundlessLayer, INT32_MAX - 1);
+        t.setPosition(rootBoundlessLayer, 32, 32);
+        t.show(rootBoundlessLayer);
+        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+        t.hide(mFGSurfaceControl);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Top left of foreground must now be visible
+        mCapture->expectBGColor(31, 31);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(32, 32, 96, 96), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(97, 97);
+    }
+}
 
 class ScreenCaptureTest : public LayerUpdateTest {
 protected:
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index ebb58dc..ab1b252 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -66,7 +66,7 @@
 constexpr hwc2_layer_t HWC_LAYER = 5000;
 constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0);
 
-constexpr DisplayId DEFAULT_DISPLAY_ID = 42;
+constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
 constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
 constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
 
@@ -790,6 +790,9 @@
                                                     LayerProperties::HEIGHT,
                                                     LayerProperties::LAYER_FLAGS));
         });
+
+        auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
+        layerDrawingState.crop_legacy = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
         return layer;
     }
 
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index a8cd867..32fce67 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -248,8 +248,8 @@
 template <typename PhysicalDisplay>
 struct PhysicalDisplayId {};
 
-template <DisplayId displayId>
-using VirtualDisplayId = std::integral_constant<DisplayId, displayId>;
+template <DisplayId::Type displayId>
+using VirtualDisplayId = std::integral_constant<DisplayId::Type, displayId>;
 
 struct NoDisplayId {};
 
@@ -278,9 +278,9 @@
     }
 };
 
-template <DisplayId displayId>
+template <DisplayId::Type displayId>
 struct DisplayIdGetter<VirtualDisplayId<displayId>> {
-    static std::optional<DisplayId> get() { return displayId; }
+    static std::optional<DisplayId> get() { return DisplayId{displayId}; }
 };
 
 template <>
@@ -1103,7 +1103,7 @@
  */
 class GetBestColorModeTest : public DisplayTransactionTest {
 public:
-    static constexpr DisplayId DEFAULT_DISPLAY_ID = 777;
+    static constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
 
     GetBestColorModeTest()
           : DisplayTransactionTest(),
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index c5b60f1..a519f1f 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -175,6 +175,7 @@
     auto& mutableLayerDrawingState(sp<Layer> layer) { return layer->mDrawingState; }
 
     void setLayerSidebandStream(sp<Layer> layer, sp<NativeHandle> sidebandStream) {
+        layer->mDrawingState.sidebandStream = sidebandStream;
         layer->getBE().compositionInfo.hwc.sidebandStream = sidebandStream;
     }
 
diff --git a/services/vr/bufferhubd/consumer_queue_channel.cpp b/services/vr/bufferhubd/consumer_queue_channel.cpp
index 74b549d..5d7d4e9 100644
--- a/services/vr/bufferhubd/consumer_queue_channel.cpp
+++ b/services/vr/bufferhubd/consumer_queue_channel.cpp
@@ -80,27 +80,35 @@
 }
 
 void ConsumerQueueChannel::RegisterNewBuffer(
-    const std::shared_ptr<ProducerChannel>& producer_channel, size_t slot) {
-  ALOGD_IF(TRACE,
-           "ConsumerQueueChannel::RegisterNewBuffer: queue_id=%d buffer_id=%d "
-           "slot=%zu silent=%d",
-           buffer_id(), producer_channel->buffer_id(), slot, silent_);
+    const std::shared_ptr<ProducerChannel>& producer_channel,
+    size_t producer_slot) {
+  ALOGD_IF(TRACE, "%s: queue_id=%d buffer_id=%d slot=%zu silent=%d",
+           __FUNCTION__, buffer_id(), producer_channel->buffer_id(),
+           producer_slot, silent_);
   // Only register buffers if the queue is not silent.
-  if (!silent_) {
-    pending_buffer_slots_.emplace(producer_channel, slot);
-
-    // Signal the client that there is new buffer available.
-    SignalAvailable();
+  if (silent_) {
+    return;
   }
+
+  auto status = producer_channel->CreateConsumerStateMask();
+  if (!status.ok()) {
+    ALOGE("%s: Failed to create consumer state mask: %s", __FUNCTION__,
+          status.GetErrorMessage().c_str());
+    return;
+  }
+  uint64_t consumer_state_mask = status.get();
+
+  pending_buffer_slots_.emplace(producer_channel, producer_slot,
+                                consumer_state_mask);
+  // Signal the client that there is new buffer available.
+  SignalAvailable();
 }
 
 Status<std::vector<std::pair<RemoteChannelHandle, size_t>>>
 ConsumerQueueChannel::OnConsumerQueueImportBuffers(Message& message) {
   std::vector<std::pair<RemoteChannelHandle, size_t>> buffer_handles;
-  ATRACE_NAME("ConsumerQueueChannel::OnConsumerQueueImportBuffers");
-  ALOGD_IF(TRACE,
-           "ConsumerQueueChannel::OnConsumerQueueImportBuffers: "
-           "pending_buffer_slots=%zu",
+  ATRACE_NAME(__FUNCTION__);
+  ALOGD_IF(TRACE, "%s: pending_buffer_slots=%zu", __FUNCTION__,
            pending_buffer_slots_.size());
 
   // Indicate this is a silent queue that will not import buffers.
@@ -108,30 +116,30 @@
     return ErrorStatus(EBADR);
 
   while (!pending_buffer_slots_.empty()) {
-    auto producer_channel = pending_buffer_slots_.front().first.lock();
-    size_t producer_slot = pending_buffer_slots_.front().second;
+    auto producer_channel =
+        pending_buffer_slots_.front().producer_channel.lock();
+    size_t producer_slot = pending_buffer_slots_.front().producer_slot;
+    uint64_t consumer_state_mask =
+        pending_buffer_slots_.front().consumer_state_mask;
     pending_buffer_slots_.pop();
 
     // It's possible that the producer channel has expired. When this occurs,
     // ignore the producer channel.
     if (producer_channel == nullptr) {
-      ALOGW(
-          "ConsumerQueueChannel::OnConsumerQueueImportBuffers: producer "
-          "channel has already been expired.");
+      ALOGW("%s: producer channel has already been expired.", __FUNCTION__);
       continue;
     }
 
-    auto status = producer_channel->CreateConsumer(message);
+    auto status =
+        producer_channel->CreateConsumer(message, consumer_state_mask);
 
     // If no buffers are imported successfully, clear available and return an
     // error. Otherwise, return all consumer handles already imported
     // successfully, but keep available bits on, so that the client can retry
     // importing remaining consumer buffers.
     if (!status) {
-      ALOGE(
-          "ConsumerQueueChannel::OnConsumerQueueImportBuffers: Failed create "
-          "consumer: %s",
-          status.GetErrorMessage().c_str());
+      ALOGE("%s: Failed create consumer: %s", __FUNCTION__,
+            status.GetErrorMessage().c_str());
       if (buffer_handles.empty()) {
         ClearAvailable();
         return status.error_status();
diff --git a/services/vr/bufferhubd/include/private/dvr/consumer_queue_channel.h b/services/vr/bufferhubd/include/private/dvr/consumer_queue_channel.h
index 8f35437..3a81b03 100644
--- a/services/vr/bufferhubd/include/private/dvr/consumer_queue_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/consumer_queue_channel.h
@@ -3,8 +3,8 @@
 
 #include <queue>
 
-#include <private/dvr/bufferhub_rpc.h>
 #include <private/dvr/buffer_hub.h>
+#include <private/dvr/bufferhub_rpc.h>
 #include <private/dvr/consumer_channel.h>
 #include <private/dvr/producer_queue_channel.h>
 
@@ -28,7 +28,8 @@
   // Called by ProdcuerQueueChannel to notify consumer queue that a new
   // buffer has been allocated.
   void RegisterNewBuffer(
-      const std::shared_ptr<ProducerChannel>& producer_channel, size_t slot);
+      const std::shared_ptr<ProducerChannel>& producer_channel,
+      size_t producer_slot);
 
   // Called after clients been signaled by service that new buffer has been
   // allocated. Clients uses kOpConsumerQueueImportBuffers to import new
@@ -40,14 +41,29 @@
   void OnProducerClosed();
 
  private:
+  // Data structure to store relavant info of a newly allocated producer buffer
+  // so that consumer channel and buffer can be created later.
+  struct PendingBuffer {
+    PendingBuffer(std::shared_ptr<ProducerChannel> channel, size_t slot,
+                  uint64_t mask) {
+      producer_channel = channel;
+      producer_slot = slot;
+      consumer_state_mask = mask;
+    }
+    PendingBuffer() = delete;
+
+    std::weak_ptr<ProducerChannel> producer_channel;
+    size_t producer_slot;
+    uint64_t consumer_state_mask;
+  };
+
   std::shared_ptr<ProducerQueueChannel> GetProducer() const;
 
   // Pointer to the producer channel.
   std::weak_ptr<Channel> producer_;
 
   // Tracks newly allocated buffer producers along with it's slot number.
-  std::queue<std::pair<std::weak_ptr<ProducerChannel>, size_t>>
-      pending_buffer_slots_;
+  std::queue<PendingBuffer> pending_buffer_slots_;
 
   // Tracks how many buffers have this queue imported.
   size_t capacity_;
diff --git a/services/vr/bufferhubd/include/private/dvr/producer_channel.h b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
index 5868b09..3ad9c70 100644
--- a/services/vr/bufferhubd/include/private/dvr/producer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
@@ -53,7 +53,9 @@
 
   BufferDescription<BorrowedHandle> GetBuffer(uint64_t client_state_mask);
 
-  pdx::Status<RemoteChannelHandle> CreateConsumer(Message& message);
+  pdx::Status<RemoteChannelHandle> CreateConsumer(Message& message,
+                                                  uint64_t consumer_state_mask);
+  pdx::Status<uint64_t> CreateConsumerStateMask();
   pdx::Status<RemoteChannelHandle> OnNewConsumer(Message& message);
 
   pdx::Status<LocalFence> OnConsumerAcquire(Message& message);
@@ -93,7 +95,7 @@
   LocalFence post_fence_;
   LocalFence returned_fence_;
   size_t user_metadata_size_;  // size of user requested buffer buffer size.
-  size_t metadata_buf_size_;  // size of the ion buffer that holds metadata.
+  size_t metadata_buf_size_;   // size of the ion buffer that holds metadata.
 
   pdx::LocalHandle acquire_fence_fd_;
   pdx::LocalHandle release_fence_fd_;
@@ -111,6 +113,10 @@
   pdx::Status<void> OnProducerPost(Message& message, LocalFence acquire_fence);
   pdx::Status<LocalFence> OnProducerGain(Message& message);
 
+  // Remove consumer from atomics in shared memory based on consumer_state_mask.
+  // This function is used for clean up for failures in CreateConsumer method.
+  void RemoveConsumerClientMask(uint64_t consumer_state_mask);
+
   ProducerChannel(const ProducerChannel&) = delete;
   void operator=(const ProducerChannel&) = delete;
 };
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index 397c0ae..c6e8ea9 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -248,21 +248,7 @@
   return {GetBuffer(BufferHubDefs::kFirstClientBitMask)};
 }
 
-Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(Message& message) {
-  ATRACE_NAME("ProducerChannel::CreateConsumer");
-  ALOGD_IF(TRACE,
-           "ProducerChannel::CreateConsumer: buffer_id=%d, producer_owns=%d",
-           buffer_id(), producer_owns_);
-
-  int channel_id;
-  auto status = message.PushChannel(0, nullptr, &channel_id);
-  if (!status) {
-    ALOGE(
-        "ProducerChannel::CreateConsumer: Failed to push consumer channel: %s",
-        status.GetErrorMessage().c_str());
-    return ErrorStatus(ENOMEM);
-  }
-
+Status<uint64_t> ProducerChannel::CreateConsumerStateMask() {
   // Try find the next consumer state bit which has not been claimed by any
   // consumer yet.
   // memory_order_acquire is chosen here because all writes in other threads
@@ -277,7 +263,6 @@
         "consumers per producer: 63.");
     return ErrorStatus(E2BIG);
   }
-
   uint64_t updated_active_clients_bit_mask =
       current_active_clients_bit_mask | client_state_mask;
   // Set the updated value only if the current value stays the same as what was
@@ -286,28 +271,71 @@
   // thread, and the modification will be visible in other threads that acquire
   // active_clients_bit_mask_. If the comparison fails, load the result of
   // all writes from all threads to updated_active_clients_bit_mask.
-  if (!active_clients_bit_mask_->compare_exchange_weak(
-          current_active_clients_bit_mask, updated_active_clients_bit_mask,
-          std::memory_order_acq_rel, std::memory_order_acquire)) {
+  // Keep on finding the next available slient state mask until succeed or out
+  // of memory.
+  while (!active_clients_bit_mask_->compare_exchange_weak(
+      current_active_clients_bit_mask, updated_active_clients_bit_mask,
+      std::memory_order_acq_rel, std::memory_order_acquire)) {
     ALOGE("Current active clients bit mask is changed to %" PRIx64
-          ", which was expected to be %" PRIx64 ".",
+          ", which was expected to be %" PRIx64
+          ". Trying to generate a new client state mask to resolve race "
+          "condition.",
           updated_active_clients_bit_mask, current_active_clients_bit_mask);
-    return ErrorStatus(EBUSY);
+    client_state_mask = BufferHubDefs::FindNextAvailableClientStateMask(
+        current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
+    if (client_state_mask == 0ULL) {
+      ALOGE(
+          "ProducerChannel::CreateConsumer: reached the maximum mumber of "
+          "consumers per producer: 63.");
+      return ErrorStatus(E2BIG);
+    }
+    updated_active_clients_bit_mask =
+        current_active_clients_bit_mask | client_state_mask;
   }
 
-  auto consumer =
-      std::make_shared<ConsumerChannel>(service(), buffer_id(), channel_id,
-                                        client_state_mask, shared_from_this());
+  return {client_state_mask};
+}
+
+void ProducerChannel::RemoveConsumerClientMask(uint64_t consumer_state_mask) {
+  // Clear up the buffer state and fence state in case there is already
+  // something there due to possible race condition between producer post and
+  // consumer failed to create channel.
+  buffer_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
+  fence_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
+
+  // Restore the consumer state bit and make it visible in other threads that
+  // acquire the active_clients_bit_mask_.
+  active_clients_bit_mask_->fetch_and(~consumer_state_mask,
+                                      std::memory_order_release);
+}
+
+Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(
+    Message& message, uint64_t consumer_state_mask) {
+  ATRACE_NAME("ProducerChannel::CreateConsumer");
+  ALOGD_IF(TRACE,
+           "ProducerChannel::CreateConsumer: buffer_id=%d, producer_owns=%d",
+           buffer_id(), producer_owns_);
+
+  int channel_id;
+  auto status = message.PushChannel(0, nullptr, &channel_id);
+  if (!status) {
+    ALOGE(
+        "ProducerChannel::CreateConsumer: Failed to push consumer channel: %s",
+        status.GetErrorMessage().c_str());
+    RemoveConsumerClientMask(consumer_state_mask);
+    return ErrorStatus(ENOMEM);
+  }
+
+  auto consumer = std::make_shared<ConsumerChannel>(
+      service(), buffer_id(), channel_id, consumer_state_mask,
+      shared_from_this());
   const auto channel_status = service()->SetChannel(channel_id, consumer);
   if (!channel_status) {
     ALOGE(
         "ProducerChannel::CreateConsumer: failed to set new consumer channel: "
         "%s",
         channel_status.GetErrorMessage().c_str());
-    // Restore the consumer state bit and make it visible in other threads that
-    // acquire the active_clients_bit_mask_.
-    active_clients_bit_mask_->fetch_and(~client_state_mask,
-                                        std::memory_order_release);
+    RemoveConsumerClientMask(consumer_state_mask);
     return ErrorStatus(ENOMEM);
   }
 
@@ -327,7 +355,11 @@
 Status<RemoteChannelHandle> ProducerChannel::OnNewConsumer(Message& message) {
   ATRACE_NAME("ProducerChannel::OnNewConsumer");
   ALOGD_IF(TRACE, "ProducerChannel::OnNewConsumer: buffer_id=%d", buffer_id());
-  return CreateConsumer(message);
+  auto status = CreateConsumerStateMask();
+  if (!status.ok()) {
+    return status.error_status();
+  }
+  return CreateConsumer(message, /*consumer_state_mask=*/status.get());
 }
 
 Status<void> ProducerChannel::OnProducerPost(Message&,
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index 003775b..258b45b 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -55,7 +55,6 @@
     "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
     "-Wall",
     "-Werror",
-    // mVrClient unused in vr_composer_client.cpp
     "-Wno-error=unused-private-field",
     // Warnings in vr_hwc.cpp to be fixed after sync of goog/master.
     "-Wno-sign-compare",
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.cpp b/services/vr/hardware_composer/impl/vr_composer_client.cpp
index d775711..786d5fa 100644
--- a/services/vr/hardware_composer/impl/vr_composer_client.cpp
+++ b/services/vr/hardware_composer/impl/vr_composer_client.cpp
@@ -45,7 +45,7 @@
 }
 
 VrComposerClient::VrCommandEngine::VrCommandEngine(VrComposerClient& client)
-    : ComposerCommandEngine(client.mHal, client.mResources.get()), mVrClient(client),
+    : ComposerCommandEngine(client.mHal, client.mResources.get()),
       mVrHal(client.mVrHal) {}
 
 VrComposerClient::VrCommandEngine::~VrCommandEngine() {}
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.h b/services/vr/hardware_composer/impl/vr_composer_client.h
index 76b1c4f..de22640 100644
--- a/services/vr/hardware_composer/impl/vr_composer_client.h
+++ b/services/vr/hardware_composer/impl/vr_composer_client.h
@@ -54,7 +54,6 @@
 
     IVrComposerClient::BufferMetadata readBufferMetadata();
 
-    VrComposerClient& mVrClient;
     android::dvr::VrHwc& mVrHal;
 
     VrCommandEngine(const VrCommandEngine&) = delete;
diff --git a/services/vr/performanced/performance_service_tests.cpp b/services/vr/performanced/performance_service_tests.cpp
index 4065785..a24c889 100644
--- a/services/vr/performanced/performance_service_tests.cpp
+++ b/services/vr/performanced/performance_service_tests.cpp
@@ -12,16 +12,16 @@
 #include <thread>
 #include <utility>
 
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <dvr/performance_client_api.h>
 #include <gtest/gtest.h>
 #include <private/android_filesystem_config.h>
 
 #include "stdio_filebuf.h"
-#include "string_trim.h"
 #include "unique_file.h"
 
-using android::dvr::Trim;
+using android::base::Trim;
 using android::dvr::UniqueFile;
 using android::dvr::stdio_filebuf;
 
diff --git a/services/vr/performanced/string_trim.h b/services/vr/performanced/string_trim.h
deleted file mode 100644
index 7094e9f..0000000
--- a/services/vr/performanced/string_trim.h
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef ANDROID_DVR_PERFORMANCED_STRING_TRIM_H_
-#define ANDROID_DVR_PERFORMANCED_STRING_TRIM_H_
-
-#include <functional>
-#include <locale>
-#include <string>
-
-namespace android {
-namespace dvr {
-
-// Trims whitespace from the left side of |subject| and returns the result as a
-// new string.
-inline std::string LeftTrim(std::string subject) {
-  subject.erase(subject.begin(),
-                std::find_if(subject.begin(), subject.end(),
-                             std::not1(std::ptr_fun<int, int>(std::isspace))));
-  return subject;
-}
-
-// Trims whitespace from the right side of |subject| and returns the result as a
-// new string.
-inline std::string RightTrim(std::string subject) {
-  subject.erase(std::find_if(subject.rbegin(), subject.rend(),
-                             std::not1(std::ptr_fun<int, int>(std::isspace)))
-                    .base(),
-                subject.end());
-  return subject;
-}
-
-// Trims whitespace from the both sides of |subject| and returns the result as a
-// new string.
-inline std::string Trim(std::string subject) {
-  subject.erase(subject.begin(),
-                std::find_if(subject.begin(), subject.end(),
-                             std::not1(std::ptr_fun<int, int>(std::isspace))));
-  subject.erase(std::find_if(subject.rbegin(), subject.rend(),
-                             std::not1(std::ptr_fun<int, int>(std::isspace)))
-                    .base(),
-                subject.end());
-  return subject;
-}
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_PERFORMANCED_STRING_TRIM_H_
diff --git a/services/vr/performanced/task.cpp b/services/vr/performanced/task.cpp
index bda1682..2fc96bf 100644
--- a/services/vr/performanced/task.cpp
+++ b/services/vr/performanced/task.cpp
@@ -10,10 +10,10 @@
 #include <memory>
 #include <sstream>
 
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 
 #include "stdio_filebuf.h"
-#include "string_trim.h"
 
 namespace {
 
@@ -102,7 +102,7 @@
 
       // The status file has lines with the format <field>:<value>. Extract the
       // value after the colon.
-      return Trim(line.substr(offset + field.size() + 1));
+      return android::base::Trim(line.substr(offset + field.size() + 1));
     }
   }
 
@@ -123,7 +123,7 @@
       }
 
       std::string key = line.substr(0, offset);
-      std::string value = Trim(line.substr(offset + 1));
+      std::string value = android::base::Trim(line.substr(offset + 1));
 
       ALOGD_IF(TRACE, "Task::ReadStatusFields: key=\"%s\" value=\"%s\"",
                key.c_str(), value.c_str());
@@ -156,7 +156,7 @@
     std::string line = "";
     std::getline(file_stream, line);
 
-    return Trim(line);
+    return android::base::Trim(line);
   } else {
     return "";
   }