Merge changes Ic348cf9f,Icfd4cecf into sc-dev

* changes:
  Add deadline histograms into timestats
  Plumb refresh and render rates into shared timeline
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index 2362c9e..a70dffd 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -31,18 +31,8 @@
 #include <stddef.h>
 #include <jni.h>
 
-#ifndef __ANDROID__
-    // Value copied from 'bionic/libc/include/android/api-level.h' which is not available on
-    // non Android systems. It is set to 10000 which is same as __ANDROID_API_FUTURE__ value.
-    #ifndef __ANDROID_API__
-        #define __ANDROID_API__ 10000
-    #endif
-
-    // Value copied from 'bionic/libc/include/android/versioning.h' which is not available on
-    // non Android systems
-    #ifndef __INTRODUCED_IN
-        #define __INTRODUCED_IN(api_level)
-    #endif
+#if !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(__api_level) /* nothing */
 #endif
 
 #ifdef __cplusplus
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index 819a6a4..cac67d4 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -51,18 +51,8 @@
 #include <android/rect.h>
 #include <stdint.h>
 
-#ifndef __ANDROID__
-    // Value copied from 'bionic/libc/include/android/api-level.h' which is not available on
-    // non Android systems. It is set to 10000 which is same as __ANDROID_API_FUTURE__ value.
-    #ifndef __ANDROID_API__
-        #define __ANDROID_API__ 10000
-    #endif
-
-    // Value copied from 'bionic/libc/include/android/versioning.h' which is not available on
-    // non Android systems
-    #ifndef __INTRODUCED_IN
-        #define __INTRODUCED_IN(api_level)
-    #endif
+#if !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(__api_level) /* nothing */
 #endif
 
 #ifdef __cplusplus
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 8744ef7..11b714f 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -30,6 +30,7 @@
  */
 
 #include <string>
+#include <unordered_map>
 
 #include <android-base/chrono_utils.h>
 
@@ -155,6 +156,7 @@
         struct Finished {
             uint32_t empty1;
             uint32_t handled; // actually a bool, but we must maintain 8-byte alignment
+            nsecs_t consumeTime; // The time when the event was consumed by the receiving end
 
             inline size_t size() const { return sizeof(Finished); }
         } finished;
@@ -362,7 +364,8 @@
 
     /* Receives the finished signal from the consumer in reply to the original dispatch signal.
      * If a signal was received, returns the message sequence number,
-     * and whether the consumer handled the message.
+     * whether the consumer handled the message, and the time the event was first read by the
+     * consumer.
      *
      * The returned sequence number is never 0 unless the operation failed.
      *
@@ -371,7 +374,8 @@
      * Returns DEAD_OBJECT if the channel's peer has been closed.
      * Other errors probably indicate that the channel is broken.
      */
-    status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled);
+    status_t receiveFinishedSignal(
+            const std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)>& callback);
 
 private:
     std::shared_ptr<InputChannel> mChannel;
@@ -577,6 +581,13 @@
     };
     std::vector<SeqChain> mSeqChains;
 
+    // The time at which each event with the sequence number 'seq' was consumed.
+    // This data is provided in 'finishInputEvent' so that the receiving end can measure the latency
+    // This collection is populated when the event is received, and the entries are erased when the
+    // events are finished. It should not grow infinitely because if an event is not ack'd, ANR
+    // will be raised for that connection, and no further events will be posted to that channel.
+    std::unordered_map<uint32_t /*seq*/, nsecs_t /*consumeTime*/> mConsumeTimes;
+
     status_t consumeBatch(InputEventFactoryInterface* factory,
             nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
     status_t consumeSamples(InputEventFactoryInterface* factory,
@@ -589,6 +600,8 @@
     ssize_t findBatch(int32_t deviceId, int32_t source) const;
     ssize_t findTouchState(int32_t deviceId, int32_t source) const;
 
+    nsecs_t getConsumeTime(uint32_t seq) const;
+    void popConsumeTime(uint32_t seq);
     status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled);
 
     static void rewriteMessage(TouchState& state, InputMessage& msg);
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index b9d00fe..1a4ede1 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -769,200 +769,116 @@
     return NO_ERROR;
 }
 
-status_t Parcel::writeUtf8AsUtf16(const std::optional<std::string>& str) {
-  if (!str) {
-    return writeInt32(-1);
-  }
-  return writeUtf8AsUtf16(*str);
-}
 
-status_t Parcel::writeUtf8AsUtf16(const std::unique_ptr<std::string>& str) {
-  if (!str) {
-    return writeInt32(-1);
-  }
-  return writeUtf8AsUtf16(*str);
-}
+status_t Parcel::writeUtf8AsUtf16(const std::optional<std::string>& str) { return writeData(str); }
+status_t Parcel::writeUtf8AsUtf16(const std::unique_ptr<std::string>& str) { return writeData(str); }
 
-status_t Parcel::writeByteVectorInternal(const int8_t* data, size_t size) {
-    if (size > std::numeric_limits<int32_t>::max()) {
-        return BAD_VALUE;
-    }
+status_t Parcel::writeString16(const std::optional<String16>& str) { return writeData(str); }
+status_t Parcel::writeString16(const std::unique_ptr<String16>& str) { return writeData(str); }
 
-    status_t status = writeInt32(size);
-    if (status != OK) {
-        return status;
-    }
+status_t Parcel::writeByteVector(const std::vector<int8_t>& val) { return writeData(val); }
+status_t Parcel::writeByteVector(const std::optional<std::vector<int8_t>>& val) { return writeData(val); }
+status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val) { return writeData(val); }
+status_t Parcel::writeByteVector(const std::vector<uint8_t>& val) { return writeData(val); }
+status_t Parcel::writeByteVector(const std::optional<std::vector<uint8_t>>& val) { return writeData(val); }
+status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val){ return writeData(val); }
+status_t Parcel::writeInt32Vector(const std::vector<int32_t>& val) { return writeData(val); }
+status_t Parcel::writeInt32Vector(const std::optional<std::vector<int32_t>>& val) { return writeData(val); }
+status_t Parcel::writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val) { return writeData(val); }
+status_t Parcel::writeInt64Vector(const std::vector<int64_t>& val) { return writeData(val); }
+status_t Parcel::writeInt64Vector(const std::optional<std::vector<int64_t>>& val) { return writeData(val); }
+status_t Parcel::writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val) { return writeData(val); }
+status_t Parcel::writeUint64Vector(const std::vector<uint64_t>& val) { return writeData(val); }
+status_t Parcel::writeUint64Vector(const std::optional<std::vector<uint64_t>>& val) { return writeData(val); }
+status_t Parcel::writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val) { return writeData(val); }
+status_t Parcel::writeFloatVector(const std::vector<float>& val) { return writeData(val); }
+status_t Parcel::writeFloatVector(const std::optional<std::vector<float>>& val) { return writeData(val); }
+status_t Parcel::writeFloatVector(const std::unique_ptr<std::vector<float>>& val) { return writeData(val); }
+status_t Parcel::writeDoubleVector(const std::vector<double>& val) { return writeData(val); }
+status_t Parcel::writeDoubleVector(const std::optional<std::vector<double>>& val) { return writeData(val); }
+status_t Parcel::writeDoubleVector(const std::unique_ptr<std::vector<double>>& val) { return writeData(val); }
+status_t Parcel::writeBoolVector(const std::vector<bool>& val) { return writeData(val); }
+status_t Parcel::writeBoolVector(const std::optional<std::vector<bool>>& val) { return writeData(val); }
+status_t Parcel::writeBoolVector(const std::unique_ptr<std::vector<bool>>& val) { return writeData(val); }
+status_t Parcel::writeCharVector(const std::vector<char16_t>& val) { return writeData(val); }
+status_t Parcel::writeCharVector(const std::optional<std::vector<char16_t>>& val) { return writeData(val); }
+status_t Parcel::writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val) { return writeData(val); }
 
-    return write(data, size);
-}
-
-status_t Parcel::writeByteVector(const std::vector<int8_t>& val) {
-    return writeByteVectorInternal(val.data(), val.size());
-}
-
-status_t Parcel::writeByteVector(const std::optional<std::vector<int8_t>>& val)
-{
-    if (!val) return writeInt32(-1);
-    return writeByteVectorInternal(val->data(), val->size());
-}
-
-status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val)
-{
-    if (!val) return writeInt32(-1);
-    return writeByteVectorInternal(val->data(), val->size());
-}
-
-status_t Parcel::writeByteVector(const std::vector<uint8_t>& val) {
-    return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val.data()), val.size());
-}
-
-status_t Parcel::writeByteVector(const std::optional<std::vector<uint8_t>>& val)
-{
-    if (!val) return writeInt32(-1);
-    return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size());
-}
-
-status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val)
-{
-    if (!val) return writeInt32(-1);
-    return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size());
-}
-
-status_t Parcel::writeInt32Vector(const std::vector<int32_t>& val)
-{
-    return writeTypedVector(val, &Parcel::writeInt32);
-}
-
-status_t Parcel::writeInt32Vector(const std::optional<std::vector<int32_t>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeInt32);
-}
-
-status_t Parcel::writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeInt32);
-}
-
-status_t Parcel::writeInt64Vector(const std::vector<int64_t>& val)
-{
-    return writeTypedVector(val, &Parcel::writeInt64);
-}
-
-status_t Parcel::writeInt64Vector(const std::optional<std::vector<int64_t>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeInt64);
-}
-
-status_t Parcel::writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeInt64);
-}
-
-status_t Parcel::writeUint64Vector(const std::vector<uint64_t>& val)
-{
-    return writeTypedVector(val, &Parcel::writeUint64);
-}
-
-status_t Parcel::writeUint64Vector(const std::optional<std::vector<uint64_t>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeUint64);
-}
-
-status_t Parcel::writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeUint64);
-}
-
-status_t Parcel::writeFloatVector(const std::vector<float>& val)
-{
-    return writeTypedVector(val, &Parcel::writeFloat);
-}
-
-status_t Parcel::writeFloatVector(const std::optional<std::vector<float>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeFloat);
-}
-
-status_t Parcel::writeFloatVector(const std::unique_ptr<std::vector<float>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeFloat);
-}
-
-status_t Parcel::writeDoubleVector(const std::vector<double>& val)
-{
-    return writeTypedVector(val, &Parcel::writeDouble);
-}
-
-status_t Parcel::writeDoubleVector(const std::optional<std::vector<double>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeDouble);
-}
-
-status_t Parcel::writeDoubleVector(const std::unique_ptr<std::vector<double>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeDouble);
-}
-
-status_t Parcel::writeBoolVector(const std::vector<bool>& val)
-{
-    return writeTypedVector(val, &Parcel::writeBool);
-}
-
-status_t Parcel::writeBoolVector(const std::optional<std::vector<bool>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeBool);
-}
-
-status_t Parcel::writeBoolVector(const std::unique_ptr<std::vector<bool>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeBool);
-}
-
-status_t Parcel::writeCharVector(const std::vector<char16_t>& val)
-{
-    return writeTypedVector(val, &Parcel::writeChar);
-}
-
-status_t Parcel::writeCharVector(const std::optional<std::vector<char16_t>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeChar);
-}
-
-status_t Parcel::writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeChar);
-}
-
-status_t Parcel::writeString16Vector(const std::vector<String16>& val)
-{
-    return writeTypedVector(val, &Parcel::writeString16);
-}
-
+status_t Parcel::writeString16Vector(const std::vector<String16>& val) { return writeData(val); }
 status_t Parcel::writeString16Vector(
-        const std::optional<std::vector<std::optional<String16>>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeString16);
-}
-
+        const std::optional<std::vector<std::optional<String16>>>& val) { return writeData(val); }
 status_t Parcel::writeString16Vector(
-        const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeString16);
-}
-
+        const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val) { return writeData(val); }
 status_t Parcel::writeUtf8VectorAsUtf16Vector(
-                        const std::optional<std::vector<std::optional<std::string>>>& val) {
-    return writeNullableTypedVector(val, &Parcel::writeUtf8AsUtf16);
-}
-
+                        const std::optional<std::vector<std::optional<std::string>>>& val) { return writeData(val); }
 status_t Parcel::writeUtf8VectorAsUtf16Vector(
-                        const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val) {
-    return writeNullableTypedVector(val, &Parcel::writeUtf8AsUtf16);
-}
+                        const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val) { return writeData(val); }
+status_t Parcel::writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val) { return writeData(val); }
 
-status_t Parcel::writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val) {
-    return writeTypedVector(val, &Parcel::writeUtf8AsUtf16);
-}
+status_t Parcel::writeUniqueFileDescriptorVector(const std::vector<base::unique_fd>& val) { return writeData(val); }
+status_t Parcel::writeUniqueFileDescriptorVector(const std::optional<std::vector<base::unique_fd>>& val) { return writeData(val); }
+status_t Parcel::writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<base::unique_fd>>& val) { return writeData(val); }
+
+status_t Parcel::writeStrongBinderVector(const std::vector<sp<IBinder>>& val) { return writeData(val); }
+status_t Parcel::writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val) { return writeData(val); }
+status_t Parcel::writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val) { return writeData(val); }
+
+status_t Parcel::writeParcelable(const Parcelable& parcelable) { return writeData(parcelable); }
+
+status_t Parcel::readUtf8FromUtf16(std::optional<std::string>* str) const { return readData(str); }
+status_t Parcel::readUtf8FromUtf16(std::unique_ptr<std::string>* str) const { return readData(str); }
+
+status_t Parcel::readString16(std::optional<String16>* pArg) const { return readData(pArg); }
+status_t Parcel::readString16(std::unique_ptr<String16>* pArg) const { return readData(pArg); }
+
+status_t Parcel::readByteVector(std::vector<int8_t>* val) const { return readData(val); }
+status_t Parcel::readByteVector(std::vector<uint8_t>* val) const { return readData(val); }
+status_t Parcel::readByteVector(std::optional<std::vector<int8_t>>* val) const { return readData(val); }
+status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const { return readData(val); }
+status_t Parcel::readByteVector(std::optional<std::vector<uint8_t>>* val) const { return readData(val); }
+status_t Parcel::readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const { return readData(val); }
+status_t Parcel::readInt32Vector(std::optional<std::vector<int32_t>>* val) const { return readData(val); }
+status_t Parcel::readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const { return readData(val); }
+status_t Parcel::readInt32Vector(std::vector<int32_t>* val) const { return readData(val); }
+status_t Parcel::readInt64Vector(std::optional<std::vector<int64_t>>* val) const { return readData(val); }
+status_t Parcel::readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const { return readData(val); }
+status_t Parcel::readInt64Vector(std::vector<int64_t>* val) const { return readData(val); }
+status_t Parcel::readUint64Vector(std::optional<std::vector<uint64_t>>* val) const { return readData(val); }
+status_t Parcel::readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const { return readData(val); }
+status_t Parcel::readUint64Vector(std::vector<uint64_t>* val) const { return readData(val); }
+status_t Parcel::readFloatVector(std::optional<std::vector<float>>* val) const { return readData(val); }
+status_t Parcel::readFloatVector(std::unique_ptr<std::vector<float>>* val) const { return readData(val); }
+status_t Parcel::readFloatVector(std::vector<float>* val) const { return readData(val); }
+status_t Parcel::readDoubleVector(std::optional<std::vector<double>>* val) const { return readData(val); }
+status_t Parcel::readDoubleVector(std::unique_ptr<std::vector<double>>* val) const { return readData(val); }
+status_t Parcel::readDoubleVector(std::vector<double>* val) const { return readData(val); }
+status_t Parcel::readBoolVector(std::optional<std::vector<bool>>* val) const { return readData(val); }
+status_t Parcel::readBoolVector(std::unique_ptr<std::vector<bool>>* val) const { return readData(val); }
+status_t Parcel::readBoolVector(std::vector<bool>* val) const { return readData(val); }
+status_t Parcel::readCharVector(std::optional<std::vector<char16_t>>* val) const { return readData(val); }
+status_t Parcel::readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const { return readData(val); }
+status_t Parcel::readCharVector(std::vector<char16_t>* val) const { return readData(val); }
+
+status_t Parcel::readString16Vector(
+        std::optional<std::vector<std::optional<String16>>>* val) const { return readData(val); }
+status_t Parcel::readString16Vector(
+        std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const { return readData(val); }
+status_t Parcel::readString16Vector(std::vector<String16>* val) const { return readData(val); }
+status_t Parcel::readUtf8VectorFromUtf16Vector(
+        std::optional<std::vector<std::optional<std::string>>>* val) const { return readData(val); }
+status_t Parcel::readUtf8VectorFromUtf16Vector(
+        std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const { return readData(val); }
+status_t Parcel::readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const { return readData(val); }
+
+status_t Parcel::readUniqueFileDescriptorVector(std::optional<std::vector<base::unique_fd>>* val) const { return readData(val); }
+status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr<std::vector<base::unique_fd>>* val) const { return readData(val); }
+status_t Parcel::readUniqueFileDescriptorVector(std::vector<base::unique_fd>* val) const { return readData(val); }
+
+status_t Parcel::readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const { return readData(val); }
+status_t Parcel::readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const { return readData(val); }
+status_t Parcel::readStrongBinderVector(std::vector<sp<IBinder>>* val) const { return readData(val); }
+
+status_t Parcel::readParcelable(Parcelable* parcelable) const { return readData(parcelable); }
 
 status_t Parcel::writeInt32(int32_t val)
 {
@@ -1091,24 +1007,6 @@
     return err;
 }
 
-status_t Parcel::writeString16(const std::optional<String16>& str)
-{
-    if (!str) {
-        return writeInt32(-1);
-    }
-
-    return writeString16(*str);
-}
-
-status_t Parcel::writeString16(const std::unique_ptr<String16>& str)
-{
-    if (!str) {
-        return writeInt32(-1);
-    }
-
-    return writeString16(*str);
-}
-
 status_t Parcel::writeString16(const String16& str)
 {
     return writeString16(str.string(), str.size());
@@ -1138,32 +1036,6 @@
     return flattenBinder(val);
 }
 
-status_t Parcel::writeStrongBinderVector(const std::vector<sp<IBinder>>& val)
-{
-    return writeTypedVector(val, &Parcel::writeStrongBinder);
-}
-
-status_t Parcel::writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeStrongBinder);
-}
-
-status_t Parcel::writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val)
-{
-    return writeNullableTypedVector(val, &Parcel::writeStrongBinder);
-}
-
-status_t Parcel::readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readNullableStrongBinder);
-}
-
-status_t Parcel::readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readNullableStrongBinder);
-}
-
-status_t Parcel::readStrongBinderVector(std::vector<sp<IBinder>>* val) const {
-    return readTypedVector(val, &Parcel::readStrongBinder);
-}
 
 status_t Parcel::writeRawNullableParcelable(const Parcelable* parcelable) {
     if (!parcelable) {
@@ -1173,14 +1045,6 @@
     return writeParcelable(*parcelable);
 }
 
-status_t Parcel::writeParcelable(const Parcelable& parcelable) {
-    status_t status = writeInt32(1);  // parcelable is not null.
-    if (status != OK) {
-        return status;
-    }
-    return parcelable.writeToParcel(this);
-}
-
 status_t Parcel::writeNativeHandle(const native_handle* handle)
 {
     if (!handle || handle->version != sizeof(native_handle))
@@ -1251,18 +1115,6 @@
     return writeDupFileDescriptor(fd.get());
 }
 
-status_t Parcel::writeUniqueFileDescriptorVector(const std::vector<base::unique_fd>& val) {
-    return writeTypedVector(val, &Parcel::writeUniqueFileDescriptor);
-}
-
-status_t Parcel::writeUniqueFileDescriptorVector(const std::optional<std::vector<base::unique_fd>>& val) {
-    return writeNullableTypedVector(val, &Parcel::writeUniqueFileDescriptor);
-}
-
-status_t Parcel::writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<base::unique_fd>>& val) {
-    return writeNullableTypedVector(val, &Parcel::writeUniqueFileDescriptor);
-}
-
 status_t Parcel::writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob)
 {
     if (len > INT32_MAX) {
@@ -1477,31 +1329,6 @@
     goto data_sorted;
 }
 
-status_t Parcel::readVectorSizeWithCoarseBoundCheck(int32_t *size) const {
-    int32_t requestedSize;
-    const status_t status = readInt32(&requestedSize);
-    if (status != NO_ERROR) return status;
-
-    // We permit negative sizes, which indicate presence of a nullable vector,
-    // i.e. a vector embedded in std::optional, std::unique_ptr, or std::shared_ptr.
-    if (requestedSize > 0) {
-        // Check if there are fewer bytes than vector elements.
-        // A lower bound is 1 byte per element, satisfied by some enum and int8_t and uint8_t.
-        const size_t availableBytes = dataAvail();
-        if (static_cast<size_t>(requestedSize) > availableBytes) {
-            // We have a size that is greater than the number of bytes available.
-            // On bounds failure we do not 'rewind' position by 4 bytes of the size already read.
-            ALOGW("%s: rejecting out of bounds vector size (requestedSize):%d "
-                    "Parcel{dataAvail:%zu mDataSize:%zu mDataPos:%zu mDataCapacity:%zu}",
-                    __func__, requestedSize, availableBytes, mDataSize, mDataPos, mDataCapacity);
-            return BAD_VALUE;
-        }
-    }
-
-    *size = requestedSize;
-    return NO_ERROR;
-}
-
 status_t Parcel::read(void* outData, size_t len) const
 {
     if (len > INT32_MAX) {
@@ -1605,236 +1432,6 @@
     return err;
 }
 
-status_t Parcel::readByteVector(std::vector<int8_t>* val) const {
-    size_t size;
-    if (status_t status = reserveOutVector(val, &size); status != OK) return status;
-    return readByteVectorInternal(val, size);
-}
-
-status_t Parcel::readByteVector(std::vector<uint8_t>* val) const {
-    size_t size;
-    if (status_t status = reserveOutVector(val, &size); status != OK) return status;
-    return readByteVectorInternal(val, size);
-}
-
-status_t Parcel::readByteVector(std::optional<std::vector<int8_t>>* val) const {
-    size_t size;
-    if (status_t status = reserveOutVector(val, &size); status != OK) return status;
-    if (!*val) {
-        // reserveOutVector does not create the out vector if size is < 0.
-        // This occurs when writing a null byte vector.
-        return OK;
-    }
-    return readByteVectorInternal(&**val, size);
-}
-
-status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const {
-    size_t size;
-    if (status_t status = reserveOutVector(val, &size); status != OK) return status;
-    if (val->get() == nullptr) {
-        // reserveOutVector does not create the out vector if size is < 0.
-        // This occurs when writing a null byte vector.
-        return OK;
-    }
-    return readByteVectorInternal(val->get(), size);
-}
-
-status_t Parcel::readByteVector(std::optional<std::vector<uint8_t>>* val) const {
-    size_t size;
-    if (status_t status = reserveOutVector(val, &size); status != OK) return status;
-    if (!*val) {
-        // reserveOutVector does not create the out vector if size is < 0.
-        // This occurs when writing a null byte vector.
-        return OK;
-    }
-    return readByteVectorInternal(&**val, size);
-}
-
-status_t Parcel::readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const {
-    size_t size;
-    if (status_t status = reserveOutVector(val, &size); status != OK) return status;
-    if (val->get() == nullptr) {
-        // reserveOutVector does not create the out vector if size is < 0.
-        // This occurs when writing a null byte vector.
-        return OK;
-    }
-    return readByteVectorInternal(val->get(), size);
-}
-
-status_t Parcel::readInt32Vector(std::optional<std::vector<int32_t>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readInt32);
-}
-
-status_t Parcel::readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readInt32);
-}
-
-status_t Parcel::readInt32Vector(std::vector<int32_t>* val) const {
-    return readTypedVector(val, &Parcel::readInt32);
-}
-
-status_t Parcel::readInt64Vector(std::optional<std::vector<int64_t>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readInt64);
-}
-
-status_t Parcel::readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readInt64);
-}
-
-status_t Parcel::readInt64Vector(std::vector<int64_t>* val) const {
-    return readTypedVector(val, &Parcel::readInt64);
-}
-
-status_t Parcel::readUint64Vector(std::optional<std::vector<uint64_t>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readUint64);
-}
-
-status_t Parcel::readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readUint64);
-}
-
-status_t Parcel::readUint64Vector(std::vector<uint64_t>* val) const {
-    return readTypedVector(val, &Parcel::readUint64);
-}
-
-status_t Parcel::readFloatVector(std::optional<std::vector<float>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readFloat);
-}
-
-status_t Parcel::readFloatVector(std::unique_ptr<std::vector<float>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readFloat);
-}
-
-status_t Parcel::readFloatVector(std::vector<float>* val) const {
-    return readTypedVector(val, &Parcel::readFloat);
-}
-
-status_t Parcel::readDoubleVector(std::optional<std::vector<double>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readDouble);
-}
-
-status_t Parcel::readDoubleVector(std::unique_ptr<std::vector<double>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readDouble);
-}
-
-status_t Parcel::readDoubleVector(std::vector<double>* val) const {
-    return readTypedVector(val, &Parcel::readDouble);
-}
-
-status_t Parcel::readBoolVector(std::optional<std::vector<bool>>* val) const {
-    const int32_t start = dataPosition();
-    int32_t size;
-    status_t status = readVectorSizeWithCoarseBoundCheck(&size);
-    val->reset();
-
-    if (status != OK || size < 0) {
-        return status;
-    }
-
-    setDataPosition(start);
-    val->emplace();
-
-    status = readBoolVector(&**val);
-
-    if (status != OK) {
-        val->reset();
-    }
-
-    return status;
-}
-
-status_t Parcel::readBoolVector(std::unique_ptr<std::vector<bool>>* val) const {
-    const int32_t start = dataPosition();
-    int32_t size;
-    status_t status = readVectorSizeWithCoarseBoundCheck(&size);
-    val->reset();
-
-    if (status != OK || size < 0) {
-        return status;
-    }
-
-    setDataPosition(start);
-    val->reset(new (std::nothrow) std::vector<bool>());
-
-    status = readBoolVector(val->get());
-
-    if (status != OK) {
-        val->reset();
-    }
-
-    return status;
-}
-
-status_t Parcel::readBoolVector(std::vector<bool>* val) const {
-    int32_t size;
-    status_t status = readVectorSizeWithCoarseBoundCheck(&size);
-
-    if (status != OK) {
-        return status;
-    }
-
-    if (size < 0) {
-        return UNEXPECTED_NULL;
-    }
-
-    val->resize(size);
-
-    /* C++ bool handling means a vector of bools isn't necessarily addressable
-     * (we might use individual bits)
-     */
-    bool data;
-    for (int32_t i = 0; i < size; ++i) {
-        status = readBool(&data);
-        (*val)[i] = data;
-
-        if (status != OK) {
-            return status;
-        }
-    }
-
-    return OK;
-}
-
-status_t Parcel::readCharVector(std::optional<std::vector<char16_t>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readChar);
-}
-
-status_t Parcel::readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readChar);
-}
-
-status_t Parcel::readCharVector(std::vector<char16_t>* val) const {
-    return readTypedVector(val, &Parcel::readChar);
-}
-
-status_t Parcel::readString16Vector(
-        std::optional<std::vector<std::optional<String16>>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readString16);
-}
-
-status_t Parcel::readString16Vector(
-        std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readString16);
-}
-
-status_t Parcel::readString16Vector(std::vector<String16>* val) const {
-    return readTypedVector(val, &Parcel::readString16);
-}
-
-status_t Parcel::readUtf8VectorFromUtf16Vector(
-        std::optional<std::vector<std::optional<std::string>>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readUtf8FromUtf16);
-}
-
-status_t Parcel::readUtf8VectorFromUtf16Vector(
-        std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readUtf8FromUtf16);
-}
-
-status_t Parcel::readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const {
-    return readTypedVector(val, &Parcel::readUtf8FromUtf16);
-}
-
 status_t Parcel::readInt32(int32_t *pArg) const
 {
     return readAligned(pArg);
@@ -2007,36 +1604,6 @@
     return NO_ERROR;
 }
 
-status_t Parcel::readUtf8FromUtf16(std::optional<std::string>* str) const {
-    const int32_t start = dataPosition();
-    int32_t size;
-    status_t status = readInt32(&size);
-    str->reset();
-
-    if (status != OK || size < 0) {
-        return status;
-    }
-
-    setDataPosition(start);
-    str->emplace();
-    return readUtf8FromUtf16(&**str);
-}
-
-status_t Parcel::readUtf8FromUtf16(std::unique_ptr<std::string>* str) const {
-    const int32_t start = dataPosition();
-    int32_t size;
-    status_t status = readInt32(&size);
-    str->reset();
-
-    if (status != OK || size < 0) {
-        return status;
-    }
-
-    setDataPosition(start);
-    str->reset(new (std::nothrow) std::string());
-    return readUtf8FromUtf16(str->get());
-}
-
 const char* Parcel::readCString() const
 {
     if (mDataPos < mDataSize) {
@@ -2103,51 +1670,6 @@
     return String16();
 }
 
-status_t Parcel::readString16(std::optional<String16>* pArg) const
-{
-    const int32_t start = dataPosition();
-    int32_t size;
-    status_t status = readInt32(&size);
-    pArg->reset();
-
-    if (status != OK || size < 0) {
-        return status;
-    }
-
-    setDataPosition(start);
-    pArg->emplace();
-
-    status = readString16(&**pArg);
-
-    if (status != OK) {
-        pArg->reset();
-    }
-
-    return status;
-}
-
-status_t Parcel::readString16(std::unique_ptr<String16>* pArg) const
-{
-    const int32_t start = dataPosition();
-    int32_t size;
-    status_t status = readInt32(&size);
-    pArg->reset();
-
-    if (status != OK || size < 0) {
-        return status;
-    }
-
-    setDataPosition(start);
-    pArg->reset(new (std::nothrow) String16());
-
-    status = readString16(pArg->get());
-
-    if (status != OK) {
-        pArg->reset();
-    }
-
-    return status;
-}
 
 status_t Parcel::readString16(String16* pArg) const
 {
@@ -2204,18 +1726,6 @@
     return val;
 }
 
-status_t Parcel::readParcelable(Parcelable* parcelable) const {
-    int32_t have_parcelable = 0;
-    status_t status = readInt32(&have_parcelable);
-    if (status != OK) {
-        return status;
-    }
-    if (!have_parcelable) {
-        return UNEXPECTED_NULL;
-    }
-    return parcelable->readFromParcel(this);
-}
-
 int32_t Parcel::readExceptionCode() const
 {
     binder::Status status;
@@ -2334,18 +1844,6 @@
     return OK;
 }
 
-status_t Parcel::readUniqueFileDescriptorVector(std::optional<std::vector<base::unique_fd>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor);
-}
-
-status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr<std::vector<base::unique_fd>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor);
-}
-
-status_t Parcel::readUniqueFileDescriptorVector(std::vector<base::unique_fd>* val) const {
-    return readTypedVector(val, &Parcel::readUniqueFileDescriptor);
-}
-
 status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob) const
 {
     int32_t blobType;
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index dc8d74c..889e15a 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -101,4 +101,11 @@
      * This does nothing if this observer was not already registered.
      */
     void unregisterPackageChangeObserver(in IPackageChangeObserver observer);
+
+    /**
+     * Returns true if the package has the SHA 256 version of the signing certificate.
+     * @see PackageManager#hasSigningCertificate(String, byte[], int), where type
+     * has been set to {@link PackageManager#CERT_INPUT_SHA256}.
+     */
+    boolean hasSha256SigningCertificate(in @utf8InCpp String packageName, in byte[] certificate);
 }
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 54c49e4..7b298f5 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -190,34 +190,47 @@
     // Write an Enum vector with underlying type int8_t.
     // Does not use padding; each byte is contiguous.
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            writeEnumVector(const std::vector<T>& val);
+    status_t            writeEnumVector(const std::vector<T>& val)
+            { return writeData(val); }
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            writeEnumVector(const std::optional<std::vector<T>>& val);
+    status_t            writeEnumVector(const std::optional<std::vector<T>>& val)
+            { return writeData(val); }
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead")));
+    status_t            writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead")))
+            { return writeData(val); }
     // Write an Enum vector with underlying type != int8_t.
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            writeEnumVector(const std::vector<T>& val);
+    status_t            writeEnumVector(const std::vector<T>& val)
+            { return writeData(val); }
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            writeEnumVector(const std::optional<std::vector<T>>& val);
+    status_t            writeEnumVector(const std::optional<std::vector<T>>& val)
+            { return writeData(val); }
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead")));
+    status_t            writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead")))
+            { return writeData(val); }
 
     template<typename T>
-    status_t            writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val);
+    status_t            writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val)
+            { return writeData(val); }
     template<typename T>
-    status_t            writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead")));
+    status_t            writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead")))
+            { return writeData(val); }
     template<typename T>
-    status_t            writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead")));
+    status_t            writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead")))
+            { return writeData(val); }
     template<typename T>
-    status_t            writeParcelableVector(const std::shared_ptr<std::vector<std::optional<T>>>& val);
+    status_t            writeParcelableVector(const std::shared_ptr<std::vector<std::optional<T>>>& val)
+            { return writeData(val); }
     template<typename T>
-    status_t            writeParcelableVector(const std::vector<T>& val);
+    status_t            writeParcelableVector(const std::vector<T>& val)
+            { return writeData(val); }
 
     template<typename T>
-    status_t            writeNullableParcelable(const std::optional<T>& parcelable);
+    status_t            writeNullableParcelable(const std::optional<T>& parcelable)
+            { return writeData(parcelable); }
     template<typename T>
-    status_t            writeNullableParcelable(const std::unique_ptr<T>& parcelable) __attribute__((deprecated("use std::optional version instead")));
+    status_t            writeNullableParcelable(const std::unique_ptr<T>& parcelable) __attribute__((deprecated("use std::optional version instead")))
+            { return writeData(parcelable); }
 
     status_t            writeParcelable(const Parcelable& parcelable);
 
@@ -335,35 +348,48 @@
     // Read an Enum vector with underlying type int8_t.
     // Does not use padding; each byte is contiguous.
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            readEnumVector(std::vector<T>* val) const;
+    status_t            readEnumVector(std::vector<T>* val) const
+            { return readData(val); }
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead")));
+    status_t            readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead")))
+            { return readData(val); }
     template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            readEnumVector(std::optional<std::vector<T>>* val) const;
+    status_t            readEnumVector(std::optional<std::vector<T>>* val) const
+            { return readData(val); }
     // Read an Enum vector with underlying type != int8_t.
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            readEnumVector(std::vector<T>* val) const;
+    status_t            readEnumVector(std::vector<T>* val) const
+            { return readData(val); }
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead")));
+    status_t            readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead")))
+            { return readData(val); }
     template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
-    status_t            readEnumVector(std::optional<std::vector<T>>* val) const;
+    status_t            readEnumVector(std::optional<std::vector<T>>* val) const
+            { return readData(val); }
 
     template<typename T>
     status_t            readParcelableVector(
-                            std::optional<std::vector<std::optional<T>>>* val) const;
+                            std::optional<std::vector<std::optional<T>>>* val) const
+            { return readData(val); }
     template<typename T>
     status_t            readParcelableVector(
-                            std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const __attribute__((deprecated("use std::optional version instead")));
+                            std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const __attribute__((deprecated("use std::optional version instead")))
+            { return readData(val); }
     template<typename T>
-    status_t            readParcelableVector(std::vector<T>* val) const;
+    status_t            readParcelableVector(std::vector<T>* val) const
+            { return readData(val); }
 
     status_t            readParcelable(Parcelable* parcelable) const;
 
     template<typename T>
-    status_t            readParcelable(std::optional<T>* parcelable) const;
+    status_t            readParcelable(std::optional<T>* parcelable) const
+            { return readData(parcelable); }
     template<typename T>
-    status_t            readParcelable(std::unique_ptr<T>* parcelable) const __attribute__((deprecated("use std::optional version instead")));
+    status_t            readParcelable(std::unique_ptr<T>* parcelable) const __attribute__((deprecated("use std::optional version instead")))
+            { return readData(parcelable); }
 
+    // If strong binder would be nullptr, readStrongBinder() returns an error.
+    // TODO: T must be derived from IInterface, fix for clarity.
     template<typename T>
     status_t            readStrongBinder(sp<T>* val) const;
 
@@ -418,20 +444,13 @@
     template<typename T>
     status_t            read(LightFlattenable<T>& val) const;
 
+    // resizeOutVector is used to resize AIDL out vector parameters.
     template<typename T>
     status_t            resizeOutVector(std::vector<T>* val) const;
     template<typename T>
     status_t            resizeOutVector(std::optional<std::vector<T>>* val) const;
     template<typename T>
     status_t            resizeOutVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead")));
-    template<typename T>
-    status_t            reserveOutVector(std::vector<T>* val, size_t* size) const;
-    template<typename T>
-    status_t            reserveOutVector(std::optional<std::vector<T>>* val,
-                                         size_t* size) const;
-    template<typename T>
-    status_t            reserveOutVector(std::unique_ptr<std::vector<T>>* val,
-                                         size_t* size) const __attribute__((deprecated("use std::optional version instead")));
 
     // Like Parcel.java's readExceptionCode().  Reads the first int32
     // off of a Parcel's header, returning 0 or the negative error
@@ -518,10 +537,6 @@
     void                scanForFds() const;
     status_t            validateReadData(size_t len) const;
 
-    // Reads an int32 size and does a coarse bounds check against the number
-    // of available bytes in the Parcel.
-    status_t            readVectorSizeWithCoarseBoundCheck(int32_t *size) const;
-
     void                updateWorkSourceRequestHeaderPosition() const;
 
     status_t            finishFlattenBinder(const sp<IBinder>& binder);
@@ -540,53 +555,544 @@
     status_t            writeRawNullableParcelable(const Parcelable*
                                                    parcelable);
 
-    template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool> = 0>
-    status_t            writeEnum(const T& val);
-    template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int64_t>, bool> = 0>
-    status_t            writeEnum(const T& val);
+    //-----------------------------------------------------------------------------
+    // Generic type read and write methods for Parcel:
+    //
+    // readData(T *value) will read a value from the Parcel.
+    // writeData(const T& value) will write a value to the Parcel.
+    //
+    // Our approach to parceling is based on two overloaded functions
+    // readData() and writeData() that generate parceling code for an
+    // object automatically based on its type. The code from templates are generated at
+    // compile time (if constexpr), and decomposes an object through a call graph matching
+    // recursive descent of the template typename.
+    //
+    // This approach unifies handling of complex objects,
+    // resulting in fewer lines of code, greater consistency,
+    // extensibility to nested types, efficiency (decisions made at compile time),
+    // and better code maintainability and optimization.
+    //
+    // Design decision: Incorporate the read and write code into Parcel rather than
+    // as a non-intrusive serializer that emits a byte stream, as we have
+    // active objects, alignment, legacy code, and historical idiosyncrasies.
+    //
+    // --- Overview
+    //
+    // Parceling is a way of serializing objects into a sequence of bytes for communication
+    // between processes, as part of marshaling data for remote procedure calls.
+    //
+    // The Parcel instance contains objects serialized as bytes, such as the following:
+    //
+    // 1) Ordinary primitive data such as int, float.
+    // 2) Established structured data such as String16, std::string.
+    // 3) Parcelables, which are C++ objects that derive from Parcelable (and thus have a
+    //    readFromParcel and writeToParcel method).  (Similar for Java)
+    // 4) A std::vector<> of such data.
+    // 5) Nullable objects contained in std::optional, std::unique_ptr, or std::shared_ptr.
+    //
+    // And active objects from the Android ecosystem such as:
+    // 6) File descriptors, base::unique_fd (kernel object handles)
+    // 7) Binder objects, sp<IBinder> (active Android RPC handles)
+    //
+    // Objects from (1) through (5) serialize into the mData buffer.
+    // Active objects (6) and (7) serialize into both mData and mObjects buffers.
+    //
+    // --- Data layout details
+    //
+    // Data is read or written to the parcel by recursively decomposing the type of the parameter
+    // type T through readData() and writeData() methods.
+    //
+    // We focus on writeData() here in our explanation of the data layout.
+    //
+    // 1) Alignment
+    // Implementation detail: Regardless of the parameter type, writeData() calls are designed
+    // to finish at a multiple of 4 bytes, the default alignment of the Parcel.
+    //
+    // Writes of single uint8_t, int8_t, enums based on types of size 1, char16_t, etc
+    // will result in 4 bytes being written.  The data is widened to int32 and then written;
+    // hence the position of the nonzero bytes depend on the native endianness of the CPU.
+    //
+    // Writes of primitive values with 8 byte size, double, int64_t, uint64_t,
+    // are stored with 4 byte alignment.  The ARM and x86/x64 permit unaligned reads
+    // and writes (albeit with potential latency/throughput penalty) which may or may
+    // not be observable unless the process is IO bound.
+    //
+    // 2) Parcelables
+    // Parcelables are detected by the type's base class, and implemented through calling
+    // into the Parcelable type's readFromParcel() or writeToParcel() methods.
+    // Historically, due to null object detection, a (int32_t) 1 is prepended to the data written.
+    // Parcelables must have a default constructor (i.e. one that takes no arguments).
+    //
+    // 3) Arrays
+    // Arrays of uint8_t and int8_t, and enums based on size 1 are written as
+    // a contiguous packed byte stream.  Hidden zero padding is applied at the end of the byte
+    // stream to make a multiple of 4 bytes (and prevent info leakage when writing).
+    //
+    // All other array writes can be conceptually thought of as recursively calling
+    // writeData on the individual elements (though may be implemented differently for speed).
+    // As discussed in (1), alignment rules are therefore applied for each element
+    // write (not as an aggregate whole), so the wire representation of data can be
+    // substantially larger.
+    //
+    // Historical Note:
+    // Because of element-wise alignment, CharVector and BoolVector are expanded
+    // element-wise into integers even though they could have been optimized to be packed
+    // just like uint8_t, int8_t (size 1 data).
+    //
+    // 3.1) Arrays accessed by the std::vector type.  This is the default for AIDL.
+    //
+    // 4) Nullables
+    // std::optional, std::unique_ptr, std::shared_ptr are all parceled identically
+    // (i.e. result in identical byte layout).
+    // The target of the std::optional, std::unique_ptr, or std::shared_ptr
+    // can either be a std::vector, String16, std::string, or a Parcelable.
+    //
+    // Detection of null relies on peeking the first int32 data and checking if the
+    // the peeked value is considered invalid for the object:
+    // (-1 for vectors, String16, std::string) (0 for Parcelables).  If the peeked value
+    // is invalid, then a null is returned.
+    //
+    // Application Note: When to use each nullable type:
+    //
+    // std::optional: Embeds the object T by value rather than creating a new instance
+    // by managed pointer as std::unique_ptr or std::shared_ptr.  This will save a malloc
+    // when creating an optional instance.
+    //
+    // Use of std::optionals by value can result in copies of the underlying value stored in it,
+    // so a std::move may be used to move in and move out (for example) a vector value into
+    // the std::optional or for the std::optional itself.
+    //
+    // std::unique_ptr, std::shared_ptr: These are preferred when the lifetime of the object is
+    // already managed by the application.  This reduces unnecessary copying of data
+    // especially when the calls are local in-proc (rather than via binder rpc).
+    //
+    // 5) StrongBinder (sp<IBinder>)
+    // StrongBinder objects are written regardless of null. When read, null StrongBinder values
+    // will be interpreted as UNKNOWN_ERROR if the type is a single argument <sp<T>>
+    // or in a vector argument <std::vector<sp<T>>. However, they will be read without an error
+    // if present in a std::optional, std::unique_ptr, or std::shared_ptr vector, e.g.
+    // <std::optional<std::vector<sp<T>>>.
+    //
+    // See AIDL annotation @Nullable, readStrongBinder(), and readNullableStrongBinder().
+    //
+    // Historical Note: writing a vector of StrongBinder objects <std::vector<sp<T>>
+    // containing a null will not cause an error. However reading such a vector will cause
+    // an error _and_ early termination of the read.
 
-    template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool> = 0>
-    status_t            readEnum(T* pArg) const;
-    template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int64_t>, bool> = 0>
-    status_t            readEnum(T* pArg) const;
+    //  --- Examples
+    //
+    // Using recursive parceling, we can parcel complex data types so long
+    // as they obey the rules described above.
+    //
+    // Example #1
+    // Parceling of a 3D vector
+    //
+    // std::vector<std::vector<std::vector<int32_t>>> v1 {
+    //     { {1}, {2, 3}, {4} },
+    //     {},
+    //     { {10}, {20}, {30, 40} },
+    // };
+    // Parcel p1;
+    // p1.writeData(v1);
+    // decltype(v1) v2;
+    // p1.setDataPosition(0);
+    // p1.readData(&v2);
+    // ASSERT_EQ(v1, v2);
+    //
+    // Example #2
+    // Parceling of mixed shared pointers
+    //
+    // Parcel p1;
+    // auto sp1 = std::make_shared<std::vector<std::shared_ptr<std::vector<int>>>>(3);
+    // (*sp1)[2] = std::make_shared<std::vector<int>>(3);
+    // (*(*sp1)[2])[2] = 2;
+    // p1.writeData(sp1);
+    // decltype(sp1) sp2;
+    // p1.setDataPosition(0);
+    // p1.readData(&sp2);
+    // ASSERT_EQ((*sp1)[0], (*sp2)[0]); // nullptr
+    // ASSERT_EQ((*sp1)[1], (*sp2)[1]); // nullptr
+    // ASSERT_EQ(*(*sp1)[2], *(*sp2)[2]); // { 0, 0, 2}
 
-    status_t writeByteVectorInternal(const int8_t* data, size_t size);
-    template<typename T>
-    status_t readByteVectorInternal(std::vector<T>* val, size_t size) const;
+    //  --- Helper Methods
+    // TODO: move this to a utils header.
+    //
+    // Determine if a type is a specialization of a templated type
+    // Example: is_specialization_v<T, std::vector>
 
-    template<typename T, typename U>
-    status_t            unsafeReadTypedVector(std::vector<T>* val,
-                                              status_t(Parcel::*read_func)(U*) const) const;
-    template<typename T>
-    status_t            readNullableTypedVector(std::optional<std::vector<T>>* val,
-                                                status_t(Parcel::*read_func)(T*) const) const;
-    template<typename T>
-    status_t            readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
-                                                status_t(Parcel::*read_func)(T*) const) const __attribute__((deprecated("use std::optional version instead")));
-    template<typename T>
-    status_t            readTypedVector(std::vector<T>* val,
-                                        status_t(Parcel::*read_func)(T*) const) const;
-    template<typename T, typename U>
-    status_t            unsafeWriteTypedVector(const std::vector<T>& val,
-                                               status_t(Parcel::*write_func)(U));
-    template<typename T>
-    status_t            writeNullableTypedVector(const std::optional<std::vector<T>>& val,
-                                                 status_t(Parcel::*write_func)(const T&));
-    template<typename T>
-    status_t            writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
-                                                 status_t(Parcel::*write_func)(const T&)) __attribute__((deprecated("use std::optional version instead")));
-    template<typename T>
-    status_t            writeNullableTypedVector(const std::optional<std::vector<T>>& val,
-                                                 status_t(Parcel::*write_func)(T));
-    template<typename T>
-    status_t            writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
-                                                 status_t(Parcel::*write_func)(T)) __attribute__((deprecated("use std::optional version instead")));
-    template<typename T>
-    status_t            writeTypedVector(const std::vector<T>& val,
-                                         status_t(Parcel::*write_func)(const T&));
-    template<typename T>
-    status_t            writeTypedVector(const std::vector<T>& val,
-                                         status_t(Parcel::*write_func)(T));
+    template <typename Test, template <typename...> class Ref>
+    struct is_specialization : std::false_type {};
+
+    template <template <typename...> class Ref, typename... Args>
+    struct is_specialization<Ref<Args...>, Ref>: std::true_type {};
+
+    template <typename Test, template <typename...> class Ref>
+    static inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value;
+
+    // Get the first template type from a container, the T from MyClass<T, ...>.
+    template<typename T> struct first_template_type;
+
+    template <template <typename ...> class V, typename T, typename... Args>
+    struct first_template_type<V<T, Args...>> {
+        using type_t = T;
+    };
+
+    template <typename T>
+    using first_template_type_t = typename first_template_type<T>::type_t;
+
+    // For static assert(false) we need a template version to avoid early failure.
+    template <typename T>
+    static inline constexpr bool dependent_false_v = false;
+
+    // primitive types that we consider packed and trivially copyable as an array
+    template <typename T>
+    static inline constexpr bool is_pointer_equivalent_array_v =
+            std::is_same_v<T, int8_t>
+            || std::is_same_v<T, uint8_t>
+            // We could support int16_t and uint16_t, but those aren't currently AIDL types.
+            || std::is_same_v<T, int32_t>
+            || std::is_same_v<T, uint32_t>
+            || std::is_same_v<T, float>
+            // are unaligned reads and write support is assumed.
+            || std::is_same_v<T, uint64_t>
+            || std::is_same_v<T, int64_t>
+            || std::is_same_v<T, double>
+            || (std::is_enum_v<T> && (sizeof(T) == 1 || sizeof(T) == 4)); // size check not type
+
+    // allowed "nullable" types
+    // These are nonintrusive containers std::optional, std::unique_ptr, std::shared_ptr.
+    template <typename T>
+    static inline constexpr bool is_parcel_nullable_type_v =
+            is_specialization_v<T, std::optional>
+            || is_specialization_v<T, std::unique_ptr>
+            || is_specialization_v<T, std::shared_ptr>;
+
+    // special int32 value to indicate NonNull or Null parcelables
+    // This is fixed to be only 0 or 1 by contract, do not change.
+    static constexpr int32_t kNonNullParcelableFlag = 1;
+    static constexpr int32_t kNullParcelableFlag = 0;
+
+    // special int32 size representing a null vector, when applicable in Nullable data.
+    // This fixed as -1 by contract, do not change.
+    static constexpr int32_t kNullVectorSize = -1;
+
+    // --- readData and writeData methods.
+    // We choose a mixture of function and template overloads to improve code readability.
+    // TODO: Consider C++20 concepts when they become available.
+
+    // writeData function overloads.
+    // Implementation detail: Function overloading improves code readability over
+    // template overloading, but prevents writeData<T> from being used for those types.
+
+    status_t writeData(bool t) {
+        return writeBool(t);  // this writes as int32_t
+    }
+
+    status_t writeData(int8_t t) {
+        return writeByte(t);  // this writes as int32_t
+    }
+
+    status_t writeData(uint8_t t) {
+        return writeByte(static_cast<int8_t>(t));  // this writes as int32_t
+    }
+
+    status_t writeData(char16_t t) {
+        return writeChar(t);  // this writes as int32_t
+    }
+
+    status_t writeData(int32_t t) {
+        return writeInt32(t);
+    }
+
+    status_t writeData(uint32_t t) {
+        return writeUint32(t);
+    }
+
+    status_t writeData(int64_t t) {
+        return writeInt64(t);
+    }
+
+    status_t writeData(uint64_t t) {
+        return writeUint64(t);
+    }
+
+    status_t writeData(float t) {
+        return writeFloat(t);
+    }
+
+    status_t writeData(double t) {
+        return writeDouble(t);
+    }
+
+    status_t writeData(const String16& t) {
+        return writeString16(t);
+    }
+
+    status_t writeData(const std::string& t) {
+        return writeUtf8AsUtf16(t);
+    }
+
+    status_t writeData(const base::unique_fd& t) {
+        return writeUniqueFileDescriptor(t);
+    }
+
+    status_t writeData(const Parcelable& t) {  // std::is_base_of_v<Parcelable, T>
+        // implemented here. writeParcelable() calls this.
+        status_t status = writeData(static_cast<int32_t>(kNonNullParcelableFlag));
+        if (status != OK) return status;
+        return t.writeToParcel(this);
+    }
+
+    // writeData<T> template overloads.
+    // Written such that the first template type parameter is the complete type
+    // of the first function parameter.
+    template <typename T,
+            typename std::enable_if_t<std::is_enum_v<T>, bool> = true>
+    status_t writeData(const T& t) {
+        // implemented here. writeEnum() calls this.
+        using UT = std::underlying_type_t<T>;
+        return writeData(static_cast<UT>(t)); // recurse
+    }
+
+    template <typename T,
+            typename std::enable_if_t<is_specialization_v<T, sp>, bool> = true>
+    status_t writeData(const T& t) {
+        return writeStrongBinder(t);
+    }
+
+    // std::optional, std::unique_ptr, std::shared_ptr special case.
+    template <typename CT,
+            typename std::enable_if_t<is_parcel_nullable_type_v<CT>, bool> = true>
+    status_t writeData(const CT& c) {
+        using T = first_template_type_t<CT>;  // The T in CT == C<T, ...>
+        if constexpr (is_specialization_v<T, std::vector>
+                || std::is_same_v<T, String16>
+                || std::is_same_v<T, std::string>) {
+            if (!c) return writeData(static_cast<int32_t>(kNullVectorSize));
+        } else if constexpr (std::is_base_of_v<Parcelable, T>) {
+            if (!c) return writeData(static_cast<int32_t>(kNullParcelableFlag));
+        } else /* constexpr */ {  // could define this, but raise as error.
+            static_assert(dependent_false_v<CT>);
+        }
+        return writeData(*c);
+    }
+
+    template <typename CT,
+            typename std::enable_if_t<is_specialization_v<CT, std::vector>, bool> = true>
+    status_t writeData(const CT& c) {
+        using T = first_template_type_t<CT>;  // The T in CT == C<T, ...>
+        if (c.size() >  std::numeric_limits<int32_t>::max()) return BAD_VALUE;
+        const auto size = static_cast<int32_t>(c.size());
+        writeData(size);
+        if constexpr (is_pointer_equivalent_array_v<T>) {
+            constexpr size_t limit = std::numeric_limits<size_t>::max() / sizeof(T);
+            if (c.size() > limit) return BAD_VALUE;
+            // is_pointer_equivalent types do not have gaps which could leak info,
+            // which is only a concern when writing through binder.
+
+            // TODO: Padding of the write is suboptimal when the length of the
+            // data is not a multiple of 4.  Consider improving the write() method.
+            return write(c.data(), c.size() * sizeof(T));
+        } else if constexpr (std::is_same_v<T, bool>
+                || std::is_same_v<T, char16_t>) {
+            // reserve data space to write to
+            auto data = reinterpret_cast<int32_t*>(writeInplace(c.size() * sizeof(int32_t)));
+            if (data == nullptr) return BAD_VALUE;
+            for (const auto t: c) {
+                *data++ = static_cast<int32_t>(t);
+            }
+        } else /* constexpr */ {
+            for (const auto &t : c) {
+                const status_t status = writeData(t);
+                if (status != OK) return status;
+            }
+        }
+        return OK;
+    }
+
+    // readData function overloads.
+    // Implementation detail: Function overloading improves code readability over
+    // template overloading, but prevents readData<T> from being used for those types.
+
+    status_t readData(bool* t) const {
+        return readBool(t);  // this reads as int32_t
+    }
+
+    status_t readData(int8_t* t) const {
+        return readByte(t);  // this reads as int32_t
+    }
+
+    status_t readData(uint8_t* t) const {
+        return readByte(reinterpret_cast<int8_t*>(t));  // NOTE: this reads as int32_t
+    }
+
+    status_t readData(char16_t* t) const {
+        return readChar(t);  // this reads as int32_t
+    }
+
+    status_t readData(int32_t* t) const {
+        return readInt32(t);
+    }
+
+    status_t readData(uint32_t* t) const {
+        return readUint32(t);
+    }
+
+    status_t readData(int64_t* t) const {
+        return readInt64(t);
+    }
+
+    status_t readData(uint64_t* t) const {
+        return readUint64(t);
+    }
+
+    status_t readData(float* t) const {
+        return readFloat(t);
+    }
+
+    status_t readData(double* t) const {
+        return readDouble(t);
+    }
+
+    status_t readData(String16* t) const {
+        return readString16(t);
+    }
+
+    status_t readData(std::string* t) const {
+        return readUtf8FromUtf16(t);
+    }
+
+    status_t readData(base::unique_fd* t) const {
+        return readUniqueFileDescriptor(t);
+    }
+
+    status_t readData(Parcelable* t) const { // std::is_base_of_v<Parcelable, T>
+        // implemented here. readParcelable() calls this.
+        int32_t present;
+        status_t status = readData(&present);
+        if (status != OK) return status;
+        if (present != kNonNullParcelableFlag) return UNEXPECTED_NULL;
+        return t->readFromParcel(this);
+    }
+
+    // readData<T> template overloads.
+    // Written such that the first template type parameter is the complete type
+    // of the first function parameter.
+
+    template <typename T,
+            typename std::enable_if_t<std::is_enum_v<T>, bool> = true>
+    status_t readData(T* t) const {
+        // implemented here. readEnum() calls this.
+        using UT = std::underlying_type_t<T>;
+        return readData(reinterpret_cast<UT*>(t));
+    }
+
+    template <typename T,
+            typename std::enable_if_t<is_specialization_v<T, sp>, bool> = true>
+    status_t readData(T* t) const {
+        return readStrongBinder(t);  // Note: on null, returns failure
+    }
+
+
+    template <typename CT,
+            typename std::enable_if_t<is_parcel_nullable_type_v<CT>, bool> = true>
+    status_t readData(CT* c) const {
+        using T = first_template_type_t<CT>;  // The T in CT == C<T, ...>
+        const size_t startPos = dataPosition();
+        int32_t peek;
+        status_t status = readData(&peek);
+        if (status != OK) return status;
+        if constexpr (is_specialization_v<T, std::vector>
+                || std::is_same_v<T, String16>
+                || std::is_same_v<T, std::string>) {
+            if (peek == kNullVectorSize) {
+                c->reset();
+                return OK;
+            }
+        } else if constexpr (std::is_base_of_v<Parcelable, T>) {
+            if (peek == kNullParcelableFlag) {
+                c->reset();
+                return OK;
+            }
+        } else /* constexpr */ {  // could define this, but raise as error.
+            static_assert(dependent_false_v<CT>);
+        }
+        // create a new object.
+        if constexpr (is_specialization_v<CT, std::optional>) {
+            c->emplace();
+        } else /* constexpr */ {
+            T* const t = new (std::nothrow) T;  // contents read from Parcel below.
+            if (t == nullptr) return NO_MEMORY;
+            c->reset(t);
+        }
+        // rewind data ptr to reread (this is pretty quick), otherwise we could
+        // pass an optional argument to readData to indicate a peeked value.
+        setDataPosition(startPos);
+        if constexpr (is_specialization_v<T, std::vector>) {
+            return readData(&**c, READ_FLAG_SP_NULLABLE);  // nullable sp<> allowed now
+        } else {
+            return readData(&**c);
+        }
+    }
+
+    // std::vector special case, incorporating flags whether the vector
+    // accepts nullable sp<> to be read.
+    enum ReadFlags {
+        READ_FLAG_NONE = 0,
+        READ_FLAG_SP_NULLABLE = 1 << 0,
+    };
+
+    template <typename CT,
+            typename std::enable_if_t<is_specialization_v<CT, std::vector>, bool> = true>
+    status_t readData(CT* c, ReadFlags readFlags = READ_FLAG_NONE) const {
+        using T = first_template_type_t<CT>;  // The T in CT == C<T, ...>
+        int32_t size;
+        status_t status = readInt32(&size);
+        if (status != OK) return status;
+        if (size < 0) return UNEXPECTED_NULL;
+        const size_t availableBytes = dataAvail();  // coarse bound on vector size.
+        if (static_cast<size_t>(size) > availableBytes) return BAD_VALUE;
+        c->clear(); // must clear before resizing/reserving otherwise move ctors may be called.
+        if constexpr (is_pointer_equivalent_array_v<T>) {
+            // could consider POD without gaps and alignment of 4.
+            auto data = reinterpret_cast<const T*>(
+                    readInplace(static_cast<size_t>(size) * sizeof(T)));
+            if (data == nullptr) return BAD_VALUE;
+            c->insert(c->begin(), data, data + size); // insert should do a reserve().
+        } else if constexpr (std::is_same_v<T, bool>
+                || std::is_same_v<T, char16_t>) {
+            c->reserve(size); // avoids default initialization
+            auto data = reinterpret_cast<const int32_t*>(
+                    readInplace(static_cast<size_t>(size) * sizeof(int32_t)));
+            if (data == nullptr) return BAD_VALUE;
+            for (int32_t i = 0; i < size; ++i) {
+                c->emplace_back(static_cast<T>(*data++));
+            }
+        } else if constexpr (is_specialization_v<T, sp>) {
+            c->resize(size); // calls ctor
+            if (readFlags & READ_FLAG_SP_NULLABLE) {
+                for (auto &t : *c) {
+                    status = readNullableStrongBinder(&t);  // allow nullable
+                    if (status != OK) return status;
+                }
+            } else {
+                for (auto &t : *c) {
+                    status = readStrongBinder(&t);
+                    if (status != OK) return status;
+                }
+            }
+        } else /* constexpr */ {
+            c->resize(size); // calls ctor
+            for (auto &t : *c) {
+                status = readData(&t);
+                if (status != OK) return status;
+            }
+        }
+        return OK;
+    }
+
+    //-----------------------------------------------------------------------------
+    private:
 
     status_t            mError;
     uint8_t*            mData;
@@ -792,7 +1298,6 @@
 template<typename T>
 status_t Parcel::resizeOutVector(std::vector<T>* val) const {
     int32_t size;
-    // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here
     status_t err = readInt32(&size);
     if (err != NO_ERROR) {
         return err;
@@ -808,7 +1313,6 @@
 template<typename T>
 status_t Parcel::resizeOutVector(std::optional<std::vector<T>>* val) const {
     int32_t size;
-    // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here
     status_t err = readInt32(&size);
     if (err != NO_ERROR) {
         return err;
@@ -825,7 +1329,6 @@
 template<typename T>
 status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const {
     int32_t size;
-    // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here
     status_t err = readInt32(&size);
     if (err != NO_ERROR) {
         return err;
@@ -840,61 +1343,6 @@
 }
 
 template<typename T>
-status_t Parcel::reserveOutVector(std::vector<T>* val, size_t* size) const {
-    int32_t read_size;
-    status_t err = readVectorSizeWithCoarseBoundCheck(&read_size);
-    if (err != NO_ERROR) {
-        return err;
-    }
-
-    if (read_size < 0) {
-        return UNEXPECTED_NULL;
-    }
-    *size = static_cast<size_t>(read_size);
-    val->reserve(*size);
-    return OK;
-}
-
-template<typename T>
-status_t Parcel::reserveOutVector(std::optional<std::vector<T>>* val, size_t* size) const {
-    int32_t read_size;
-    status_t err = readVectorSizeWithCoarseBoundCheck(&read_size);
-    if (err != NO_ERROR) {
-        return err;
-    }
-
-    if (read_size >= 0) {
-        *size = static_cast<size_t>(read_size);
-        val->emplace();
-        (*val)->reserve(*size);
-    } else {
-        val->reset();
-    }
-
-    return OK;
-}
-
-template<typename T>
-status_t Parcel::reserveOutVector(std::unique_ptr<std::vector<T>>* val,
-                                  size_t* size) const {
-    int32_t read_size;
-    status_t err = readVectorSizeWithCoarseBoundCheck(&read_size);
-    if (err != NO_ERROR) {
-        return err;
-    }
-
-    if (read_size >= 0) {
-        *size = static_cast<size_t>(read_size);
-        val->reset(new std::vector<T>());
-        (*val)->reserve(*size);
-    } else {
-        val->reset();
-    }
-
-    return OK;
-}
-
-template<typename T>
 status_t Parcel::readStrongBinder(sp<T>* val) const {
     sp<IBinder> tmp;
     status_t ret = readStrongBinder(&tmp);
@@ -926,422 +1374,6 @@
     return ret;
 }
 
-template<typename T, typename U>
-status_t Parcel::unsafeReadTypedVector(
-        std::vector<T>* val,
-        status_t(Parcel::*read_func)(U*) const) const {
-    int32_t size;
-    status_t status = this->readVectorSizeWithCoarseBoundCheck(&size);
-
-    if (status != OK) {
-        return status;
-    }
-
-    if (size < 0) {
-        return UNEXPECTED_NULL;
-    }
-
-    if (val->max_size() < static_cast<size_t>(size)) {
-        return NO_MEMORY;
-    }
-
-    val->resize(static_cast<size_t>(size));
-
-    if (val->size() < static_cast<size_t>(size)) {
-        return NO_MEMORY;
-    }
-
-    for (auto& v: *val) {
-        status = (this->*read_func)(&v);
-
-        if (status != OK) {
-            return status;
-        }
-    }
-
-    return OK;
-}
-
-template<typename T>
-status_t Parcel::readTypedVector(std::vector<T>* val,
-                                 status_t(Parcel::*read_func)(T*) const) const {
-    return unsafeReadTypedVector(val, read_func);
-}
-
-template<typename T>
-status_t Parcel::readNullableTypedVector(std::optional<std::vector<T>>* val,
-                                         status_t(Parcel::*read_func)(T*) const) const {
-    const size_t start = dataPosition();
-    int32_t size;
-    status_t status = readVectorSizeWithCoarseBoundCheck(&size);
-    val->reset();
-
-    if (status != OK || size < 0) {
-        return status;
-    }
-
-    setDataPosition(start);
-    val->emplace();
-
-    status = unsafeReadTypedVector(&**val, read_func);
-
-    if (status != OK) {
-       val->reset();
-    }
-
-    return status;
-}
-
-template<typename T>
-status_t Parcel::readNullableTypedVector(std::unique_ptr<std::vector<T>>* val,
-                                         status_t(Parcel::*read_func)(T*) const) const {
-    const size_t start = dataPosition();
-    int32_t size;
-    status_t status = readVectorSizeWithCoarseBoundCheck(&size);
-    val->reset();
-
-    if (status != OK || size < 0) {
-        return status;
-    }
-
-    setDataPosition(start);
-    val->reset(new std::vector<T>());
-
-    status = unsafeReadTypedVector(val->get(), read_func);
-
-    if (status != OK) {
-        val->reset();
-    }
-
-    return status;
-}
-
-template<typename T, typename U>
-status_t Parcel::unsafeWriteTypedVector(const std::vector<T>& val,
-                                        status_t(Parcel::*write_func)(U)) {
-    if (val.size() > std::numeric_limits<int32_t>::max()) {
-        return BAD_VALUE;
-    }
-
-    status_t status = this->writeInt32(static_cast<int32_t>(val.size()));
-
-    if (status != OK) {
-        return status;
-    }
-
-    for (const auto& item : val) {
-        status = (this->*write_func)(item);
-
-        if (status != OK) {
-            return status;
-        }
-    }
-
-    return OK;
-}
-
-template<typename T>
-status_t Parcel::writeTypedVector(const std::vector<T>& val,
-                                  status_t(Parcel::*write_func)(const T&)) {
-    return unsafeWriteTypedVector(val, write_func);
-}
-
-template<typename T>
-status_t Parcel::writeTypedVector(const std::vector<T>& val,
-                                  status_t(Parcel::*write_func)(T)) {
-    return unsafeWriteTypedVector(val, write_func);
-}
-
-template<typename T>
-status_t Parcel::writeNullableTypedVector(const std::optional<std::vector<T>>& val,
-                                          status_t(Parcel::*write_func)(const T&)) {
-    if (!val) {
-        return this->writeInt32(-1);
-    }
-
-    return unsafeWriteTypedVector(*val, write_func);
-}
-
-template<typename T>
-status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
-                                          status_t(Parcel::*write_func)(const T&)) {
-    if (val.get() == nullptr) {
-        return this->writeInt32(-1);
-    }
-
-    return unsafeWriteTypedVector(*val, write_func);
-}
-
-template<typename T>
-status_t Parcel::writeNullableTypedVector(const std::optional<std::vector<T>>& val,
-                                          status_t(Parcel::*write_func)(T)) {
-    if (!val) {
-        return this->writeInt32(-1);
-    }
-
-    return unsafeWriteTypedVector(*val, write_func);
-}
-
-template<typename T>
-status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val,
-                                          status_t(Parcel::*write_func)(T)) {
-    if (val.get() == nullptr) {
-        return this->writeInt32(-1);
-    }
-
-    return unsafeWriteTypedVector(*val, write_func);
-}
-
-template<typename T>
-status_t Parcel::readParcelableVector(std::vector<T>* val) const {
-    return unsafeReadTypedVector<T, Parcelable>(val, &Parcel::readParcelable);
-}
-
-template<typename T>
-status_t Parcel::readParcelableVector(std::optional<std::vector<std::optional<T>>>* val) const {
-    const size_t start = dataPosition();
-    int32_t size;
-    status_t status = readVectorSizeWithCoarseBoundCheck(&size);
-    val->reset();
-
-    if (status != OK || size < 0) {
-        return status;
-    }
-
-    setDataPosition(start);
-    val->emplace();
-
-    using NullableT = std::optional<T>;
-    status = unsafeReadTypedVector<NullableT, NullableT>(&**val, &Parcel::readParcelable);
-
-    if (status != OK) {
-        val->reset();
-    }
-
-    return status;
-}
-
-template<typename T>
-status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const {
-    const size_t start = dataPosition();
-    int32_t size;
-    status_t status = readVectorSizeWithCoarseBoundCheck(&size);
-    val->reset();
-
-    if (status != OK || size < 0) {
-        return status;
-    }
-
-    setDataPosition(start);
-    val->reset(new std::vector<std::unique_ptr<T>>());
-
-    using NullableT = std::unique_ptr<T>;
-    status = unsafeReadTypedVector<NullableT, NullableT>(val->get(), &Parcel::readParcelable);
-
-    if (status != OK) {
-        val->reset();
-    }
-
-    return status;
-}
-
-template<typename T>
-status_t Parcel::readParcelable(std::optional<T>* parcelable) const {
-    const size_t start = dataPosition();
-    int32_t present;
-    status_t status = readInt32(&present);
-    parcelable->reset();
-
-    if (status != OK || !present) {
-        return status;
-    }
-
-    setDataPosition(start);
-    parcelable->emplace();
-
-    status = readParcelable(&**parcelable);
-
-    if (status != OK) {
-        parcelable->reset();
-    }
-
-    return status;
-}
-
-template<typename T>
-status_t Parcel::readParcelable(std::unique_ptr<T>* parcelable) const {
-    const size_t start = dataPosition();
-    int32_t present;
-    status_t status = readInt32(&present);
-    parcelable->reset();
-
-    if (status != OK || !present) {
-        return status;
-    }
-
-    setDataPosition(start);
-    parcelable->reset(new T());
-
-    status = readParcelable(parcelable->get());
-
-    if (status != OK) {
-        parcelable->reset();
-    }
-
-    return status;
-}
-
-template<typename T>
-status_t Parcel::writeNullableParcelable(const std::optional<T>& parcelable) {
-    return writeRawNullableParcelable(parcelable ? &*parcelable : nullptr);
-}
-
-template<typename T>
-status_t Parcel::writeNullableParcelable(const std::unique_ptr<T>& parcelable) {
-    return writeRawNullableParcelable(parcelable.get());
-}
-
-template<typename T>
-status_t Parcel::writeParcelableVector(const std::vector<T>& val) {
-    return unsafeWriteTypedVector<T,const Parcelable&>(val, &Parcel::writeParcelable);
-}
-
-template<typename T>
-status_t Parcel::writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val) {
-    if (!val) {
-        return this->writeInt32(-1);
-    }
-
-    using NullableT = std::optional<T>;
-    return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable);
-}
-
-template<typename T>
-status_t Parcel::writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) {
-    if (val.get() == nullptr) {
-        return this->writeInt32(-1);
-    }
-
-    return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable<T>);
-}
-
-template<typename T>
-status_t Parcel::writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) {
-    if (val.get() == nullptr) {
-        return this->writeInt32(-1);
-    }
-
-    using NullableT = std::unique_ptr<T>;
-    return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable);
-}
-
-template<typename T>
-status_t Parcel::writeParcelableVector(const std::shared_ptr<std::vector<std::optional<T>>>& val) {
-    if (val.get() == nullptr) {
-        return this->writeInt32(-1);
-    }
-
-    using NullableT = std::optional<T>;
-    return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable);
-}
-
-template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool>>
-status_t Parcel::writeEnum(const T& val) {
-    return writeInt32(static_cast<int32_t>(val));
-}
-template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int64_t>, bool>>
-status_t Parcel::writeEnum(const T& val) {
-    return writeInt64(static_cast<int64_t>(val));
-}
-
-template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
-status_t Parcel::writeEnumVector(const std::vector<T>& val) {
-    return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val.data()), val.size());
-}
-template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
-status_t Parcel::writeEnumVector(const std::optional<std::vector<T>>& val) {
-    if (!val) return writeInt32(-1);
-    return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size());
-}
-template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
-status_t Parcel::writeEnumVector(const std::unique_ptr<std::vector<T>>& val) {
-    if (!val) return writeInt32(-1);
-    return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size());
-}
-template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
-status_t Parcel::writeEnumVector(const std::vector<T>& val) {
-    return writeTypedVector(val, &Parcel::writeEnum);
-}
-template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
-status_t Parcel::writeEnumVector(const std::optional<std::vector<T>>& val) {
-    return writeNullableTypedVector(val, &Parcel::writeEnum);
-}
-template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
-status_t Parcel::writeEnumVector(const std::unique_ptr<std::vector<T>>& val) {
-    return writeNullableTypedVector(val, &Parcel::writeEnum);
-}
-
-template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool>>
-status_t Parcel::readEnum(T* pArg) const {
-    return readInt32(reinterpret_cast<int32_t *>(pArg));
-}
-template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int64_t>, bool>>
-status_t Parcel::readEnum(T* pArg) const {
-    return readInt64(reinterpret_cast<int64_t *>(pArg));
-}
-
-template<typename T>
-inline status_t Parcel::readByteVectorInternal(std::vector<T>* val, size_t size) const {
-  // readByteVectorInternal expects a vector that has been reserved (but not
-  // resized) to have the provided size.
-  const T* data = reinterpret_cast<const T*>(readInplace(size));
-  if (!data) return BAD_VALUE;
-  val->clear();
-  val->insert(val->begin(), data, data+size);
-  return NO_ERROR;
-}
-
-template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
-status_t Parcel::readEnumVector(std::vector<T>* val) const {
-    size_t size;
-    if (status_t status = reserveOutVector(val, &size); status != OK) return status;
-    return readByteVectorInternal(val, size);
-}
-template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
-status_t Parcel::readEnumVector(std::optional<std::vector<T>>* val) const {
-    size_t size;
-    if (status_t status = reserveOutVector(val, &size); status != OK) return status;
-    if (!*val) {
-        // reserveOutVector does not create the out vector if size is < 0.
-        // This occurs when writing a null Enum vector.
-        return OK;
-    }
-    return readByteVectorInternal(&**val, size);
-}
-template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
-status_t Parcel::readEnumVector(std::unique_ptr<std::vector<T>>* val) const {
-    size_t size;
-    if (status_t status = reserveOutVector(val, &size); status != OK) return status;
-    if (val->get() == nullptr) {
-        // reserveOutVector does not create the out vector if size is < 0.
-        // This occurs when writing a null Enum vector.
-        return OK;
-    }
-    return readByteVectorInternal(val->get(), size);
-}
-template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
-status_t Parcel::readEnumVector(std::vector<T>* val) const {
-    return readTypedVector(val, &Parcel::readEnum);
-}
-template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
-status_t Parcel::readEnumVector(std::optional<std::vector<T>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readEnum);
-}
-template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>>
-status_t Parcel::readEnumVector(std::unique_ptr<std::vector<T>>* val) const {
-    return readNullableTypedVector(val, &Parcel::readEnum);
-}
-
 // ---------------------------------------------------------------------------
 
 inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel)
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 42c1e0a..d53a88f 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -16,12 +16,17 @@
 
 //! Trait definitions for binder objects
 
-use crate::error::{status_t, Result};
+use crate::error::{status_t, Result, StatusCode};
 use crate::parcel::Parcel;
-use crate::proxy::{DeathRecipient, SpIBinder};
+use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder};
 use crate::sys;
 
+use std::borrow::Borrow;
+use std::cmp::Ordering;
 use std::ffi::{c_void, CStr, CString};
+use std::fmt;
+use std::marker::PhantomData;
+use std::ops::Deref;
 use std::os::raw::c_char;
 use std::os::unix::io::AsRawFd;
 use std::ptr;
@@ -44,7 +49,7 @@
 /// interfaces) must implement this trait.
 ///
 /// This is equivalent `IInterface` in C++.
-pub trait Interface {
+pub trait Interface: Send {
     /// Convert this binder object into a generic [`SpIBinder`] reference.
     fn as_binder(&self) -> SpIBinder {
         panic!("This object was not a Binder object and cannot be converted into an SpIBinder.")
@@ -230,6 +235,132 @@
     }
 }
 
+/// Strong reference to a binder object
+pub struct Strong<I: FromIBinder + ?Sized>(Box<I>);
+
+impl<I: FromIBinder + ?Sized> Strong<I> {
+    /// Create a new strong reference to the provided binder object
+    pub fn new(binder: Box<I>) -> Self {
+        Self(binder)
+    }
+
+    /// Construct a new weak reference to this binder
+    pub fn downgrade(this: &Strong<I>) -> Weak<I> {
+        Weak::new(this)
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Clone for Strong<I> {
+    fn clone(&self) -> Self {
+        // Since we hold a strong reference, we should always be able to create
+        // a new strong reference to the same interface type, so try_from()
+        // should never fail here.
+        FromIBinder::try_from(self.0.as_binder()).unwrap()
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Borrow<I> for Strong<I> {
+    fn borrow(&self) -> &I {
+        &self.0
+    }
+}
+
+impl<I: FromIBinder + ?Sized> AsRef<I> for Strong<I> {
+    fn as_ref(&self) -> &I {
+        &self.0
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Deref for Strong<I> {
+    type Target = I;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<I: FromIBinder + fmt::Debug + ?Sized> fmt::Debug for Strong<I> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Debug::fmt(&**self, f)
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Ord for Strong<I> {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.0.as_binder().cmp(&other.0.as_binder())
+    }
+}
+
+impl<I: FromIBinder + ?Sized> PartialOrd for Strong<I> {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        self.0.as_binder().partial_cmp(&other.0.as_binder())
+    }
+}
+
+impl<I: FromIBinder + ?Sized> PartialEq for Strong<I> {
+    fn eq(&self, other: &Self) -> bool {
+        self.0.as_binder().eq(&other.0.as_binder())
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Eq for Strong<I> {}
+
+/// Weak reference to a binder object
+#[derive(Debug)]
+pub struct Weak<I: FromIBinder + ?Sized> {
+    weak_binder: WpIBinder,
+    interface_type: PhantomData<I>,
+}
+
+impl<I: FromIBinder + ?Sized> Weak<I> {
+    /// Construct a new weak reference from a strong reference
+    fn new(binder: &Strong<I>) -> Self {
+        let weak_binder = binder.as_binder().downgrade();
+        Weak {
+            weak_binder,
+            interface_type: PhantomData,
+        }
+    }
+
+    /// Upgrade this weak reference to a strong reference if the binder object
+    /// is still alive
+    pub fn upgrade(&self) -> Result<Strong<I>> {
+        self.weak_binder
+            .promote()
+            .ok_or(StatusCode::DEAD_OBJECT)
+            .and_then(FromIBinder::try_from)
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Clone for Weak<I> {
+    fn clone(&self) -> Self {
+        Self {
+            weak_binder: self.weak_binder.clone(),
+            interface_type: PhantomData,
+        }
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Ord for Weak<I> {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.weak_binder.cmp(&other.weak_binder)
+    }
+}
+
+impl<I: FromIBinder + ?Sized> PartialOrd for Weak<I> {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        self.weak_binder.partial_cmp(&other.weak_binder)
+    }
+}
+
+impl<I: FromIBinder + ?Sized> PartialEq for Weak<I> {
+    fn eq(&self, other: &Self) -> bool {
+        self.weak_binder == other.weak_binder
+    }
+}
+
+impl<I: FromIBinder + ?Sized> Eq for Weak<I> {}
+
 /// Create a function implementing a static getter for an interface class.
 ///
 /// Each binder interface (i.e. local [`Remotable`] service or remote proxy
@@ -354,12 +485,12 @@
 ///     }
 /// }
 /// ```
-pub trait FromIBinder {
+pub trait FromIBinder: Interface {
     /// Try to interpret a generic Binder object as this interface.
     ///
     /// Returns a trait object for the `Self` interface if this object
     /// implements that interface.
-    fn try_from(ibinder: SpIBinder) -> Result<Box<Self>>;
+    fn try_from(ibinder: SpIBinder) -> Result<Strong<Self>>;
 }
 
 /// Trait for transparent Rust wrappers around android C++ native types.
@@ -534,8 +665,9 @@
 
         impl $native {
             /// Create a new binder service.
-            pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> impl $interface {
-                $crate::Binder::new($native(Box::new(inner)))
+            pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> $crate::Strong<dyn $interface> {
+                let binder = $crate::Binder::new($native(Box::new(inner)));
+                $crate::Strong::new(Box::new(binder))
             }
         }
 
@@ -577,7 +709,7 @@
         }
 
         impl $crate::FromIBinder for dyn $interface {
-            fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<Box<dyn $interface>> {
+            fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $interface>> {
                 use $crate::AssociateClass;
 
                 let existing_class = ibinder.get_class();
@@ -590,7 +722,7 @@
                         // associated object as remote, because we can't cast it
                         // into a Rust service object without a matching class
                         // pointer.
-                        return Ok(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
@@ -600,10 +732,10 @@
                     if let Ok(service) = service {
                         // We were able to associate with our expected class and
                         // the service is local.
-                        return Ok(Box::new(service));
+                        return Ok($crate::Strong::new(Box::new(service)));
                     } else {
                         // Service is remote
-                        return Ok(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?));
+                        return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
                     }
                 }
 
@@ -633,9 +765,9 @@
             }
         }
 
-        // Convert a &dyn $interface to Box<dyn $interface>
+        /// Convert a &dyn $interface to Strong<dyn $interface>
         impl std::borrow::ToOwned for dyn $interface {
-            type Owned = Box<dyn $interface>;
+            type Owned = $crate::Strong<dyn $interface>;
             fn to_owned(&self) -> Self::Owned {
                 self.as_binder().into_interface()
                     .expect(concat!("Error cloning interface ", stringify!($interface)))
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index edfb56a..43a237a 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -107,7 +107,8 @@
 pub mod parcel;
 
 pub use crate::binder::{
-    FromIBinder, IBinder, Interface, InterfaceClass, Remotable, TransactionCode, TransactionFlags,
+    FromIBinder, IBinder, Interface, InterfaceClass, Remotable, Strong, TransactionCode,
+    TransactionFlags, Weak,
 };
 pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
 pub use native::add_service;
@@ -122,7 +123,8 @@
     pub use super::parcel::ParcelFileDescriptor;
     pub use super::{add_service, get_interface};
     pub use super::{
-        ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode, WpIBinder,
+        ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode, Strong, ThreadState,
+        Weak, WpIBinder,
     };
 
     /// Binder result containing a [`Status`] on error.
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 8d18fb4..f57788b 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-use crate::binder::{AsNative, FromIBinder};
+use crate::binder::{AsNative, FromIBinder, Strong};
 use crate::error::{status_result, status_t, Result, Status, StatusCode};
 use crate::parcel::Parcel;
 use crate::proxy::SpIBinder;
@@ -628,26 +628,26 @@
     }
 }
 
-impl<T: Serialize + ?Sized> Serialize for Box<T> {
+impl<T: Serialize + FromIBinder + ?Sized> Serialize for Strong<T> {
     fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
         Serialize::serialize(&**self, parcel)
     }
 }
 
-impl<T: SerializeOption + ?Sized> SerializeOption for Box<T> {
+impl<T: SerializeOption + FromIBinder + ?Sized> SerializeOption for Strong<T> {
     fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
         SerializeOption::serialize_option(this.map(|b| &**b), parcel)
     }
 }
 
-impl<T: FromIBinder + ?Sized> Deserialize for Box<T> {
+impl<T: FromIBinder + ?Sized> Deserialize for Strong<T> {
     fn deserialize(parcel: &Parcel) -> Result<Self> {
         let ibinder: SpIBinder = parcel.read()?;
         FromIBinder::try_from(ibinder)
     }
 }
 
-impl<T: FromIBinder + ?Sized> DeserializeOption for Box<T> {
+impl<T: FromIBinder + ?Sized> DeserializeOption for Strong<T> {
     fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
         let ibinder: Option<SpIBinder> = parcel.read()?;
         ibinder.map(FromIBinder::try_from).transpose()
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index e9e74c0..132e075 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -17,7 +17,7 @@
 //! Rust API for interacting with a remote binder service.
 
 use crate::binder::{
-    AsNative, FromIBinder, IBinder, Interface, InterfaceClass, TransactionCode, TransactionFlags,
+    AsNative, FromIBinder, IBinder, Interface, InterfaceClass, Strong, TransactionCode, TransactionFlags,
 };
 use crate::error::{status_result, Result, StatusCode};
 use crate::parcel::{
@@ -27,6 +27,7 @@
 use crate::sys;
 
 use std::convert::TryInto;
+use std::cmp::Ordering;
 use std::ffi::{c_void, CString};
 use std::fmt;
 use std::os::unix::io::AsRawFd;
@@ -99,7 +100,7 @@
     ///
     /// If this object does not implement the expected interface, the error
     /// `StatusCode::BAD_TYPE` is returned.
-    pub fn into_interface<I: FromIBinder + ?Sized>(self) -> Result<Box<I>> {
+    pub fn into_interface<I: FromIBinder + Interface + ?Sized>(self) -> Result<Strong<I>> {
         FromIBinder::try_from(self)
     }
 
@@ -148,6 +149,36 @@
     }
 }
 
+impl Ord for SpIBinder {
+    fn cmp(&self, other: &Self) -> Ordering {
+        let less_than = unsafe {
+            // Safety: SpIBinder always holds a valid `AIBinder` pointer, so
+            // this pointer is always safe to pass to `AIBinder_lt` (null is
+            // also safe to pass to this function, but we should never do that).
+            sys::AIBinder_lt(self.0, other.0)
+        };
+        let greater_than = unsafe {
+            // Safety: SpIBinder always holds a valid `AIBinder` pointer, so
+            // this pointer is always safe to pass to `AIBinder_lt` (null is
+            // also safe to pass to this function, but we should never do that).
+            sys::AIBinder_lt(other.0, self.0)
+        };
+        if !less_than && !greater_than {
+            Ordering::Equal
+        } else if less_than {
+            Ordering::Less
+        } else {
+            Ordering::Greater
+        }
+    }
+}
+
+impl PartialOrd for SpIBinder {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
 impl PartialEq for SpIBinder {
     fn eq(&self, other: &Self) -> bool {
         ptr::eq(self.0, other.0)
@@ -326,7 +357,7 @@
             // Safety: `SpIBinder` guarantees that `self` always contains a
             // valid pointer to an `AIBinder`. `recipient` can always be
             // converted into a valid pointer to an
-            // `AIBinder_DeatRecipient`. Any value is safe to pass as the
+            // `AIBinder_DeathRecipient`. Any value is safe to pass as the
             // cookie, although we depend on this value being set by
             // `get_cookie` when the death recipient callback is called.
             sys::AIBinder_linkToDeath(
@@ -342,7 +373,7 @@
             // Safety: `SpIBinder` guarantees that `self` always contains a
             // valid pointer to an `AIBinder`. `recipient` can always be
             // converted into a valid pointer to an
-            // `AIBinder_DeatRecipient`. Any value is safe to pass as the
+            // `AIBinder_DeathRecipient`. Any value is safe to pass as the
             // cookie, although we depend on this value being set by
             // `get_cookie` when the death recipient callback is called.
             sys::AIBinder_unlinkToDeath(
@@ -430,6 +461,62 @@
     }
 }
 
+impl Clone for WpIBinder {
+    fn clone(&self) -> Self {
+        let ptr = unsafe {
+            // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer,
+            // so this pointer is always safe to pass to `AIBinder_Weak_clone`
+            // (although null is also a safe value to pass to this API).
+            //
+            // We get ownership of the returned pointer, so can construct a new
+            // WpIBinder object from it.
+            sys::AIBinder_Weak_clone(self.0)
+        };
+        assert!(!ptr.is_null(), "Unexpected null pointer from AIBinder_Weak_clone");
+        Self(ptr)
+    }
+}
+
+impl Ord for WpIBinder {
+    fn cmp(&self, other: &Self) -> Ordering {
+        let less_than = unsafe {
+            // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer,
+            // so this pointer is always safe to pass to `AIBinder_Weak_lt`
+            // (null is also safe to pass to this function, but we should never
+            // do that).
+            sys::AIBinder_Weak_lt(self.0, other.0)
+        };
+        let greater_than = unsafe {
+            // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer,
+            // so this pointer is always safe to pass to `AIBinder_Weak_lt`
+            // (null is also safe to pass to this function, but we should never
+            // do that).
+            sys::AIBinder_Weak_lt(other.0, self.0)
+        };
+        if !less_than && !greater_than {
+            Ordering::Equal
+        } else if less_than {
+            Ordering::Less
+        } else {
+            Ordering::Greater
+        }
+    }
+}
+
+impl PartialOrd for WpIBinder {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl PartialEq for WpIBinder {
+    fn eq(&self, other: &Self) -> bool {
+        self.cmp(other) == Ordering::Equal
+    }
+}
+
+impl Eq for WpIBinder {}
+
 impl Drop for WpIBinder {
     fn drop(&mut self) {
         unsafe {
@@ -564,7 +651,7 @@
 
 /// Retrieve an existing service for a particular interface, blocking for a few
 /// seconds if it doesn't yet exist.
-pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Box<T>> {
+pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
     let service = get_service(name);
     match service {
         Some(service) => FromIBinder::try_from(service),
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index bb8c492..719229c 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -209,7 +209,7 @@
     use std::thread;
     use std::time::Duration;
 
-    use binder::{Binder, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode};
+    use binder::{Binder, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode, Strong};
 
     use super::{BnTest, ITest, ITestSameDescriptor, RUST_SERVICE_BINARY, TestService};
 
@@ -271,7 +271,7 @@
     fn trivial_client() {
         let service_name = "trivial_client_test";
         let _process = ScopedServiceProcess::new(service_name);
-        let test_client: Box<dyn ITest> =
+        let test_client: Strong<dyn ITest> =
             binder::get_interface(service_name).expect("Did not get manager binder service");
         assert_eq!(test_client.test().unwrap(), "trivial_client_test");
     }
@@ -280,7 +280,7 @@
     fn get_selinux_context() {
         let service_name = "get_selinux_context";
         let _process = ScopedServiceProcess::new(service_name);
-        let test_client: Box<dyn ITest> =
+        let test_client: Strong<dyn ITest> =
             binder::get_interface(service_name).expect("Did not get manager binder service");
         let expected_context = unsafe {
             let mut out_ptr = ptr::null_mut();
@@ -453,7 +453,7 @@
 
             let extension = maybe_extension.expect("Remote binder did not have an extension");
 
-            let extension: Box<dyn ITest> = FromIBinder::try_from(extension)
+            let extension: Strong<dyn ITest> = FromIBinder::try_from(extension)
                 .expect("Extension could not be converted to the expected interface");
 
             assert_eq!(extension.test().unwrap(), extension_name);
@@ -479,7 +479,7 @@
 
         // This should succeed although we will have to treat the service as
         // remote.
-        let _interface: Box<dyn ITestSameDescriptor> = FromIBinder::try_from(service.as_binder())
+        let _interface: Strong<dyn ITestSameDescriptor> = FromIBinder::try_from(service.as_binder())
             .expect("Could not re-interpret service as the ITestSameDescriptor interface");
     }
 
@@ -490,9 +490,60 @@
         let service_ibinder = BnTest::new_binder(TestService { s: service_name.to_string() })
             .as_binder();
 
-        let service: Box<dyn ITest> = service_ibinder.into_interface()
+        let service: Strong<dyn ITest> = service_ibinder.into_interface()
             .expect("Could not reassociate the generic ibinder");
 
         assert_eq!(service.test().unwrap(), service_name);
     }
+
+    #[test]
+    fn weak_binder_upgrade() {
+        let service_name = "testing_service";
+        let service = BnTest::new_binder(TestService { s: service_name.to_string() });
+
+        let weak = Strong::downgrade(&service);
+
+        let upgraded = weak.upgrade().expect("Could not upgrade weak binder");
+
+        assert_eq!(service, upgraded);
+    }
+
+    #[test]
+    fn weak_binder_upgrade_dead() {
+        let service_name = "testing_service";
+        let weak = {
+            let service = BnTest::new_binder(TestService { s: service_name.to_string() });
+
+            Strong::downgrade(&service)
+        };
+
+        assert_eq!(weak.upgrade(), Err(StatusCode::DEAD_OBJECT));
+    }
+
+    #[test]
+    fn weak_binder_clone() {
+        let service_name = "testing_service";
+        let service = BnTest::new_binder(TestService { s: service_name.to_string() });
+
+        let weak = Strong::downgrade(&service);
+        let cloned = weak.clone();
+        assert_eq!(weak, cloned);
+
+        let upgraded = weak.upgrade().expect("Could not upgrade weak binder");
+        let clone_upgraded = cloned.upgrade().expect("Could not upgrade weak binder");
+
+        assert_eq!(service, upgraded);
+        assert_eq!(service, clone_upgraded);
+    }
+
+    #[test]
+    #[allow(clippy::eq_op)]
+    fn binder_ord() {
+        let service1 = BnTest::new_binder(TestService { s: "testing_service1".to_string() });
+        let service2 = BnTest::new_binder(TestService { s: "testing_service2".to_string() });
+
+        assert!(!(service1 < service1));
+        assert!(!(service1 > service1));
+        assert_eq!(service1 < service2, !(service2 < service1));
+    }
 }
diff --git a/libs/binder/rust/tests/ndk_rust_interop.rs b/libs/binder/rust/tests/ndk_rust_interop.rs
index 70a6dc0..ce75ab7 100644
--- a/libs/binder/rust/tests/ndk_rust_interop.rs
+++ b/libs/binder/rust/tests/ndk_rust_interop.rs
@@ -37,7 +37,7 @@
 
     // The Rust class descriptor pointer will not match the NDK one, but the
     // descriptor strings match so this needs to still associate.
-    let service: Box<dyn IBinderRustNdkInteropTest> = match binder::get_interface(service_name) {
+    let service: binder::Strong<dyn IBinderRustNdkInteropTest> = match binder::get_interface(service_name) {
         Err(e) => {
             eprintln!("Could not find Ndk service {}: {:?}", service_name, e);
             return StatusCode::NAME_NOT_FOUND as c_int;
@@ -53,7 +53,7 @@
     }
 
     // Try using the binder service through the wrong interface type
-    let wrong_service: Result<Box<dyn IBinderRustNdkInteropTestOther>, StatusCode> =
+    let wrong_service: Result<binder::Strong<dyn IBinderRustNdkInteropTestOther>, StatusCode> =
         binder::get_interface(service_name);
     match wrong_service {
         Err(e) if e == StatusCode::BAD_TYPE => {}
diff --git a/libs/binder/tests/binderParcelBenchmark.cpp b/libs/binder/tests/binderParcelBenchmark.cpp
index ec69c36..26c50eb 100644
--- a/libs/binder/tests/binderParcelBenchmark.cpp
+++ b/libs/binder/tests/binderParcelBenchmark.cpp
@@ -91,56 +91,56 @@
 
   Results on Crosshatch Pixel 3XL
 
-  #BM_BoolVector/1         40 ns      40 ns     17261011
-  #BM_BoolVector/2         46 ns      46 ns     15029619
-  #BM_BoolVector/4         65 ns      64 ns     10888021
-  #BM_BoolVector/8        114 ns     114 ns      6130937
-  #BM_BoolVector/16       179 ns     179 ns      3902462
-  #BM_BoolVector/32       328 ns     327 ns      2138812
-  #BM_BoolVector/64       600 ns     598 ns      1169414
-  #BM_BoolVector/128     1168 ns    1165 ns       601281
-  #BM_BoolVector/256     2288 ns    2281 ns       305737
-  #BM_BoolVector/512     4535 ns    4521 ns       154668
-  #BM_ByteVector/1         53 ns      52 ns     13212196
-  #BM_ByteVector/2         53 ns      53 ns     13194050
-  #BM_ByteVector/4         50 ns      50 ns     13768037
-  #BM_ByteVector/8         50 ns      50 ns     13890210
-  #BM_ByteVector/16        50 ns      50 ns     13897305
-  #BM_ByteVector/32        51 ns      51 ns     13679862
-  #BM_ByteVector/64        54 ns      53 ns     12988544
-  #BM_ByteVector/128       64 ns      64 ns     10921227
-  #BM_ByteVector/256       82 ns      81 ns      8542549
-  #BM_ByteVector/512      118 ns     118 ns      5862931
-  #BM_CharVector/1         32 ns      32 ns     21783579
-  #BM_CharVector/2         38 ns      38 ns     18200971
-  #BM_CharVector/4         53 ns      53 ns     13111785
-  #BM_CharVector/8         80 ns      80 ns      8698331
-  #BM_CharVector/16       159 ns     159 ns      4390738
-  #BM_CharVector/32       263 ns     262 ns      2667310
-  #BM_CharVector/64       486 ns     485 ns      1441118
-  #BM_CharVector/128      937 ns     934 ns       749006
-  #BM_CharVector/256     1848 ns    1843 ns       379537
-  #BM_CharVector/512     3650 ns    3639 ns       191713
-  #BM_Int32Vector/1        31 ns      31 ns     22104147
-  #BM_Int32Vector/2        38 ns      38 ns     18075471
-  #BM_Int32Vector/4        53 ns      52 ns     13249969
-  #BM_Int32Vector/8        80 ns      80 ns      8719798
-  #BM_Int32Vector/16      161 ns     160 ns      4350096
-  #BM_Int32Vector/32      271 ns     270 ns      2591896
-  #BM_Int32Vector/64      499 ns     498 ns      1406201
-  #BM_Int32Vector/128     948 ns     945 ns       740052
-  #BM_Int32Vector/256    1855 ns    1849 ns       379127
-  #BM_Int32Vector/512    3665 ns    3653 ns       191533
-  #BM_Int64Vector/1        31 ns      31 ns     22388370
-  #BM_Int64Vector/2        38 ns      38 ns     18300347
-  #BM_Int64Vector/4        53 ns      53 ns     13137818
-  #BM_Int64Vector/8        81 ns      81 ns      8599613
-  #BM_Int64Vector/16      167 ns     166 ns      4195953
-  #BM_Int64Vector/32      280 ns     280 ns      2499271
-  #BM_Int64Vector/64      523 ns     522 ns      1341380
-  #BM_Int64Vector/128     991 ns     988 ns       707437
-  #BM_Int64Vector/256    1940 ns    1934 ns       361704
-  #BM_Int64Vector/512    3843 ns    3831 ns       183204
+  #BM_BoolVector/1         44 ns      44 ns     15630626
+  #BM_BoolVector/2         54 ns      54 ns     12900340
+  #BM_BoolVector/4         73 ns      72 ns      9749841
+  #BM_BoolVector/8        107 ns     107 ns      6503326
+  #BM_BoolVector/16       186 ns     185 ns      3773627
+  #BM_BoolVector/32       337 ns     336 ns      2083877
+  #BM_BoolVector/64       607 ns     605 ns      1154113
+  #BM_BoolVector/128     1155 ns    1151 ns       608128
+  #BM_BoolVector/256     2259 ns    2253 ns       310973
+  #BM_BoolVector/512     4469 ns    4455 ns       157277
+  #BM_ByteVector/1         41 ns      41 ns     16837425
+  #BM_ByteVector/2         41 ns      41 ns     16820726
+  #BM_ByteVector/4         38 ns      38 ns     18217813
+  #BM_ByteVector/8         38 ns      38 ns     18290298
+  #BM_ByteVector/16        38 ns      38 ns     18117817
+  #BM_ByteVector/32        38 ns      38 ns     18172385
+  #BM_ByteVector/64        41 ns      41 ns     16950055
+  #BM_ByteVector/128       53 ns      53 ns     13170749
+  #BM_ByteVector/256       69 ns      69 ns     10113626
+  #BM_ByteVector/512      106 ns     106 ns      6561936
+  #BM_CharVector/1         38 ns      38 ns     18074831
+  #BM_CharVector/2         40 ns      40 ns     17206266
+  #BM_CharVector/4         50 ns      50 ns     13785944
+  #BM_CharVector/8         67 ns      67 ns     10223316
+  #BM_CharVector/16        96 ns      96 ns      7297285
+  #BM_CharVector/32       156 ns     155 ns      4484845
+  #BM_CharVector/64       277 ns     276 ns      2536003
+  #BM_CharVector/128      520 ns     518 ns      1347070
+  #BM_CharVector/256     1006 ns    1003 ns       695952
+  #BM_CharVector/512     1976 ns    1970 ns       354673
+  #BM_Int32Vector/1        41 ns      41 ns     16951262
+  #BM_Int32Vector/2        41 ns      41 ns     16916883
+  #BM_Int32Vector/4        41 ns      41 ns     16761373
+  #BM_Int32Vector/8        42 ns      42 ns     16553179
+  #BM_Int32Vector/16       43 ns      43 ns     16200362
+  #BM_Int32Vector/32       55 ns      54 ns     12724454
+  #BM_Int32Vector/64       70 ns      69 ns     10049223
+  #BM_Int32Vector/128     107 ns     107 ns      6525796
+  #BM_Int32Vector/256     179 ns     178 ns      3922563
+  #BM_Int32Vector/512     324 ns     323 ns      2160653
+  #BM_Int64Vector/1        41 ns      41 ns     16909470
+  #BM_Int64Vector/2        41 ns      41 ns     16740788
+  #BM_Int64Vector/4        42 ns      42 ns     16564197
+  #BM_Int64Vector/8        43 ns      42 ns     16284082
+  #BM_Int64Vector/16       54 ns      54 ns     12839474
+  #BM_Int64Vector/32       69 ns      69 ns     10011010
+  #BM_Int64Vector/64      107 ns     106 ns      6557956
+  #BM_Int64Vector/128     177 ns     177 ns      3925618
+  #BM_Int64Vector/256     324 ns     323 ns      2163321
+  #BM_Int64Vector/512     613 ns     611 ns      1140418
 */
 
 static void BM_BoolVector(benchmark::State& state) {
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index c2ec0fe..1e6fc2b 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -133,8 +133,10 @@
     if (enableTripleBuffering) {
         mProducer->setMaxDequeuedBufferCount(2);
     }
-    mBufferItemConsumer =
-        new BLASTBufferItemConsumer(mConsumer, GraphicBuffer::USAGE_HW_COMPOSER, 1, false);
+    mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
+                                                      GraphicBuffer::USAGE_HW_COMPOSER |
+                                                              GraphicBuffer::USAGE_HW_TEXTURE,
+                                                      1, false);
     static int32_t id = 0;
     auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id);
     id++;
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 5c9949b..9cd3f63 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -36,9 +36,7 @@
 DisplayEventDispatcher::DisplayEventDispatcher(
         const sp<Looper>& looper, ISurfaceComposer::VsyncSource vsyncSource,
         ISurfaceComposer::EventRegistrationFlags eventRegistration)
-      : mLooper(looper),
-        mReceiver(vsyncSource, eventRegistration),
-        mVsyncState(VsyncState::Unregistered) {
+      : mLooper(looper), mReceiver(vsyncSource, eventRegistration), mWaitingForVsync(false) {
     ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
 }
 
@@ -68,37 +66,26 @@
 }
 
 status_t DisplayEventDispatcher::scheduleVsync() {
-    switch (mVsyncState) {
-        case VsyncState::Unregistered: {
-            ALOGV("dispatcher %p ~ Scheduling vsync.", this);
+    if (!mWaitingForVsync) {
+        ALOGV("dispatcher %p ~ Scheduling vsync.", this);
 
-            // Drain all pending events.
-            nsecs_t vsyncTimestamp;
-            PhysicalDisplayId vsyncDisplayId;
-            uint32_t vsyncCount;
-            VsyncEventData vsyncEventData;
-            if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount,
-                                     &vsyncEventData)) {
-                ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "",
-                      this, ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
-            }
+        // Drain all pending events.
+        nsecs_t vsyncTimestamp;
+        PhysicalDisplayId vsyncDisplayId;
+        uint32_t vsyncCount;
+        VsyncEventData vsyncEventData;
+        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) {
+            ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", this,
+                  ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
+        }
 
-            status_t status = mReceiver.setVsyncRate(1);
-            if (status) {
-                ALOGW("Failed to set vsync rate, status=%d", status);
-                return status;
-            }
+        status_t status = mReceiver.requestNextVsync();
+        if (status) {
+            ALOGW("Failed to request next vsync, status=%d", status);
+            return status;
+        }
 
-            mVsyncState = VsyncState::RegisteredAndWaitingForVsync;
-            break;
-        }
-        case VsyncState::Registered: {
-            mVsyncState = VsyncState::RegisteredAndWaitingForVsync;
-            break;
-        }
-        case VsyncState::RegisteredAndWaitingForVsync: {
-            break;
-        }
+        mWaitingForVsync = true;
     }
     return OK;
 }
@@ -136,23 +123,8 @@
               ", displayId=%s, count=%d, vsyncId=%" PRId64,
               this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount,
               vsyncEventData.id);
-        switch (mVsyncState) {
-            case VsyncState::Unregistered:
-                ALOGW("Received unexpected VSYNC event");
-                break;
-            case VsyncState::RegisteredAndWaitingForVsync:
-                mVsyncState = VsyncState::Registered;
-                dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData);
-                break;
-            case VsyncState::Registered:
-                status_t status = mReceiver.setVsyncRate(0);
-                if (status) {
-                    ALOGW("Failed to reset vsync rate, status=%d", status);
-                    return status;
-                }
-                mVsyncState = VsyncState::Unregistered;
-                break;
-        }
+        mWaitingForVsync = false;
+        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData);
     }
 
     return 1; // keep the callback
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 10d48a3..762746c 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -1235,6 +1235,18 @@
         }
         return reply.readInt32();
     }
+
+    status_t getExtraBufferCount(int* extraBuffers) const override {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        status_t err = remote()->transact(BnSurfaceComposer::GET_EXTRA_BUFFER_COUNT, data, &reply);
+        if (err != NO_ERROR) {
+            ALOGE("getExtraBufferCount failed to read data:  %s (%d)", strerror(-err), err);
+            return err;
+        }
+
+        return reply.readInt32(extraBuffers);
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -2107,6 +2119,16 @@
             SAFE_PARCEL(reply->writeInt32, priority);
             return NO_ERROR;
         }
+        case GET_EXTRA_BUFFER_COUNT: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            int extraBuffers = 0;
+            int err = getExtraBufferCount(&extraBuffers);
+            if (err != NO_ERROR) {
+                return err;
+            }
+            SAFE_PARCEL(reply->writeInt32, extraBuffers);
+            return NO_ERROR;
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index fff3305..f053372 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -167,6 +167,9 @@
         SAFE_PARCEL(output.writeInt32, region.right);
         SAFE_PARCEL(output.writeInt32, region.bottom);
     }
+
+    SAFE_PARCEL(output.write, stretchEffect);
+
     return NO_ERROR;
 }
 
@@ -290,6 +293,9 @@
         SAFE_PARCEL(input.readInt32, &region.bottom);
         blurRegions.push_back(region);
     }
+
+    SAFE_PARCEL(input.read, stretchEffect);
+
     return NO_ERROR;
 }
 
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 59ad8d2..07fc069 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1499,6 +1499,9 @@
     case NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO:
         res = dispatchSetFrameTimelineInfo(args);
         break;
+    case NATIVE_WINDOW_GET_EXTRA_BUFFER_COUNT:
+        res = dispatchGetExtraBufferCount(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -1815,6 +1818,14 @@
     return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId});
 }
 
+int Surface::dispatchGetExtraBufferCount(va_list args) {
+    ATRACE_CALL();
+    auto extraBuffers = static_cast<int*>(va_arg(args, int*));
+
+    ALOGV("Surface::dispatchGetExtraBufferCount");
+    return getExtraBufferCount(extraBuffers);
+}
+
 bool Surface::transformToDisplayInverse() {
     return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
             NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
@@ -2584,4 +2595,8 @@
     return composerService()->setFrameTimelineInfo(mGraphicBufferProducer, frameTimelineInfo);
 }
 
+status_t Surface::getExtraBufferCount(int* extraBuffers) const {
+    return composerService()->getExtraBufferCount(extraBuffers);
+}
+
 }; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 4493a21..27fb2a8 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1569,6 +1569,23 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setStretchEffect(
+        const sp<SurfaceControl>& sc, float left, float top, float right, float bottom, float vecX,
+        float vecY, float maxAmount) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eStretchChanged;
+    s->stretchEffect = StretchEffect{.area = {left, top, right, bottom},
+                                     .vectorX = vecX,
+                                     .vectorY = vecY,
+                                     .maxAmount = maxAmount};
+    return *this;
+}
+
 // ---------------------------------------------------------------------------
 
 DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index 43b9feb..d74c2ba 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -52,20 +52,7 @@
 private:
     sp<Looper> mLooper;
     DisplayEventReceiver mReceiver;
-    // The state of vsync event registration and whether the client is expecting
-    // an event or not.
-    enum class VsyncState {
-        // The dispatcher is not registered for vsync events.
-        Unregistered,
-        // The dispatcher is registered to receive vsync events but should not dispatch it to the
-        // client as the client is not expecting a vsync event.
-        Registered,
-
-        // The dispatcher is registered to receive vsync events and supposed to dispatch it to
-        // the client.
-        RegisteredAndWaitingForVsync,
-    };
-    VsyncState mVsyncState;
+    bool mWaitingForVsync;
 
     std::vector<FrameRateOverride> mFrameRateOverrides;
 
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 6d1a3ab..292838e 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -505,6 +505,24 @@
      * Gets priority of the RenderEngine in SurfaceFlinger.
      */
     virtual int getGPUContextPriority() = 0;
+
+    /**
+     * Gets the extra buffers a client would need to allocate if it passes
+     * the Choreographer#getVsyncId with its buffers.
+     *
+     * When Choreographer#getVsyncId is passed to SurfaceFlinger, it is used
+     * as an indication of when to latch the buffer. SurfaceFlinger will make
+     * sure that it will give the app at least the time configured as the
+     * 'appDuration' before trying to latch the buffer.
+     *
+     * The total buffers needed for a given configuration is basically the
+     * numbers of vsyncs a single buffer is used across the stack. For the default
+     * configuration a buffer is held ~1 vsync by the app, ~1 vsync by SurfaceFlinger
+     * and 1 vsync by the display. The extra buffers are calculated as the
+     * number of additional buffers on top of the 3 buffers already allocated
+     * by the app.
+     */
+    virtual status_t getExtraBufferCount(int* extraBuffers) const = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -567,6 +585,7 @@
         SET_FRAME_TIMELINE_INFO,
         ADD_TRANSACTION_TRACE_LISTENER,
         GET_GPU_CONTEXT_PRIORITY,
+        GET_EXTRA_BUFFER_COUNT,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 2f9a0c0..b273805 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -56,6 +56,7 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Rotation.h>
+#include <ui/StretchEffect.h>
 #include <ui/Transform.h>
 #include <utils/Errors.h>
 
@@ -135,6 +136,7 @@
         eFrameTimelineInfoChanged = 0x800'00000000,
         eBlurRegionsChanged = 0x1000'00000000,
         eAutoRefreshChanged = 0x2000'00000000,
+        eStretchChanged = 0x4000'00000000,
     };
 
     layer_state_t();
@@ -244,6 +246,9 @@
     // can and not wait for a frame to become available. This is only relevant
     // in shared buffer mode.
     bool autoRefresh;
+
+    // Stretch effect to be applied to this layer
+    StretchEffect stretchEffect;
 };
 
 struct ComposerState {
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index b6b5c7c..5881221 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -189,6 +189,7 @@
 
     virtual status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
     virtual status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
+    virtual status_t getExtraBufferCount(int* extraBuffers) const;
 
 protected:
     virtual ~Surface();
@@ -275,6 +276,7 @@
     int dispatchAddQueryInterceptor(va_list args);
     int dispatchGetLastQueuedBuffer(va_list args);
     int dispatchSetFrameTimelineInfo(va_list args);
+    int dispatchGetExtraBufferCount(va_list args);
     bool transformToDisplayInverse();
 
 protected:
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index bd9f066..e89f3a7 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -550,6 +550,10 @@
         // transactions from blocking each other.
         Transaction& setApplyToken(const sp<IBinder>& token);
 
+        Transaction& setStretchEffect(const sp<SurfaceControl>& sc, float left, float top,
+                                      float right, float bottom, float vecX, float vecY,
+                                      float maxAmount);
+
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
 
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 80e4c77..43909ac 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -889,6 +889,8 @@
 
     int getGPUContextPriority() override { return 0; };
 
+    status_t getExtraBufferCount(int* /*extraBuffers*/) const override { return NO_ERROR; }
+
 protected:
     IBinder* onAsBinder() override { return nullptr; }
 
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index acea473..6218fdc 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -234,6 +234,7 @@
         }
         case InputMessage::Type::FINISHED: {
             msg->body.finished.handled = body.finished.handled;
+            msg->body.finished.consumeTime = body.finished.consumeTime;
             break;
         }
         case InputMessage::Type::FOCUS: {
@@ -597,7 +598,8 @@
     return mChannel->sendMessage(&msg);
 }
 
-status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
+status_t InputPublisher::receiveFinishedSignal(
+        const std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)>& callback) {
     if (DEBUG_TRANSPORT_ACTIONS) {
         ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str());
     }
@@ -605,8 +607,6 @@
     InputMessage msg;
     status_t result = mChannel->receiveMessage(&msg);
     if (result) {
-        *outSeq = 0;
-        *outHandled = false;
         return result;
     }
     if (msg.header.type != InputMessage::Type::FINISHED) {
@@ -614,8 +614,7 @@
                 mChannel->getName().c_str(), msg.header.type);
         return UNKNOWN_ERROR;
     }
-    *outSeq = msg.header.seq;
-    *outHandled = msg.body.finished.handled == 1;
+    callback(msg.header.seq, msg.body.finished.handled == 1, msg.body.finished.consumeTime);
     return OK;
 }
 
@@ -651,6 +650,9 @@
         } else {
             // Receive a fresh message.
             status_t result = mChannel->receiveMessage(&mMsg);
+            if (result == OK) {
+                mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC));
+            }
             if (result) {
                 // Consume the next batched event unless batches are being held for later.
                 if (consumeBatches || result != WOULD_BLOCK) {
@@ -1147,12 +1149,33 @@
     return sendUnchainedFinishedSignal(seq, handled);
 }
 
+nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const {
+    auto it = mConsumeTimes.find(seq);
+    // Consume time will be missing if either 'finishInputEvent' is called twice, or if it was
+    // called for the wrong (synthetic?) input event. Either way, it is a bug that should be fixed.
+    LOG_ALWAYS_FATAL_IF(it == mConsumeTimes.end(), "Could not find consume time for seq=%" PRIu32,
+                        seq);
+    return it->second;
+}
+
+void InputConsumer::popConsumeTime(uint32_t seq) {
+    mConsumeTimes.erase(seq);
+}
+
 status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
     InputMessage msg;
     msg.header.type = InputMessage::Type::FINISHED;
     msg.header.seq = seq;
     msg.body.finished.handled = handled ? 1 : 0;
-    return mChannel->sendMessage(&msg);
+    msg.body.finished.consumeTime = getConsumeTime(seq);
+    status_t result = mChannel->sendMessage(&msg);
+    if (result == OK) {
+        // Remove the consume time if the socket write succeeded. We will not need to ack this
+        // message anymore. If the socket write did not succeed, we will try again and will still
+        // need consume time.
+        popConsumeTime(seq);
+    }
+    return result;
 }
 
 bool InputConsumer::hasDeferredEvent() const {
@@ -1304,8 +1327,9 @@
                     break;
                 }
                 case InputMessage::Type::FINISHED: {
-                    out += android::base::StringPrintf("handled=%s",
-                                                       toString(msg.body.finished.handled));
+                    out += android::base::StringPrintf("handled=%s, consumeTime=%" PRId64,
+                                                       toString(msg.body.finished.handled),
+                                                       msg.body.finished.consumeTime);
                     break;
                 }
                 case InputMessage::Type::FOCUS: {
@@ -1335,6 +1359,14 @@
     if (mSeqChains.empty()) {
         out += "    <empty>\n";
     }
+    out += "mConsumeTimes:\n";
+    for (const auto& [seq, consumeTime] : mConsumeTimes) {
+        out += android::base::StringPrintf("    seq = %" PRIu32 " consumeTime = %" PRId64, seq,
+                                           consumeTime);
+    }
+    if (mConsumeTimes.empty()) {
+        out += "    <empty>\n";
+    }
     return out;
 }
 
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 9da7b69..e7e566d 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -82,6 +82,7 @@
     constexpr int32_t repeatCount = 1;
     constexpr nsecs_t downTime = 3;
     constexpr nsecs_t eventTime = 4;
+    const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     status = mPublisher->publishKeyEvent(seq, eventId, deviceId, source, displayId, hmac, action,
                                          flags, keyCode, scanCode, metaState, repeatCount, downTime,
@@ -122,13 +123,22 @@
 
     uint32_t finishedSeq = 0;
     bool handled = false;
-    status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+    nsecs_t consumeTime;
+    status = mPublisher->receiveFinishedSignal(
+            [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
+                                                   nsecs_t inConsumeTime) -> void {
+                finishedSeq = inSeq;
+                handled = inHandled;
+                consumeTime = inConsumeTime;
+            });
     ASSERT_EQ(OK, status)
             << "publisher receiveFinishedSignal should return OK";
     ASSERT_EQ(seq, finishedSeq)
             << "publisher receiveFinishedSignal should have returned the original sequence number";
     ASSERT_TRUE(handled)
             << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+    ASSERT_GE(consumeTime, publishTime)
+            << "finished signal's consume time should be greater than publish time";
 }
 
 void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() {
@@ -160,6 +170,7 @@
     constexpr nsecs_t downTime = 3;
     constexpr size_t pointerCount = 3;
     constexpr nsecs_t eventTime = 4;
+    const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
     PointerProperties pointerProperties[pointerCount];
     PointerCoords pointerCoords[pointerCount];
     for (size_t i = 0; i < pointerCount; i++) {
@@ -262,13 +273,22 @@
 
     uint32_t finishedSeq = 0;
     bool handled = true;
-    status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+    nsecs_t consumeTime;
+    status = mPublisher->receiveFinishedSignal(
+            [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
+                                                   nsecs_t inConsumeTime) -> void {
+                finishedSeq = inSeq;
+                handled = inHandled;
+                consumeTime = inConsumeTime;
+            });
     ASSERT_EQ(OK, status)
             << "publisher receiveFinishedSignal should return OK";
     ASSERT_EQ(seq, finishedSeq)
             << "publisher receiveFinishedSignal should have returned the original sequence number";
     ASSERT_FALSE(handled)
             << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+    ASSERT_GE(consumeTime, publishTime)
+            << "finished signal's consume time should be greater than publish time";
 }
 
 void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() {
@@ -278,6 +298,7 @@
     int32_t eventId = InputEvent::nextId();
     constexpr bool hasFocus = true;
     constexpr bool inTouchMode = true;
+    const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode);
     ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
@@ -302,12 +323,21 @@
 
     uint32_t finishedSeq = 0;
     bool handled = false;
-    status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+    nsecs_t consumeTime;
+    status = mPublisher->receiveFinishedSignal(
+            [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
+                                                   nsecs_t inConsumeTime) -> void {
+                finishedSeq = inSeq;
+                handled = inHandled;
+                consumeTime = inConsumeTime;
+            });
     ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
     ASSERT_EQ(seq, finishedSeq)
             << "publisher receiveFinishedSignal should have returned the original sequence number";
     ASSERT_TRUE(handled)
             << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+    ASSERT_GE(consumeTime, publishTime)
+            << "finished signal's consume time should be greater than publish time";
 }
 
 void InputPublisherAndConsumerTest::PublishAndConsumeCaptureEvent() {
@@ -316,6 +346,7 @@
     constexpr uint32_t seq = 42;
     int32_t eventId = InputEvent::nextId();
     constexpr bool captureEnabled = true;
+    const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     status = mPublisher->publishCaptureEvent(seq, eventId, captureEnabled);
     ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
@@ -339,12 +370,21 @@
 
     uint32_t finishedSeq = 0;
     bool handled = false;
-    status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+    nsecs_t consumeTime;
+    status = mPublisher->receiveFinishedSignal(
+            [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled,
+                                                   nsecs_t inConsumeTime) -> void {
+                finishedSeq = inSeq;
+                handled = inHandled;
+                consumeTime = inConsumeTime;
+            });
     ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
     ASSERT_EQ(seq, finishedSeq)
             << "publisher receiveFinishedSignal should have returned the original sequence number";
     ASSERT_TRUE(handled)
             << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+    ASSERT_GE(consumeTime, publishTime)
+            << "finished signal's consume time should be greater than publish time";
 }
 
 TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 4107d61..a886585 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -83,6 +83,7 @@
   CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4);
 
   CHECK_OFFSET(InputMessage::Body::Finished, handled, 4);
+  CHECK_OFFSET(InputMessage::Body::Finished, consumeTime, 8);
 }
 
 void TestHeaderSize() {
@@ -100,7 +101,7 @@
     static_assert(sizeof(InputMessage::Body::Motion) ==
                   offsetof(InputMessage::Body::Motion, pointers) +
                           sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
-    static_assert(sizeof(InputMessage::Body::Finished) == 8);
+    static_assert(sizeof(InputMessage::Body::Finished) == 16);
     static_assert(sizeof(InputMessage::Body::Focus) == 8);
     static_assert(sizeof(InputMessage::Body::Capture) == 8);
 }
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index ffe4412..7aa2cf4 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -256,6 +256,7 @@
     NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER          = 46,    /* private */
     NATIVE_WINDOW_SET_QUERY_INTERCEPTOR           = 47,    /* private */
     NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO         = 48,    /* private */
+    NATIVE_WINDOW_GET_EXTRA_BUFFER_COUNT          = 49,    /* private */
     // clang-format on
 };
 
@@ -1030,6 +1031,11 @@
                            frameTimelineVsyncId, inputEventId);
 }
 
+static inline int native_window_get_extra_buffer_count(
+    struct ANativeWindow* window, int* extraBuffers) {
+    return window->perform(window, NATIVE_WINDOW_GET_EXTRA_BUFFER_COUNT, extraBuffers);
+}
+
 // ------------------------------------------------------------------------------------------------
 // Candidates for APEX visibility
 // These functions are planned to be made stable for APEX modules, but have not
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index d69c7ae..00540b8 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -31,10 +31,6 @@
         "libui",
         "libutils",
     ],
-    include_dirs: [
-        "external/skia/src/gpu",
-    ],
-    whole_static_libs: ["libskia"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
 }
@@ -106,6 +102,10 @@
         ":librenderengine_threaded_sources",
         ":librenderengine_skia_sources",
     ],
+    include_dirs: [
+        "external/skia/src/gpu",
+    ],
+    whole_static_libs: ["libskia"],
     lto: {
         thin: true,
     },
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 3a727c9..7661233 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -28,6 +28,7 @@
 #include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
+#include <ui/StretchEffect.h>
 #include <ui/Transform.h>
 
 namespace android {
@@ -155,6 +156,8 @@
 
     std::vector<BlurRegion> blurRegions;
 
+    StretchEffect stretchEffect;
+
     // Name associated with the layer for debugging purposes.
     std::string name;
 };
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index dd26b17..b4a2d79 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -286,6 +286,7 @@
     }
 
     if (args.supportsBackgroundBlur) {
+        ALOGD("Background Blurs Enabled");
         mBlurFilter = new BlurFilter();
     }
     mCapture = std::make_unique<SkiaCapture>();
@@ -491,6 +492,9 @@
                                                               const LayerSettings* layer,
                                                               const DisplaySettings& display,
                                                               bool undoPremultipliedAlpha) {
+    if (layer->stretchEffect.hasEffect()) {
+        // TODO: Implement
+    }
     if (mUseColorManagement &&
         needsLinearEffect(layer->colorTransform, layer->sourceDataspace, display.outputDataspace)) {
         LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace,
@@ -513,6 +517,45 @@
     return shader;
 }
 
+void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
+    if (mCapture->isCaptureRunning()) {
+        // Record display settings when capture is running.
+        std::stringstream displaySettings;
+        PrintTo(display, &displaySettings);
+        // Store the DisplaySettings in additional information.
+        canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings",
+                               SkData::MakeWithCString(displaySettings.str().c_str()));
+    }
+
+    // Before doing any drawing, let's make sure that we'll start at the origin of the display.
+    // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual
+    // displays might have different scaling when compared to the physical screen.
+
+    canvas->clipRect(getSkRect(display.physicalDisplay));
+    canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top);
+
+    const auto clipWidth = display.clip.width();
+    const auto clipHeight = display.clip.height();
+    auto rotatedClipWidth = clipWidth;
+    auto rotatedClipHeight = clipHeight;
+    // Scale is contingent on the rotation result.
+    if (display.orientation & ui::Transform::ROT_90) {
+        std::swap(rotatedClipWidth, rotatedClipHeight);
+    }
+    const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) /
+            static_cast<SkScalar>(rotatedClipWidth);
+    const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) /
+            static_cast<SkScalar>(rotatedClipHeight);
+    canvas->scale(scaleX, scaleY);
+
+    // Canvas rotation is done by centering the clip window at the origin, rotating, translating
+    // back so that the top left corner of the clip is at (0, 0).
+    canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2);
+    canvas->rotate(toDegrees(display.orientation));
+    canvas->translate(-clipWidth / 2, -clipHeight / 2);
+    canvas->translate(-display.clip.left, -display.clip.top);
+}
+
 status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
                                         const std::vector<const LayerSettings*>& layers,
                                         const sp<GraphicBuffer>& buffer,
@@ -567,57 +610,49 @@
         }
     }
 
-    sk_sp<SkSurface> surface =
+    sk_sp<SkSurface> dstSurface =
             surfaceTextureRef->getTexture()->getOrCreateSurface(mUseColorManagement
                                                                         ? display.outputDataspace
                                                                         : ui::Dataspace::UNKNOWN,
                                                                 grContext.get());
 
-    SkCanvas* canvas = mCapture->tryCapture(surface.get());
-    if (canvas == nullptr) {
+    SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
+    if (dstCanvas == nullptr) {
         ALOGE("Cannot acquire canvas from Skia.");
         return BAD_VALUE;
     }
+
+    // Find if any layers have requested blur, we'll use that info to decide when to render to an
+    // offscreen buffer and when to render to the native buffer.
+    sk_sp<SkSurface> activeSurface(dstSurface);
+    SkCanvas* canvas = dstCanvas;
+    const LayerSettings* blurCompositionLayer = nullptr;
+    if (mBlurFilter) {
+        bool requiresCompositionLayer = false;
+        for (const auto& layer : layers) {
+            if (layer->backgroundBlurRadius > 0) {
+                // when skbug.com/11208 and b/176903027 are resolved we can add the additional
+                // restriction for layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius
+                requiresCompositionLayer = true;
+            }
+            for (auto region : layer->blurRegions) {
+                if (region.blurRadius < BlurFilter::kMaxCrossFadeRadius) {
+                    requiresCompositionLayer = true;
+                }
+            }
+            if (requiresCompositionLayer) {
+                activeSurface = dstSurface->makeSurface(dstSurface->imageInfo());
+                canvas = activeSurface->getCanvas();
+                blurCompositionLayer = layer;
+                break;
+            }
+        }
+    }
+
+    canvas->save();
     // Clear the entire canvas with a transparent black to prevent ghost images.
     canvas->clear(SK_ColorTRANSPARENT);
-    canvas->save();
-
-    if (mCapture->isCaptureRunning()) {
-        // Record display settings when capture is running.
-        std::stringstream displaySettings;
-        PrintTo(display, &displaySettings);
-        // Store the DisplaySettings in additional information.
-        canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings",
-                               SkData::MakeWithCString(displaySettings.str().c_str()));
-    }
-
-    // Before doing any drawing, let's make sure that we'll start at the origin of the display.
-    // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual
-    // displays might have different scaling when compared to the physical screen.
-
-    canvas->clipRect(getSkRect(display.physicalDisplay));
-    canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top);
-
-    const auto clipWidth = display.clip.width();
-    const auto clipHeight = display.clip.height();
-    auto rotatedClipWidth = clipWidth;
-    auto rotatedClipHeight = clipHeight;
-    // Scale is contingent on the rotation result.
-    if (display.orientation & ui::Transform::ROT_90) {
-        std::swap(rotatedClipWidth, rotatedClipHeight);
-    }
-    const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) /
-            static_cast<SkScalar>(rotatedClipWidth);
-    const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) /
-            static_cast<SkScalar>(rotatedClipHeight);
-    canvas->scale(scaleX, scaleY);
-
-    // Canvas rotation is done by centering the clip window at the origin, rotating, translating
-    // back so that the top left corner of the clip is at (0, 0).
-    canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2);
-    canvas->rotate(toDegrees(display.orientation));
-    canvas->translate(-clipWidth / 2, -clipHeight / 2);
-    canvas->translate(-display.clip.left, -display.clip.top);
+    initCanvas(canvas, display);
 
     // TODO: clearRegion was required for SurfaceView when a buffer is not yet available but the
     // view is still on-screen. The clear region could be re-specified as a black color layer,
@@ -644,8 +679,36 @@
 
     for (const auto& layer : layers) {
         ATRACE_NAME("DrawLayer");
-        canvas->save();
 
+        sk_sp<SkImage> blurInput;
+        if (blurCompositionLayer == layer) {
+            LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface);
+            LOG_ALWAYS_FATAL_IF(canvas == dstCanvas);
+
+            // save a snapshot of the activeSurface to use as input to the blur shaders
+            blurInput = activeSurface->makeImageSnapshot();
+
+            // TODO we could skip this step if we know the blur will cover the entire image
+            //  blit the offscreen framebuffer into the destination AHB
+            SkPaint paint;
+            paint.setBlendMode(SkBlendMode::kSrc);
+            activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint);
+
+            // assign dstCanvas to canvas and ensure that the canvas state is up to date
+            canvas = dstCanvas;
+            canvas->save();
+            initCanvas(canvas, display);
+
+            LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getSaveCount() !=
+                                dstSurface->getCanvas()->getSaveCount());
+            LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getTotalMatrix() !=
+                                dstSurface->getCanvas()->getTotalMatrix());
+
+            // assign dstSurface to activeSurface
+            activeSurface = dstSurface;
+        }
+
+        canvas->save();
         if (mCapture->isCaptureRunning()) {
             // Record the name of the layer if the capture is running.
             std::stringstream layerSettings;
@@ -654,34 +717,42 @@
             canvas->drawAnnotation(SkRect::MakeEmpty(), layer->name.c_str(),
                                    SkData::MakeWithCString(layerSettings.str().c_str()));
         }
-
         // Layers have a local transform that should be applied to them
         canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());
 
-        SkPaint paint;
-        const auto& bounds = layer->geometry.boundaries;
-        const auto dest = getSkRect(bounds);
-        const auto layerRect = canvas->getTotalMatrix().mapRect(dest);
-        std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
-        if (mBlurFilter) {
+        const auto bounds = getSkRect(layer->geometry.boundaries);
+        if (mBlurFilter && layerHasBlur(layer)) {
+            std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
+
+            // if multiple layers have blur, then we need to take a snapshot now because
+            // only the lowest layer will have blurImage populated earlier
+            if (!blurInput) {
+                blurInput = activeSurface->makeImageSnapshot();
+            }
+            // rect to be blurred in the coordinate space of blurInput
+            const auto blurRect = canvas->getTotalMatrix().mapRect(bounds);
+
             if (layer->backgroundBlurRadius > 0) {
                 ATRACE_NAME("BackgroundBlur");
-                auto blurredSurface = mBlurFilter->generate(canvas, surface,
-                                                            layer->backgroundBlurRadius, layerRect);
-                cachedBlurs[layer->backgroundBlurRadius] = blurredSurface;
+                auto blurredImage =
+                        mBlurFilter->generate(grContext.get(), layer->backgroundBlurRadius,
+                                              blurInput, blurRect);
 
-                drawBlurRegion(canvas, getBlurRegion(layer), layerRect, blurredSurface);
+                cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
+
+                mBlurFilter->drawBlurRegion(canvas, getBlurRegion(layer), blurRect, blurredImage,
+                                            blurInput);
             }
-            if (layer->blurRegions.size() > 0) {
-                for (auto region : layer->blurRegions) {
-                    if (cachedBlurs[region.blurRadius]) {
-                        continue;
-                    }
+            for (auto region : layer->blurRegions) {
+                if (cachedBlurs[region.blurRadius] != nullptr) {
                     ATRACE_NAME("BlurRegion");
-                    auto blurredSurface =
-                            mBlurFilter->generate(canvas, surface, region.blurRadius, layerRect);
-                    cachedBlurs[region.blurRadius] = blurredSurface;
+                    cachedBlurs[region.blurRadius] =
+                            mBlurFilter->generate(grContext.get(), region.blurRadius, blurInput,
+                                                  blurRect);
                 }
+
+                mBlurFilter->drawBlurRegion(canvas, region, blurRect,
+                                            cachedBlurs[region.blurRadius], blurInput);
             }
         }
 
@@ -695,6 +766,7 @@
                            : layer->sourceDataspace)
                 : ui::Dataspace::UNKNOWN;
 
+        SkPaint paint;
         if (layer->source.buffer.buffer) {
             ATRACE_NAME("DrawImage");
             const auto& item = layer->source.buffer;
@@ -721,7 +793,7 @@
             // textureTansform was intended to be passed directly into a shader, so when
             // building the total matrix with the textureTransform we need to first
             // normalize it, then apply the textureTransform, then scale back up.
-            texMatrix.preScale(1.0f / bounds.getWidth(), 1.0f / bounds.getHeight());
+            texMatrix.preScale(1.0f / bounds.width(), 1.0f / bounds.height());
             texMatrix.postScale(image->width(), image->height());
 
             SkMatrix matrix;
@@ -791,14 +863,10 @@
 
         paint.setColorFilter(filter);
 
-        for (const auto effectRegion : layer->blurRegions) {
-            drawBlurRegion(canvas, effectRegion, layerRect, cachedBlurs[effectRegion.blurRadius]);
-        }
-
         if (layer->shadow.length > 0) {
             const auto rect = layer->geometry.roundedCornersRadius > 0
                     ? getSkRect(layer->geometry.roundedCornersCrop)
-                    : dest;
+                    : bounds;
             drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
         } else {
             // Shadows are assumed to live only on their own layer - it's not valid
@@ -809,7 +877,7 @@
             if (layer->geometry.roundedCornersRadius > 0) {
                 canvas->clipRRect(getRoundedRect(layer), true);
             }
-            canvas->drawRect(dest, paint);
+            canvas->drawRect(bounds, paint);
         }
         canvas->restore();
     }
@@ -817,7 +885,8 @@
     mCapture->endCapture();
     {
         ATRACE_NAME("flush surface");
-        surface->flush();
+        LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
+        activeSurface->flush();
     }
 
     if (drawFence != nullptr) {
@@ -874,6 +943,10 @@
                       .bottom = static_cast<int>(rect.fBottom)};
 }
 
+inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer) {
+    return layer->backgroundBlurRadius > 0 || layer->blurRegions.size();
+}
+
 inline SkColor SkiaGLRenderEngine::getSkColor(const vec4& color) {
     return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255);
 }
@@ -913,53 +986,6 @@
                               flags);
 }
 
-void SkiaGLRenderEngine::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
-                                        const SkRect& layerRect, sk_sp<SkImage> blurredImage) {
-    ATRACE_CALL();
-
-    SkPaint paint;
-    paint.setAlpha(static_cast<int>(effectRegion.alpha * 255));
-    const auto matrix = getBlurShaderTransform(canvas, layerRect);
-    SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone);
-    paint.setShader(blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
-                                             &matrix));
-
-    auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
-                                 effectRegion.bottom);
-
-    if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 ||
-        effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) {
-        const SkVector radii[4] =
-                {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL),
-                 SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR),
-                 SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL),
-                 SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)};
-        SkRRect roundedRect;
-        roundedRect.setRectRadii(rect, radii);
-        canvas->drawRRect(roundedRect, paint);
-    } else {
-        canvas->drawRect(rect, paint);
-    }
-}
-
-SkMatrix SkiaGLRenderEngine::getBlurShaderTransform(const SkCanvas* canvas,
-                                                    const SkRect& layerRect) {
-    // 1. Apply the blur shader matrix, which scales up the blured surface to its real size
-    auto matrix = mBlurFilter->getShaderMatrix();
-    // 2. Since the blurred surface has the size of the layer, we align it with the
-    // top left corner of the layer position.
-    matrix.postConcat(SkMatrix::Translate(layerRect.fLeft, layerRect.fTop));
-    // 3. Finally, apply the inverse canvas matrix. The snapshot made in the BlurFilter is in the
-    // original surface orientation. The inverse matrix has to be applied to align the blur
-    // surface with the current orientation/position of the canvas.
-    SkMatrix drawInverse;
-    if (canvas->getTotalMatrix().invert(&drawInverse)) {
-        matrix.postConcat(drawInverse);
-    }
-
-    return matrix;
-}
-
 EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
                                                 EGLContext shareContext,
                                                 std::optional<ContextPriority> contextPriority,
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 810fc2a..8ef7359 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -81,17 +81,16 @@
     inline SkRect getSkRect(const Rect& layer);
     inline SkRRect getRoundedRect(const LayerSettings* layer);
     inline BlurRegion getBlurRegion(const LayerSettings* layer);
+    inline bool layerHasBlur(const LayerSettings* layer);
     inline SkColor getSkColor(const vec4& color);
     inline SkM44 getSkM44(const mat4& matrix);
     inline SkPoint3 getSkPoint3(const vec3& vector);
 
     base::unique_fd flush();
     bool waitFence(base::unique_fd fenceFd);
+    void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
     void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
                     const ShadowSettings& shadowSettings);
-    void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& layerRect,
-                        sk_sp<SkImage> blurredImage);
-    SkMatrix getBlurShaderTransform(const SkCanvas* canvas, const SkRect& layerRect);
     // If mUseColorManagement is correct and layer needsLinearEffect, it returns a linear runtime
     // shader. Otherwise it returns the input shader.
     sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings* layer,
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 87b1a7c..9d20e32 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -34,17 +34,14 @@
 BlurFilter::BlurFilter() {
     SkString blurString(R"(
         in shader input;
-        uniform float in_inverseScale;
         uniform float2 in_blurOffset;
 
         half4 main(float2 xy) {
-            float2 scaled_xy = float2(xy.x * in_inverseScale, xy.y * in_inverseScale);
-
-            half4 c = sample(input, scaled_xy);
-            c += sample(input, scaled_xy + float2( in_blurOffset.x,  in_blurOffset.y));
-            c += sample(input, scaled_xy + float2( in_blurOffset.x, -in_blurOffset.y));
-            c += sample(input, scaled_xy + float2(-in_blurOffset.x,  in_blurOffset.y));
-            c += sample(input, scaled_xy + float2(-in_blurOffset.x, -in_blurOffset.y));
+            half4 c = sample(input, xy);
+            c += sample(input, xy + float2( in_blurOffset.x,  in_blurOffset.y));
+            c += sample(input, xy + float2( in_blurOffset.x, -in_blurOffset.y));
+            c += sample(input, xy + float2(-in_blurOffset.x,  in_blurOffset.y));
+            c += sample(input, xy + float2(-in_blurOffset.x, -in_blurOffset.y));
 
             return half4(c.rgb * 0.2, 1.0);
         }
@@ -55,10 +52,26 @@
         LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
     }
     mBlurEffect = std::move(blurEffect);
+
+    SkString mixString(R"(
+        in shader blurredInput;
+        in shader originalInput;
+        uniform float mixFactor;
+
+        half4 main(float2 xy) {
+            return half4(mix(sample(originalInput), sample(blurredInput), mixFactor));
+        }
+    )");
+
+    auto [mixEffect, mixError] = SkRuntimeEffect::Make(mixString);
+    if (!mixEffect) {
+        LOG_ALWAYS_FATAL("RuntimeShader error: %s", mixError.c_str());
+    }
+    mMixEffect = std::move(mixEffect);
 }
 
-sk_sp<SkImage> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> input,
-                                    const uint32_t blurRadius, SkRect rect) const {
+sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
+                                    const sk_sp<SkImage> input, const SkRect& blurRect) const {
     // Kawase is an approximation of Gaussian, but it behaves differently from it.
     // A radius transformation is required for approximating them, and also to introduce
     // non-integer steps, necessary to smoothly interpolate large radii.
@@ -66,40 +79,110 @@
     float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
     float radiusByPasses = tmpRadius / (float)numberOfPasses;
 
-    SkImageInfo scaledInfo = SkImageInfo::MakeN32Premul((float)rect.width() * kInputScale,
-                                                        (float)rect.height() * kInputScale);
+    // create blur surface with the bit depth and colorspace of the original surface
+    SkImageInfo scaledInfo = input->imageInfo().makeWH(blurRect.width() * kInputScale,
+                                                       blurRect.height() * kInputScale);
 
     const float stepX = radiusByPasses;
     const float stepY = radiusByPasses;
 
-    // start by drawing and downscaling and doing the first blur pass
+    // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
+    // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale)
+    // but instead we must do the inverse.
+    SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
+    blurMatrix.postScale(kInputScale, kInputScale);
+
+    // start by downscaling and doing the first blur pass
     SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
     SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
     blurBuilder.child("input") =
-            input->makeImageSnapshot(rect.round())
-                    ->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
-    blurBuilder.uniform("in_inverseScale") = kInverseInputScale;
-    blurBuilder.uniform("in_blurOffset") =
-            SkV2{stepX * kInverseInputScale, stepY * kInverseInputScale};
+            input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
+    blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInputScale, stepY * kInputScale};
 
-    sk_sp<SkImage> tmpBlur(
-            blurBuilder.makeImage(canvas->recordingContext(), nullptr, scaledInfo, false));
+    sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
 
     // And now we'll build our chain of scaled blur stages
-    blurBuilder.uniform("in_inverseScale") = 1.0f;
     for (auto i = 1; i < numberOfPasses; i++) {
         const float stepScale = (float)i * kInputScale;
         blurBuilder.child("input") =
                 tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
         blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
-        tmpBlur = blurBuilder.makeImage(canvas->recordingContext(), nullptr, scaledInfo, false);
+        tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
     }
 
     return tmpBlur;
 }
 
-SkMatrix BlurFilter::getShaderMatrix() const {
-    return SkMatrix::Scale(kInverseInputScale, kInverseInputScale);
+static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRect, float scale) {
+    // 1. Apply the blur shader matrix, which scales up the blured surface to its real size
+    auto matrix = SkMatrix::Scale(scale, scale);
+    // 2. Since the blurred surface has the size of the layer, we align it with the
+    // top left corner of the layer position.
+    matrix.postConcat(SkMatrix::Translate(blurRect.fLeft, blurRect.fTop));
+    // 3. Finally, apply the inverse canvas matrix. The snapshot made in the BlurFilter is in the
+    // original surface orientation. The inverse matrix has to be applied to align the blur
+    // surface with the current orientation/position of the canvas.
+    SkMatrix drawInverse;
+    if (canvas != nullptr && canvas->getTotalMatrix().invert(&drawInverse)) {
+        matrix.postConcat(drawInverse);
+    }
+    return matrix;
+}
+
+void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
+                                const SkRect& blurRect, sk_sp<SkImage> blurredImage,
+                                sk_sp<SkImage> input) {
+    ATRACE_CALL();
+
+    SkPaint paint;
+    paint.setAlphaf(effectRegion.alpha);
+    if (effectRegion.alpha == 1.0f) {
+        paint.setBlendMode(SkBlendMode::kSrc);
+    }
+
+    const auto blurMatrix = getShaderTransform(canvas, blurRect, kInverseInputScale);
+    SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone);
+    const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+                                                     linearSampling, &blurMatrix);
+
+    if (effectRegion.blurRadius < kMaxCrossFadeRadius) {
+        // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
+        // case you might expect the matrix to simply be the canvas matrix.
+        SkMatrix inputMatrix;
+        if (!canvas->getTotalMatrix().invert(&inputMatrix)) {
+            ALOGE("matrix was unable to be inverted");
+        }
+
+        SkRuntimeShaderBuilder blurBuilder(mMixEffect);
+        blurBuilder.child("blurredInput") = blurShader;
+        blurBuilder.child("originalInput") =
+                input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
+                                  inputMatrix);
+        blurBuilder.uniform("mixFactor") = effectRegion.blurRadius / kMaxCrossFadeRadius;
+
+        paint.setShader(blurBuilder.makeShader(nullptr, true));
+    } else {
+        paint.setShader(blurShader);
+    }
+
+    // TODO we should AA at least the drawRoundRect which would mean no SRC blending
+    // TODO this round rect calculation doesn't match the one used to draw in RenderEngine
+    auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
+                                 effectRegion.bottom);
+
+    if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 ||
+        effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) {
+        const SkVector radii[4] =
+                {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL),
+                 SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR),
+                 SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL),
+                 SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)};
+        SkRRect roundedRect;
+        roundedRect.setRectRadii(rect, radii);
+        canvas->drawRRect(roundedRect, paint);
+    } else {
+        canvas->drawRect(rect, paint);
+    }
 }
 
 } // namespace skia
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 08641c9..731ba11 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -20,6 +20,7 @@
 #include <SkImage.h>
 #include <SkRuntimeEffect.h>
 #include <SkSurface.h>
+#include <ui/BlurRegion.h>
 
 using namespace std;
 
@@ -48,13 +49,15 @@
     virtual ~BlurFilter(){};
 
     // Execute blur, saving it to a texture
-    sk_sp<SkImage> generate(SkCanvas* canvas, const sk_sp<SkSurface> input, const uint32_t radius,
-                            SkRect rect) const;
-    // Returns a matrix that should be applied to the blur shader
-    SkMatrix getShaderMatrix() const;
+    sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+                            const sk_sp<SkImage> blurInput, const SkRect& blurRect) const;
+
+    void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& blurRect,
+                        sk_sp<SkImage> blurredImage, sk_sp<SkImage> input);
 
 private:
     sk_sp<SkRuntimeEffect> mBlurEffect;
+    sk_sp<SkRuntimeEffect> mMixEffect;
 };
 
 } // namespace skia
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 58afe6e..886c9da 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -950,15 +950,18 @@
     blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     blurLayer.geometry.boundaries = fullscreenRect().toFloatRect();
     blurLayer.backgroundBlurRadius = blurRadius;
+    SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this);
     blurLayer.alpha = 0;
     layers.push_back(&blurLayer);
 
     invokeDraw(settings, layers);
 
-    expectBufferColor(Rect(center - 1, center - 5, center, center + 5), 150, 150, 0, 255,
-                      50 /* tolerance */);
-    expectBufferColor(Rect(center, center - 5, center + 1, center + 5), 150, 150, 0, 255,
-                      50 /* tolerance */);
+    // solid color
+    expectBufferColor(Rect(0, 0, 1, 1), 255, 0, 0, 255, 0 /* tolerance */);
+
+    // blurred color (downsampling should result in the center color being close to 128)
+    expectBufferColor(Rect(center - 1, center - 5, center + 1, center + 5), 128, 128, 0, 255,
+                      10 /* tolerance */);
 }
 
 template <typename SourceVariant>
diff --git a/libs/ui/include/ui/StretchEffect.h b/libs/ui/include/ui/StretchEffect.h
new file mode 100644
index 0000000..1d2460c
--- /dev/null
+++ b/libs/ui/include/ui/StretchEffect.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021 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 <utils/Flattenable.h>
+#include "FloatRect.h"
+
+#include <type_traits>
+
+namespace android {
+
+struct StretchEffect : public LightFlattenablePod<StretchEffect> {
+    FloatRect area = {0, 0, 0, 0};
+    float vectorX = 0;
+    float vectorY = 0;
+    float maxAmount = 0;
+
+    bool operator==(const StretchEffect& other) const {
+        return area == other.area && vectorX == other.vectorX && vectorY == other.vectorY &&
+                maxAmount == other.maxAmount;
+    }
+
+    static bool isZero(float value) {
+        constexpr float NON_ZERO_EPSILON = 0.001f;
+        return fabsf(value) <= NON_ZERO_EPSILON;
+    }
+
+    bool isNoOp() const { return isZero(vectorX) && isZero(vectorY); }
+
+    bool hasEffect() const { return !isNoOp(); }
+
+    void sanitize() {
+        // If the area is empty, or the max amount is zero, then reset back to defaults
+        if (area.bottom >= area.top || area.left >= area.right || isZero(maxAmount)) {
+            *this = StretchEffect{};
+        }
+    }
+};
+
+static_assert(std::is_trivially_copyable<StretchEffect>::value,
+              "StretchEffect must be trivially copyable to be flattenable");
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 499f42e..45a007b 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -272,6 +272,7 @@
     std::string obscuringPackage;
     bool enabled;
     int32_t pid;
+    nsecs_t consumeTime; // time when the event was consumed by InputConsumer
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 2909939..e4237b5 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3113,7 +3113,7 @@
 
 void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
                                                 const sp<Connection>& connection, uint32_t seq,
-                                                bool handled) {
+                                                bool handled, nsecs_t consumeTime) {
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
           connection->getInputChannelName().c_str(), seq, toString(handled));
@@ -3125,7 +3125,7 @@
     }
 
     // Notify other system components and prepare to start the next dispatch cycle.
-    onDispatchCycleFinishedLocked(currentTime, connection, seq, handled);
+    onDispatchCycleFinishedLocked(currentTime, connection, seq, handled, consumeTime);
 }
 
 void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
@@ -3196,13 +3196,15 @@
             bool gotOne = false;
             status_t status;
             for (;;) {
-                uint32_t seq;
-                bool handled;
-                status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
+                std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)> callback =
+                        std::bind(&InputDispatcher::finishDispatchCycleLocked, d, currentTime,
+                                  connection, std::placeholders::_1, std::placeholders::_2,
+                                  std::placeholders::_3);
+
+                status = connection->inputPublisher.receiveFinishedSignal(callback);
                 if (status) {
                     break;
                 }
-                d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
                 gotOne = true;
             }
             if (gotOne) {
@@ -5154,13 +5156,14 @@
 
 void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
                                                     const sp<Connection>& connection, uint32_t seq,
-                                                    bool handled) {
+                                                    bool handled, nsecs_t consumeTime) {
     std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
             &InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
     commandEntry->connection = connection;
     commandEntry->eventTime = currentTime;
     commandEntry->seq = seq;
     commandEntry->handled = handled;
+    commandEntry->consumeTime = consumeTime;
     postCommandLocked(std::move(commandEntry));
 }
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index c93d74e..83094c2 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -528,7 +528,7 @@
     void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
             REQUIRES(mLock);
     void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
-                                   uint32_t seq, bool handled) REQUIRES(mLock);
+                                   uint32_t seq, bool handled, nsecs_t consumeTime) REQUIRES(mLock);
     void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
                                         bool notify) REQUIRES(mLock);
     void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
@@ -578,7 +578,8 @@
 
     // Interesting events that we might like to log or tell the framework about.
     void onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection,
-                                       uint32_t seq, bool handled) REQUIRES(mLock);
+                                       uint32_t seq, bool handled, nsecs_t consumeTime)
+            REQUIRES(mLock);
     void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
             REQUIRES(mLock);
     void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 8fc6f4a..574f651 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -520,7 +520,8 @@
 }
 
 void InputDevice::notifyReset(nsecs_t when) {
-    mContext->notifyDeviceReset(when, mId);
+    NotifyDeviceResetArgs args(mContext->getNextId(), when, mId);
+    mContext->getListener()->notifyDeviceReset(&args);
 }
 
 std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index c044393..14fb77b 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -339,7 +339,8 @@
     updateGlobalMetaStateLocked();
 
     // Enqueue configuration changed.
-    mContext.notifyConfigurationChanged(when);
+    NotifyConfigurationChangedArgs args(mContext.getNextId(), when);
+    mQueuedListener->notifyConfigurationChanged(&args);
 }
 
 void InputReader::refreshConfigurationLocked(uint32_t changes) {
@@ -366,7 +367,9 @@
     }
 
     if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) {
-        mContext.notifyPointerCaptureChanged(now, mConfig.pointerCapture);
+        const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
+                                                   mConfig.pointerCapture);
+        mQueuedListener->notifyPointerCaptureChanged(&args);
     }
 }
 
@@ -885,69 +888,16 @@
     return mReader->mPolicy.get();
 }
 
+InputListenerInterface* InputReader::ContextImpl::getListener() {
+    return mReader->mQueuedListener.get();
+}
+
 EventHubInterface* InputReader::ContextImpl::getEventHub() {
     return mReader->mEventHub.get();
 }
 
-void InputReader::ContextImpl::notifyConfigurationChanged(nsecs_t when) {
-    NotifyConfigurationChangedArgs args(mIdGenerator.nextId(), when);
-    mReader->mQueuedListener->notifyConfigurationChanged(&args);
-}
-
-void InputReader::ContextImpl::notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                                         int32_t displayId, uint32_t policyFlags, int32_t action,
-                                         int32_t flags, int32_t keyCode, int32_t scanCode,
-                                         int32_t metaState, nsecs_t downTime) {
-    NotifyKeyArgs args(mIdGenerator.nextId(), eventTime, deviceId, source, displayId, policyFlags,
-                       action, flags, keyCode, scanCode, metaState, downTime);
-    mReader->mQueuedListener->notifyKey(&args);
-}
-void InputReader::ContextImpl::notifyMotion(
-        nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
-        uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
-        int32_t metaState, int32_t buttonState, MotionClassification classification,
-        int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties,
-        const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
-        float xCursorPosition, float yCursorPosition, nsecs_t downTime,
-        const std::vector<TouchVideoFrame>& videoFrames) {
-    NotifyMotionArgs args(mIdGenerator.nextId(), eventTime, deviceId, source, displayId,
-                          policyFlags, action, actionButton, flags, metaState, buttonState,
-                          classification, edgeFlags, pointerCount, pointerProperties, pointerCoords,
-                          xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime,
-                          videoFrames);
-    mReader->mQueuedListener->notifyMotion(&args);
-}
-
-void InputReader::ContextImpl::notifySensor(nsecs_t when, int32_t deviceId,
-                                            InputDeviceSensorType sensorType,
-                                            InputDeviceSensorAccuracy accuracy,
-                                            bool accuracyChanged, nsecs_t timestamp,
-                                            std::vector<float> values) {
-    NotifySensorArgs args(mIdGenerator.nextId(), when, deviceId, AINPUT_SOURCE_SENSOR, sensorType,
-                          accuracy, accuracyChanged, timestamp, std::move(values));
-    mReader->mQueuedListener->notifySensor(&args);
-}
-
-void InputReader::ContextImpl::notifyVibratorState(nsecs_t when, int32_t deviceId, bool isOn) {
-    NotifyVibratorStateArgs args(mIdGenerator.nextId(), when, deviceId, isOn);
-    mReader->mQueuedListener->notifyVibratorState(&args);
-}
-
-void InputReader::ContextImpl::notifySwitch(nsecs_t eventTime, uint32_t switchValues,
-                                            uint32_t switchMask) {
-    NotifySwitchArgs args(mIdGenerator.nextId(), eventTime, 0 /*policyFlags*/, switchValues,
-                          switchMask);
-    mReader->mQueuedListener->notifySwitch(&args);
-}
-
-void InputReader::ContextImpl::notifyDeviceReset(nsecs_t when, int32_t deviceId) {
-    NotifyDeviceResetArgs args(mIdGenerator.nextId(), when, deviceId);
-    mReader->mQueuedListener->notifyDeviceReset(&args);
-}
-
-void InputReader::ContextImpl::notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) {
-    const NotifyPointerCaptureChangedArgs args(mIdGenerator.nextId(), when, hasCapture);
-    mReader->mQueuedListener->notifyPointerCaptureChanged(&args);
+int32_t InputReader::ContextImpl::getNextId() {
+    return mIdGenerator.nextId();
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 5f78149..81e3e9a 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -131,31 +131,11 @@
         void dispatchExternalStylusState(const StylusState& outState)
                 NO_THREAD_SAFETY_ANALYSIS override;
         InputReaderPolicyInterface* getPolicy() NO_THREAD_SAFETY_ANALYSIS override;
+        InputListenerInterface* getListener() NO_THREAD_SAFETY_ANALYSIS override;
         EventHubInterface* getEventHub() NO_THREAD_SAFETY_ANALYSIS override;
+        int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override;
         void updateLedMetaState(int32_t metaState) NO_THREAD_SAFETY_ANALYSIS override;
         int32_t getLedMetaState() NO_THREAD_SAFETY_ANALYSIS override;
-
-        // Send events to InputListener interface
-        void notifyConfigurationChanged(nsecs_t when) override;
-        void notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
-                       uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
-                       int32_t scanCode, int32_t metaState, nsecs_t downTime) override;
-        void notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
-                          uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
-                          int32_t metaState, int32_t buttonState,
-                          MotionClassification classification, int32_t edgeFlags,
-                          uint32_t pointerCount, const PointerProperties* pointerProperties,
-                          const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
-                          float xCursorPosition, float yCursorPosition, nsecs_t downTime,
-                          const std::vector<TouchVideoFrame>& videoFrames) override;
-        void notifySwitch(nsecs_t eventTime, uint32_t switchValues, uint32_t switchMask) override;
-        void notifySensor(nsecs_t when, int32_t deviceId, InputDeviceSensorType sensorType,
-                          InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
-                          nsecs_t timestamp, std::vector<float> values) override;
-        void notifyVibratorState(nsecs_t when, int32_t deviceId, bool isOn) override;
-        void notifyDeviceReset(nsecs_t when, int32_t deviceId) override;
-        void notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) override;
-
     } mContext;
 
     friend class ContextImpl;
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index e6ea523..dc807f7 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -55,34 +55,13 @@
     virtual void dispatchExternalStylusState(const StylusState& outState) = 0;
 
     virtual InputReaderPolicyInterface* getPolicy() = 0;
+    virtual InputListenerInterface* getListener() = 0;
     virtual EventHubInterface* getEventHub() = 0;
 
+    virtual int32_t getNextId() = 0;
+
     virtual void updateLedMetaState(int32_t metaState) = 0;
     virtual int32_t getLedMetaState() = 0;
-
-    // Send events to InputListener interface
-
-    virtual void notifyConfigurationChanged(nsecs_t when) = 0;
-    virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
-                           uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
-                           int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
-    virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                              int32_t displayId, uint32_t policyFlags, int32_t action,
-                              int32_t actionButton, int32_t flags, int32_t metaState,
-                              int32_t buttonState, MotionClassification classification,
-                              int32_t edgeFlags, uint32_t pointerCount,
-                              const PointerProperties* pointerProperties,
-                              const PointerCoords* pointerCoords, float xPrecision,
-                              float yPrecision, float xCursorPosition, float yCursorPosition,
-                              nsecs_t downTime,
-                              const std::vector<TouchVideoFrame>& videoFrames) = 0;
-    virtual void notifySwitch(nsecs_t eventTime, uint32_t switchValues, uint32_t switchMask) = 0;
-    virtual void notifySensor(nsecs_t when, int32_t deviceId, InputDeviceSensorType sensorType,
-                              InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
-                              nsecs_t timestamp, std::vector<float> values) = 0;
-    virtual void notifyVibratorState(nsecs_t when, int32_t deviceId, bool isOn) = 0;
-    virtual void notifyDeviceReset(nsecs_t when, int32_t deviceId) = 0;
-    virtual void notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 7f7b33c..254b64b 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -175,7 +175,8 @@
         }
         bumpGeneration();
         if (changes) {
-            getContext()->notifyDeviceReset(when, getDeviceId());
+            NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
+            getListener()->notifyDeviceReset(&args);
         }
     }
 
@@ -382,35 +383,40 @@
             while (!released.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
                 buttonState &= ~actionButton;
-                getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                           AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
-                                           metaState, buttonState, MotionClassification::NONE,
-                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                           &pointerCoords, mXPrecision, mYPrecision,
-                                           xCursorPosition, yCursorPosition, downTime,
-                                           /* videoFrames */ {});
+                NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, getDeviceId(),
+                                             mSource, displayId, policyFlags,
+                                             AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
+                                             metaState, buttonState, MotionClassification::NONE,
+                                             AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                             &pointerCoords, mXPrecision, mYPrecision,
+                                             xCursorPosition, yCursorPosition, downTime,
+                                             /* videoFrames */ {});
+                getListener()->notifyMotion(&releaseArgs);
             }
         }
 
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   motionEventAction, 0, 0, metaState, currentButtonState,
-                                   MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                   &pointerProperties, &pointerCoords, mXPrecision, mYPrecision,
-                                   xCursorPosition, yCursorPosition, downTime,
-                                   /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, motionEventAction, 0, 0, metaState, currentButtonState,
+                              MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                              &pointerProperties, &pointerCoords, mXPrecision, mYPrecision,
+                              xCursorPosition, yCursorPosition, downTime,
+                              /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
 
         if (buttonsPressed) {
             BitSet32 pressed(buttonsPressed);
             while (!pressed.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
                 buttonState |= actionButton;
-                getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
+                NotifyMotionArgs pressArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
+                                           displayId, policyFlags,
                                            AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
                                            metaState, buttonState, MotionClassification::NONE,
                                            AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                            &pointerCoords, mXPrecision, mYPrecision,
                                            xCursorPosition, yCursorPosition, downTime,
                                            /* videoFrames */ {});
+                getListener()->notifyMotion(&pressArgs);
             }
         }
 
@@ -418,12 +424,13 @@
 
         // Send hover move after UP to tell the application that the mouse is hovering now.
         if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
-            getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                       AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                                       currentButtonState, MotionClassification::NONE,
+            NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
+                                       displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
+                                       0, metaState, currentButtonState, MotionClassification::NONE,
                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                        &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
                                        yCursorPosition, downTime, /* videoFrames */ {});
+            getListener()->notifyMotion(&hoverArgs);
         }
 
         // Send scroll events.
@@ -431,12 +438,13 @@
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-            getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                       AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
-                                       currentButtonState, MotionClassification::NONE,
-                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                       &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
-                                       yCursorPosition, downTime, /* videoFrames */ {});
+            NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
+                                        displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
+                                        metaState, currentButtonState, MotionClassification::NONE,
+                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                        &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
+                                        yCursorPosition, downTime, /* videoFrames */ {});
+            getListener()->notifyMotion(&scrollArgs);
         }
     }
 
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 44af998..1cc5979 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -19,6 +19,7 @@
 
 #include "EventHub.h"
 #include "InputDevice.h"
+#include "InputListener.h"
 #include "InputReaderContext.h"
 #include "StylusState.h"
 #include "VibrationElement.h"
@@ -47,6 +48,7 @@
     inline const std::string getDeviceName() { return mDeviceContext.getName(); }
     inline InputReaderContext* getContext() { return mDeviceContext.getContext(); }
     inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); }
+    inline InputListenerInterface* getListener() { return getContext()->getListener(); }
 
     virtual uint32_t getSources() = 0;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 28f29e0..37aa140 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -337,12 +337,13 @@
     // TODO: Use the input device configuration to control this behavior more finely.
     uint32_t policyFlags = 0;
 
-    getContext()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE,
-                               policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
-                               MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                               &pointerProperties, &pointerCoords, 0, 0,
-                               AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                               AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
+    NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_JOYSTICK,
+                          ADISPLAY_ID_NONE, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
+                          buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                          &pointerProperties, &pointerCoords, 0, 0,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
+    getListener()->notifyMotion(&args);
 }
 
 void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 03d7405..8b9f235 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -350,9 +350,10 @@
         policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
     }
 
-    getContext()->notifyKey(when, getDeviceId(), mSource, getDisplayId(), policyFlags,
-                            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
-                            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
+    NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, getDisplayId(),
+                       policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+                       AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
+    getListener()->notifyKey(&args);
 }
 
 ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 3f8a364..594ff42 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -121,12 +121,13 @@
         int32_t metaState = getContext()->getGlobalMetaState();
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
 
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
-                                   /* buttonState */ 0, MotionClassification::NONE,
-                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
-                                   &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                   AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
+        NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
+                                    displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
+                                    metaState, /* buttonState */ 0, MotionClassification::NONE,
+                                    AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                    &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                    AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
+        getListener()->notifyMotion(&scrollArgs);
     }
 
     mRotaryEncoderScrollAccumulator.finishSync();
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index 68c1e40..7ac2dec 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -405,10 +405,13 @@
             // Convert to Android unit
             convertFromLinuxToAndroid(values, sensorType);
             // Notify dispatcher for sensor event
-            getContext()->notifySensor(when, getDeviceId(), sensorType, sensor.sensorInfo.accuracy,
-                                       sensor.accuracy !=
-                                               sensor.sensorInfo.accuracy /* accuracyChanged */,
-                                       timestamp /* hwTimestamp */, std::move(values));
+            NotifySensorArgs args(getContext()->getNextId(), when, getDeviceId(),
+                                  AINPUT_SOURCE_SENSOR, sensorType, sensor.sensorInfo.accuracy,
+                                  sensor.accuracy !=
+                                          sensor.sensorInfo.accuracy /* accuracyChanged */,
+                                  timestamp /* hwTimestamp */, values);
+
+            getListener()->notifySensor(&args);
             sensor.lastSampleTimeNs = timestamp;
             sensor.accuracy = sensor.sensorInfo.accuracy;
         }
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index 07de244..4f73681 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -56,7 +56,10 @@
 void SwitchInputMapper::sync(nsecs_t when) {
     if (mUpdatedSwitchMask) {
         uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
-        getContext()->notifySwitch(when, updatedSwitchValues, mUpdatedSwitchMask);
+        NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/,
+                              updatedSwitchValues, mUpdatedSwitchMask);
+        getListener()->notifySwitch(&args);
+
         mUpdatedSwitchMask = 0;
     }
 }
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index ff6341f..a86443d 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -66,8 +66,9 @@
          (currentButtonState & buttonState)) ||
         (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
          !(currentButtonState & buttonState))) {
-        context->notifyKey(when, deviceId, source, displayId, policyFlags, action, 0 /*flags*/,
-                           keyCode, 0 /*scanCode*/, context->getGlobalMetaState(), when);
+        NotifyKeyArgs args(context->getNextId(), when, deviceId, source, displayId, policyFlags,
+                           action, 0, keyCode, 0, context->getGlobalMetaState(), when);
+        context->getListener()->notifyKey(&args);
     }
 }
 
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 1a17bef..d1df37b 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -397,7 +397,8 @@
     if (changes && resetNeeded) {
         // Send reset, unless this is the first time the device has been configured,
         // in which case the reader will call reset itself after all mappers are ready.
-        getContext()->notifyDeviceReset(when, getDeviceId());
+        NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
+        getListener()->notifyDeviceReset(&args);
     }
 }
 
@@ -1845,9 +1846,10 @@
     int32_t metaState = getContext()->getGlobalMetaState();
     policyFlags |= POLICY_FLAG_VIRTUAL;
 
-    getContext()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, mViewport.displayId,
-                            policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode,
-                            metaState, downTime);
+    NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
+                       mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode,
+                       scanCode, metaState, downTime);
+    getListener()->notifyKey(&args);
 }
 
 void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) {
@@ -2526,11 +2528,12 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
 
         const int32_t displayId = mPointerController->getDisplayId();
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, buttonState,
-                                   MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                   &pointerProperties, &pointerCoords, 0, 0, x, y,
-                                   mPointerGesture.downTime, /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+                              buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+                              1, &pointerProperties, &pointerCoords, 0, 0, x, y,
+                              mPointerGesture.downTime, /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
     }
 
     // Update state.
@@ -3445,28 +3448,28 @@
         mPointerSimple.down = false;
 
         // Send up.
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
-                                   mLastRawState.buttonState, MotionClassification::NONE,
-                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
-                                   &mPointerSimple.lastCoords, mOrientedXPrecision,
-                                   mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                                   mPointerSimple.downTime,
-                                   /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
+                              mLastRawState.buttonState, MotionClassification::NONE,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+                              &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
+                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
+                              /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
     }
 
     if (mPointerSimple.hovering && !hovering) {
         mPointerSimple.hovering = false;
 
         // Send hover exit.
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
-                                   mLastRawState.buttonState, MotionClassification::NONE,
-                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
-                                   &mPointerSimple.lastCoords, mOrientedXPrecision,
-                                   mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                                   mPointerSimple.downTime,
-                                   /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
+                              mLastRawState.buttonState, MotionClassification::NONE,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+                              &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
+                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
+                              /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
     }
 
     if (down) {
@@ -3475,24 +3478,25 @@
             mPointerSimple.downTime = when;
 
             // Send down.
-            getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                       AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState,
-                                       mCurrentRawState.buttonState, MotionClassification::NONE,
-                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                       &mPointerSimple.currentProperties,
-                                       &mPointerSimple.currentCoords, mOrientedXPrecision,
-                                       mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                                       mPointerSimple.downTime, /* videoFrames */ {});
+            NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
+                                  displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+                                  metaState, mCurrentRawState.buttonState,
+                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
+            getListener()->notifyMotion(&args);
         }
 
         // Send move.
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
-                                   mCurrentRawState.buttonState, MotionClassification::NONE,
-                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                   &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                                   mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                   yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
+                              mCurrentRawState.buttonState, MotionClassification::NONE,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
+                              &mPointerSimple.currentCoords, mOrientedXPrecision,
+                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                              mPointerSimple.downTime, /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
     }
 
     if (hovering) {
@@ -3500,24 +3504,25 @@
             mPointerSimple.hovering = true;
 
             // Send hover enter.
-            getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                       AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
-                                       mCurrentRawState.buttonState, MotionClassification::NONE,
-                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                       &mPointerSimple.currentProperties,
-                                       &mPointerSimple.currentCoords, mOrientedXPrecision,
-                                       mOrientedYPrecision, xCursorPosition, yCursorPosition,
-                                       mPointerSimple.downTime, /* videoFrames */ {});
+            NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
+                                  displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
+                                  metaState, mCurrentRawState.buttonState,
+                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
+            getListener()->notifyMotion(&args);
         }
 
         // Send hover move.
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                                   mCurrentRawState.buttonState, MotionClassification::NONE,
-                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                   &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                                   mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                   yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+                              mCurrentRawState.buttonState, MotionClassification::NONE,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
+                              &mPointerSimple.currentCoords, mOrientedXPrecision,
+                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                              mPointerSimple.downTime, /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
     }
 
     if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
@@ -3532,14 +3537,14 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-        getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags,
-                                   AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
-                                   mCurrentRawState.buttonState, MotionClassification::NONE,
-                                   AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                                   &mPointerSimple.currentProperties, &pointerCoords,
-                                   mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
-                                   yCursorPosition, mPointerSimple.downTime,
-                                   /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+                              mCurrentRawState.buttonState, MotionClassification::NONE,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
+                              &pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
+                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
+                              /* videoFrames */ {});
+        getListener()->notifyMotion(&args);
     }
 
     // Save state.
@@ -3610,11 +3615,12 @@
     std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
     std::for_each(frames.begin(), frames.end(),
                   [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
-    getContext()->notifyMotion(when, deviceId, source, displayId, policyFlags, action, actionButton,
-                               flags, metaState, buttonState, MotionClassification::NONE, edgeFlags,
-                               pointerCount, pointerProperties, pointerCoords, xPrecision,
-                               yPrecision, xCursorPosition, yCursorPosition, downTime,
-                               std::move(frames));
+    NotifyMotionArgs args(getContext()->getNextId(), when, deviceId, source, displayId, policyFlags,
+                          action, actionButton, flags, metaState, buttonState,
+                          MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
+                          pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
+                          downTime, std::move(frames));
+    getListener()->notifyMotion(&args);
 }
 
 bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 2e4ab45..3df6f36 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -53,7 +53,8 @@
     mIndex = -1;
 
     // Request InputReader to notify InputManagerService for vibration started.
-    getContext()->notifyVibratorState(systemTime(), getDeviceId(), true);
+    NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), true);
+    getListener()->notifyVibratorState(&args);
     nextStep();
 }
 
@@ -131,7 +132,8 @@
     getDeviceContext().cancelVibrate();
 
     // Request InputReader to notify InputManagerService for vibration complete.
-    getContext()->notifyVibratorState(systemTime(), getDeviceId(), false);
+    NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), false);
+    getListener()->notifyVibratorState(&args);
 }
 
 void VibratorInputMapper::dump(std::string& dump) {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index bea9932..409c62a 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -409,7 +409,7 @@
 
     KeyedVector<int32_t, Device*> mDevices;
     std::vector<std::string> mExcludedDevices;
-    List<RawEvent> mEvents GUARDED_BY(mLock);
+    std::vector<RawEvent> mEvents GUARDED_BY(mLock);
     std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
     std::vector<int32_t> mVibrators = {0, 1};
 
@@ -722,16 +722,15 @@
         mExcludedDevices = devices;
     }
 
-    size_t getEvents(int, RawEvent* buffer, size_t) override {
-        std::scoped_lock<std::mutex> lock(mLock);
-        if (mEvents.empty()) {
-            return 0;
-        }
+    size_t getEvents(int, RawEvent* buffer, size_t bufferSize) override {
+        std::scoped_lock lock(mLock);
 
-        *buffer = *mEvents.begin();
-        mEvents.erase(mEvents.begin());
+        const size_t filledSize = std::min(mEvents.size(), bufferSize);
+        std::copy(mEvents.begin(), mEvents.begin() + filledSize, buffer);
+
+        mEvents.erase(mEvents.begin(), mEvents.begin() + filledSize);
         mEventsCondition.notify_all();
-        return 1;
+        return filledSize;
     }
 
     std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override {
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index cf87f62..579130a 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -35,6 +35,7 @@
 #include <renderengine/Image.h>
 
 #include "EffectLayer.h"
+#include "FrameTracer/FrameTracer.h"
 #include "TimeStats/TimeStats.h"
 
 namespace android {
@@ -337,7 +338,7 @@
 bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
                                  nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp,
                                  const client_cache_t& clientCacheId, uint64_t frameNumber,
-                                 std::optional<nsecs_t> /* dequeueTime */,
+                                 std::optional<nsecs_t> dequeueTime,
                                  const FrameTimelineInfo& info) {
     ATRACE_CALL();
 
@@ -377,6 +378,14 @@
         setFrameTimelineVsyncForBufferTransaction(info, postTime);
     }
 
+    if (dequeueTime && *dequeueTime != 0) {
+        const uint64_t bufferId = buffer->getId();
+        mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+        mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime,
+                                               FrameTracer::FrameEvent::DEQUEUE);
+        mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, postTime,
+                                               FrameTracer::FrameEvent::QUEUE);
+    }
     return true;
 }
 
@@ -452,6 +461,7 @@
         if (willPresent) {
             // If this transaction set an acquire fence on this layer, set its acquire time
             handle->acquireTime = mCallbackHandleAcquireTime;
+            handle->frameNumber = mCurrentState.frameNumber;
 
             // Notify the transaction completed thread that there is a pending latched callback
             // handle
@@ -623,14 +633,22 @@
     }
 
     for (auto& handle : mDrawingState.callbackHandles) {
-        handle->latchTime = latchTime;
-        handle->frameNumber = mDrawingState.frameNumber;
+        if (handle->frameNumber == mDrawingState.frameNumber) {
+            handle->latchTime = latchTime;
+        }
     }
 
     const int32_t layerId = getSequence();
-    mFlinger->mTimeStats->setAcquireFence(layerId, mDrawingState.frameNumber,
-                                          std::make_shared<FenceTime>(mDrawingState.acquireFence));
-    mFlinger->mTimeStats->setLatchTime(layerId, mDrawingState.frameNumber, latchTime);
+    const uint64_t bufferId = mDrawingState.buffer->getId();
+    const uint64_t frameNumber = mDrawingState.frameNumber;
+    const auto acquireFence = std::make_shared<FenceTime>(mDrawingState.acquireFence);
+    mFlinger->mTimeStats->setAcquireFence(layerId, frameNumber, acquireFence);
+    mFlinger->mTimeStats->setLatchTime(layerId, frameNumber, latchTime);
+
+    mFlinger->mFrameTracer->traceFence(layerId, bufferId, frameNumber, acquireFence,
+                                       FrameTracer::FrameEvent::ACQUIRE_FENCE);
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, latchTime,
+                                           FrameTracer::FrameEvent::LATCH);
 
     auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX;
     if (bufferSurfaceFrame != nullptr &&
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index ea832a2..175a40b 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -124,6 +124,7 @@
 
 private:
     friend class SlotGenerationTest;
+    friend class TransactionFrameTracerTest;
     friend class TransactionSurfaceFrameTest;
 
     inline void tracePendingBufferCount();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index c445d5b..8402149 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -34,6 +34,7 @@
 #include <gui/BufferQueue.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicTypes.h>
+#include <ui/StretchEffect.h>
 
 #include "DisplayHardware/Hal.h"
 
@@ -123,6 +124,8 @@
     // List of regions that require blur
     std::vector<BlurRegion> blurRegions;
 
+    StretchEffect stretchEffect;
+
     /*
      * Geometry state
      */
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 4f99495..3907ac5 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -989,9 +989,16 @@
                    });
 
     const nsecs_t renderEngineStart = systemTime();
+    // Only use the framebuffer cache when rendering to an internal display
+    // TODO(b/173560331): This is only to help mitigate memory leaks from virtual displays because
+    // right now we don't have a concrete eviction policy for output buffers: GLESRenderEngine
+    // bounds its framebuffer cache but Skia RenderEngine has no current policy. The best fix is
+    // probably to encapsulate the output buffer into a structure that dispatches resource cleanup
+    // over to RenderEngine, in which case this flag can be removed from the drawLayers interface.
+    const bool useFramebufferCache = outputState.layerStackInternal;
     status_t status =
             renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buf,
-                                    /*useFramebufferCache=*/true, std::move(fd), &readyFence);
+                                    useFramebufferCache, std::move(fd), &readyFence);
 
     if (status != NO_ERROR && mClientCompositionRequestCache) {
         // If rendering was not successful, remove the request from the cache.
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 376bac8..c4ae3a7 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3003,7 +3003,7 @@
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, true, _, _))
+    EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, false, _, _))
             .WillRepeatedly(Return(NO_ERROR));
 
     verify().execute().expectAFenceWasReturned();
@@ -3030,6 +3030,36 @@
                     }));
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
+            .WillRepeatedly(Return(NO_ERROR));
+
+    verify().execute().expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest,
+       buildsAndRendersRequestListAndCachesFramebufferForInternalLayers) {
+    LayerFE::LayerSettings r1;
+    LayerFE::LayerSettings r2;
+
+    r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+    r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+    const constexpr uint32_t kInternalLayerStack = 1234;
+    mOutput.setLayerStackFilter(kInternalLayerStack, true);
+
+    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+            .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(
+                    Invoke([&](const Region&,
+                               std::vector<LayerFE::LayerSettings>& clientCompositionLayers) {
+                        clientCompositionLayers.emplace_back(r2);
+                    }));
+
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
     EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
             .WillRepeatedly(Return(NO_ERROR));
 
@@ -3054,7 +3084,7 @@
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
             .Times(2)
             .WillOnce(Return(NO_ERROR));
 
@@ -3083,7 +3113,7 @@
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
 
@@ -3115,7 +3145,7 @@
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_))
             .WillOnce(Return(mOutputBuffer))
             .WillOnce(Return(otherOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
             .WillRepeatedly(Return(NO_ERROR));
 
     verify().execute().expectAFenceWasReturned();
@@ -3145,9 +3175,9 @@
             .WillRepeatedly(Return());
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
             .WillOnce(Return(NO_ERROR));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r3)), _, true, _, _))
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r3)), _, false, _, _))
             .WillOnce(Return(NO_ERROR));
 
     verify().execute().expectAFenceWasReturned();
@@ -3197,7 +3227,7 @@
     struct ExpectDisplaySettingsState
           : public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> {
         auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) {
-            EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, true, _, _))
+            EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _, _))
                     .WillOnce(Return(NO_ERROR));
             return nextState<ExecuteState>();
         }
@@ -3296,7 +3326,7 @@
         EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
                 .WillRepeatedly(Return());
         EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _))
+        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _))
                 .WillRepeatedly(Return(NO_ERROR));
     }
 
@@ -3349,7 +3379,7 @@
     EXPECT_CALL(*mRenderSurface, setProtected(true));
     // Must happen after setting the protected content state.
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
 
     mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
@@ -3419,7 +3449,7 @@
     InSequence seq;
 
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
 
     mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
 }
@@ -3434,7 +3464,7 @@
         EXPECT_CALL(mLayer.outputLayer, writeStateToHWC(false));
         EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
                 .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
-        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
         EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
         EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
                 .WillRepeatedly(Return(&mLayer.outputLayer));
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index a785968..c751f22 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -265,7 +265,8 @@
     StringAppendF(&result, "+ %s\n", getDebugName().c_str());
     StringAppendF(&result, "   powerMode=%s (%d)\n", to_string(mPowerMode).c_str(),
                   static_cast<int32_t>(mPowerMode));
-    StringAppendF(&result, "   activeMode=%s\n", to_string(*getActiveMode()).c_str());
+    StringAppendF(&result, "   activeMode=%s\n",
+                  mSupportedModes.size() ? to_string(*getActiveMode()).c_str() : "none");
 
     result.append("   supportedModes=\n");
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index f271df8..6a28da3 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -488,6 +488,7 @@
     compositionState->alpha = alpha;
     compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
     compositionState->blurRegions = drawingState.blurRegions;
+    compositionState->stretchEffect = drawingState.stretchEffect;
 }
 
 void Layer::prepareGeometryCompositionState() {
@@ -556,8 +557,8 @@
             isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
 
     // Force client composition for special cases known only to the front-end.
-    if (isHdrY410() || usesRoundedCorners || drawShadows() ||
-        getDrawingState().blurRegions.size() > 0) {
+    if (isHdrY410() || usesRoundedCorners || drawShadows() || drawingState.blurRegions.size() > 0 ||
+        drawingState.stretchEffect.hasEffect()) {
         compositionState->forceClientComposition = true;
     }
 }
@@ -656,6 +657,7 @@
         layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
         layerSettings.blurRegions = getBlurRegions();
     }
+    layerSettings.stretchEffect = getDrawingState().stretchEffect;
     // Record the name of the layer for debugging further down the stack.
     layerSettings.name = getName();
     return layerSettings;
@@ -867,9 +869,7 @@
 void Layer::popPendingState(State* stateToCommit) {
     ATRACE_CALL();
 
-    if (stateToCommit != nullptr) {
-        mergeSurfaceFrames(*stateToCommit, mPendingStates[0]);
-    }
+    mergeSurfaceFrames(*stateToCommit, mPendingStates[0]);
     *stateToCommit = mPendingStates[0];
     mPendingStates.pop_front();
     ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
@@ -1423,6 +1423,19 @@
     return true;
 }
 
+bool Layer::setStretchEffect(const StretchEffect& effect) {
+    StretchEffect temp = effect;
+    temp.sanitize();
+    if (mCurrentState.stretchEffect == temp) {
+        return false;
+    }
+    mCurrentState.sequence++;
+    mCurrentState.stretchEffect = temp;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
 void Layer::updateTreeHasFrameRateVote() {
     const auto traverseTree = [&](const LayerVector::Visitor& visitor) {
         auto parent = getParent();
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 36ae19c..a1fdc3c 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -32,6 +32,7 @@
 #include <ui/GraphicBuffer.h>
 #include <ui/PixelFormat.h>
 #include <ui/Region.h>
+#include <ui/StretchEffect.h>
 #include <ui/Transform.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
@@ -323,6 +324,9 @@
         // An arbitrary threshold for the number of BufferlessSurfaceFrames in the state. Used to
         // trigger a warning if the number of SurfaceFrames crosses the threshold.
         static constexpr uint32_t kStateSurfaceFramesThreshold = 25;
+
+        // Stretch effect to apply to this layer
+        StretchEffect stretchEffect;
     };
 
     /*
@@ -938,6 +942,8 @@
 
     bool backpressureEnabled() { return mDrawingState.flags & layer_state_t::eEnableBackpressure; }
 
+    bool setStretchEffect(const StretchEffect& effect);
+
 protected:
     class SyncPoint {
     public:
@@ -1004,6 +1010,7 @@
     friend class TestableSurfaceFlinger;
     friend class RefreshRateSelectionTest;
     friend class SetFrameRateTest;
+    friend class TransactionFrameTracerTest;
     friend class TransactionSurfaceFrameTest;
 
     virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom);
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index bfe9b4a..ba43e70 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -303,10 +303,6 @@
     std::lock_guard<std::mutex> lock(mMutex);
 
     const auto request = rate == 0 ? VSyncRequest::None : static_cast<VSyncRequest>(rate);
-    if (request != VSyncRequest::None && connection->resyncCallback) {
-        connection->resyncCallback();
-    }
-
     if (connection->vsyncRequest != request) {
         connection->vsyncRequest = request;
         mCondition.notify_all();
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4f274b6..7fada82 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3950,6 +3950,11 @@
     if (what & layer_state_t::eAutoRefreshChanged) {
         layer->setAutoRefresh(s.autoRefresh);
     }
+    if (what & layer_state_t::eStretchChanged) {
+        if (layer->setStretchEffect(s.stretchEffect)) {
+            flags |= eTraversalNeeded;
+        }
+    }
     // This has to happen after we reparent children because when we reparent to null we remove
     // child layers from current state and remove its relative z. If the children are reparented in
     // the same transaction, then we have to make sure we reparent the children first so we do not
@@ -5030,8 +5035,9 @@
         case CAPTURE_DISPLAY:
         case SET_DISPLAY_BRIGHTNESS:
         case SET_FRAME_TIMELINE_INFO:
-        // This is not sensitive information, so should not require permission control.
-        case GET_GPU_CONTEXT_PRIORITY: {
+        case GET_GPU_CONTEXT_PRIORITY:
+        case GET_EXTRA_BUFFER_COUNT: {
+            // This is not sensitive information, so should not require permission control.
             return OK;
         }
         case ADD_REGION_SAMPLING_LISTENER:
@@ -6456,6 +6462,25 @@
     return getRenderEngine().getContextPriority();
 }
 
+int SurfaceFlinger::calculateExtraBufferCount(Fps maxSupportedRefreshRate,
+                                              std::chrono::nanoseconds presentLatency) {
+    auto pipelineDepth = presentLatency.count() / maxSupportedRefreshRate.getPeriodNsecs();
+    if (presentLatency.count() % maxSupportedRefreshRate.getPeriodNsecs()) {
+        pipelineDepth++;
+    }
+    return std::max(0ll, pipelineDepth - 2);
+}
+
+status_t SurfaceFlinger::getExtraBufferCount(int* extraBuffers) const {
+    const auto maxSupportedRefreshRate = mRefreshRateConfigs->getSupportedRefreshRateRange().max;
+    const auto vsyncConfig =
+            mVsyncConfiguration->getConfigsForRefreshRate(maxSupportedRefreshRate).late;
+    const auto presentLatency = vsyncConfig.appWorkDuration + vsyncConfig.sfWorkDuration;
+
+    *extraBuffers = calculateExtraBufferCount(maxSupportedRefreshRate, presentLatency);
+    return NO_ERROR;
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ed5b098..a62d0b9 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -622,6 +622,8 @@
 
     int getGPUContextPriority() override;
 
+    status_t getExtraBufferCount(int* extraBuffers) const override;
+
     // Implements IBinder::DeathRecipient.
     void binderDied(const wp<IBinder>& who) override;
 
@@ -1052,6 +1054,9 @@
         return std::nullopt;
     }
 
+    static int calculateExtraBufferCount(Fps maxSupportedRefreshRate,
+                                         std::chrono::nanoseconds presentLatency);
+
     sp<StartPropertySetThread> mStartPropertySetThread;
     surfaceflinger::Factory& mFactory;
 
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 17928a0..e55821f 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -73,6 +73,7 @@
         "FrameTracerTest.cpp",
         "TimerTest.cpp",
         "TransactionApplicationTest.cpp",
+        "TransactionFrameTracerTest.cpp",
         "TransactionSurfaceFrameTest.cpp",
         "StrongTypingTest.cpp",
         "VSyncDispatchTimerQueueTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 694790f..e46a270 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -69,6 +69,8 @@
     Scheduler::ConnectionHandle mConnectionHandle;
     mock::EventThread* mEventThread;
     sp<MockEventThreadConnection> mEventThreadConnection;
+
+    TestableSurfaceFlinger mFlinger;
 };
 
 SchedulerTest::SchedulerTest() {
@@ -187,4 +189,14 @@
     EXPECT_CALL(*mEventThread, onModeChanged(_, _, _)).Times(0);
 }
 
+TEST_F(SchedulerTest, calculateExtraBufferCount) {
+    EXPECT_EQ(0, mFlinger.calculateExtraBufferCount(Fps(60), 30ms));
+    EXPECT_EQ(1, mFlinger.calculateExtraBufferCount(Fps(90), 30ms));
+    EXPECT_EQ(2, mFlinger.calculateExtraBufferCount(Fps(120), 30ms));
+
+    EXPECT_EQ(1, mFlinger.calculateExtraBufferCount(Fps(60), 40ms));
+
+    EXPECT_EQ(0, mFlinger.calculateExtraBufferCount(Fps(60), 10ms));
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 6aa6d31..dee13d6 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -391,6 +391,11 @@
 
     auto getGPUContextPriority() { return mFlinger->getGPUContextPriority(); }
 
+    auto calculateExtraBufferCount(Fps maxSupportedRefreshRate,
+                                   std::chrono::nanoseconds presentLatency) const {
+        return SurfaceFlinger::calculateExtraBufferCount(maxSupportedRefreshRate, presentLatency);
+    }
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
@@ -405,6 +410,10 @@
 
     const auto& getCompositorTiming() const { return mFlinger->getBE().mCompositorTiming; }
 
+    mock::FrameTracer* getFrameTracer() const {
+        return static_cast<mock::FrameTracer*>(mFlinger->mFrameTracer.get());
+    }
+
     /* ------------------------------------------------------------------------
      * Read-write access to private data to set up preconditions and assert
      * post-conditions.
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
new file mode 100644
index 0000000..dbadf75
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/SurfaceComposerClient.h>
+#include <log/log.h>
+#include <utils/String8.h>
+
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockVsyncController.h"
+
+namespace android {
+
+using testing::_;
+using testing::Mock;
+using testing::Return;
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using PresentState = frametimeline::SurfaceFrame::PresentState;
+
+class TransactionFrameTracerTest : public testing::Test {
+public:
+    TransactionFrameTracerTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+        setupScheduler();
+        setupComposer(0);
+    }
+
+    ~TransactionFrameTracerTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    sp<BufferStateLayer> createBufferStateLayer() {
+        sp<Client> client;
+        LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0,
+                               LayerMetadata());
+        return new BufferStateLayer(args);
+    }
+
+    void commitTransaction(Layer* layer) {
+        layer->pushPendingState();
+        auto c = layer->getCurrentState();
+        if (layer->applyPendingStates(&c)) {
+            layer->commitTransaction(c);
+        }
+    }
+
+    void setupScheduler() {
+        auto eventThread = std::make_unique<mock::EventThread>();
+        auto sfEventThread = std::make_unique<mock::EventThread>();
+
+        EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*eventThread, createEventConnection(_, _))
+                .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+                                                           ResyncCallback())));
+
+        EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+                .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+                                                           ResyncCallback())));
+
+        auto vsyncController = std::make_unique<mock::VsyncController>();
+        auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*vsyncTracker, currentPeriod())
+                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+        mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+                                std::move(eventThread), std::move(sfEventThread));
+    }
+
+    void setupComposer(uint32_t virtualDisplayCount) {
+        mComposer = new Hwc2::mock::Composer();
+        EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+        mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+        Mock::VerifyAndClear(mComposer);
+    }
+
+    TestableSurfaceFlinger mFlinger;
+    Hwc2::mock::Composer* mComposer = nullptr;
+    FenceToFenceTimeMap fenceFactory;
+    client_cache_t mClientCache;
+
+    void BLASTTransactionSendsFrameTracerEvents() {
+        sp<BufferStateLayer> layer = createBufferStateLayer();
+
+        sp<Fence> fence(new Fence());
+        sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+        int32_t layerId = layer->getSequence();
+        uint64_t bufferId = buffer->getId();
+        uint64_t frameNumber = 5;
+        nsecs_t dequeueTime = 10;
+        nsecs_t postTime = 20;
+        EXPECT_CALL(*mFlinger.getFrameTracer(), traceNewLayer(layerId, "buffer-state-layer"));
+        EXPECT_CALL(*mFlinger.getFrameTracer(),
+                    traceTimestamp(layerId, bufferId, frameNumber, dequeueTime,
+                                   FrameTracer::FrameEvent::DEQUEUE, /*duration*/ 0));
+        EXPECT_CALL(*mFlinger.getFrameTracer(),
+                    traceTimestamp(layerId, bufferId, frameNumber, postTime,
+                                   FrameTracer::FrameEvent::QUEUE, /*duration*/ 0));
+        layer->setBuffer(buffer, fence, postTime, /*desiredPresentTime*/ 30, false, mClientCache,
+                         frameNumber, dequeueTime, FrameTimelineInfo{});
+
+        commitTransaction(layer.get());
+        bool computeVisisbleRegions;
+        nsecs_t latchTime = 25;
+        EXPECT_CALL(*mFlinger.getFrameTracer(),
+                    traceFence(layerId, bufferId, frameNumber, _,
+                               FrameTracer::FrameEvent::ACQUIRE_FENCE, /*startTime*/ 0));
+        EXPECT_CALL(*mFlinger.getFrameTracer(),
+                    traceTimestamp(layerId, bufferId, frameNumber, latchTime,
+                                   FrameTracer::FrameEvent::LATCH, /*duration*/ 0));
+        layer->updateTexImage(computeVisisbleRegions, latchTime, /*expectedPresentTime*/ 0);
+
+        auto glDoneFence = fenceFactory.createFenceTimeForTest(fence);
+        auto presentFence = fenceFactory.createFenceTimeForTest(fence);
+        CompositorTiming compositorTiming;
+        EXPECT_CALL(*mFlinger.getFrameTracer(),
+                    traceFence(layerId, bufferId, frameNumber, presentFence,
+                               FrameTracer::FrameEvent::PRESENT_FENCE, /*startTime*/ 0));
+        layer->onPostComposition(nullptr, glDoneFence, presentFence, compositorTiming);
+    }
+};
+
+TEST_F(TransactionFrameTracerTest, BLASTTransactionSendsFrameTracerEvents) {
+    BLASTTransactionSendsFrameTracerEvents();
+}
+
+} // namespace android
\ No newline at end of file