Merge "ActivityManager: use proc state from compiled aidl"
diff --git a/data/etc/cec_config.xml b/data/etc/cec_config.xml
index 8e78ad7..5defb2f 100644
--- a/data/etc/cec_config.xml
+++ b/data/etc/cec_config.xml
@@ -9,6 +9,15 @@
     </allowed-values>
     <default-value int-value="1" />
   </setting>
+  <setting name="hdmi_cec_version"
+           value-type="int"
+           user-configurable="true">
+    <allowed-values>
+      <value int-value="0x05" />
+      <value int-value="0x06" />
+    </allowed-values>
+    <default-value int-value="0x05" />
+  </setting>
   <setting name="send_standby_on_sleep"
            value-type="string"
            user-configurable="true">
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 278d8e1..26b997e 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -112,6 +112,7 @@
         "Stability.cpp",
         "Status.cpp",
         "TextOutput.cpp",
+        "Utils.cpp",
         ":libbinder_aidl",
         ":activity_manager_procstate_aidl",
     ],
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 6ca3b16..f2d223d 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -173,6 +173,10 @@
 {
     data.setDataPosition(0);
 
+    if (reply != nullptr && (flags & FLAG_CLEAR_BUF)) {
+        reply->markSensitive();
+    }
+
     status_t err = NO_ERROR;
     switch (code) {
         case PING_TRANSACTION:
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 05fcc2b..a3a2f87 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1244,7 +1244,9 @@
             if ((tr.flags & TF_ONE_WAY) == 0) {
                 LOG_ONEWAY("Sending reply to %d!", mCallingPid);
                 if (error < NO_ERROR) reply.setError(error);
-                sendReply(reply, 0);
+
+                constexpr uint32_t kForwardReplyFlags = TF_CLEAR_BUF;
+                sendReply(reply, (tr.flags & kForwardReplyFlags));
             } else {
                 if (error != OK || reply.dataSize() != 0) {
                     alog << "oneway function results will be dropped but finished with status "
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 4381386..041c364 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -49,6 +49,7 @@
 
 #include <private/binder/binder_module.h>
 #include "Static.h"
+#include "Utils.h"
 
 #define LOG_REFS(...)
 //#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
@@ -502,6 +503,11 @@
     return mHasFds;
 }
 
+void Parcel::markSensitive() const
+{
+    mDeallocZero = true;
+}
+
 void Parcel::updateWorkSourceRequestHeaderPosition() const {
     // Only update the request headers once. We only want to point
     // to the first headers read/written.
@@ -2627,6 +2633,9 @@
             LOG_ALLOC("Parcel %p: freeing with %zu capacity", this, mDataCapacity);
             gParcelGlobalAllocSize -= mDataCapacity;
             gParcelGlobalAllocCount--;
+            if (mDeallocZero) {
+                zeroMemory(mData, mDataSize);
+            }
             free(mData);
         }
         if (mObjects) free(mObjects);
@@ -2649,6 +2658,21 @@
             : continueWrite(std::max(newSize, (size_t) 128));
 }
 
+static uint8_t* reallocZeroFree(uint8_t* data, size_t oldCapacity, size_t newCapacity, bool zero) {
+    if (!zero) {
+        return (uint8_t*)realloc(data, newCapacity);
+    }
+    uint8_t* newData = (uint8_t*)malloc(newCapacity);
+    if (!newData) {
+        return nullptr;
+    }
+
+    memcpy(newData, data, std::min(oldCapacity, newCapacity));
+    zeroMemory(data, oldCapacity);
+    free(data);
+    return newData;
+}
+
 status_t Parcel::restartWrite(size_t desired)
 {
     if (desired > INT32_MAX) {
@@ -2662,7 +2686,7 @@
         return continueWrite(desired);
     }
 
-    uint8_t* data = (uint8_t*)realloc(mData, desired);
+    uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
     if (!data && desired > mDataCapacity) {
         mError = NO_MEMORY;
         return NO_MEMORY;
@@ -2813,7 +2837,7 @@
 
         // We own the data, so we can just do a realloc().
         if (desired > mDataCapacity) {
-            uint8_t* data = (uint8_t*)realloc(mData, desired);
+            uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
             if (data) {
                 LOG_ALLOC("Parcel %p: continue from %zu to %zu capacity", this, mDataCapacity,
                         desired);
@@ -2881,6 +2905,7 @@
     mHasFds = false;
     mFdsKnown = true;
     mAllowFds = true;
+    mDeallocZero = false;
     mOwner = nullptr;
     mOpenAshmemSize = 0;
     mWorkSourceRequestHeaderPosition = 0;
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index c232283..61a611d 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -16,6 +16,13 @@
       "name": "binderTextOutputTest"
     },
     {
+      "name": "binderParcelTest"
+    },
+    {
+      "name": "binderParcelTest",
+      "host": true
+    },
+    {
       "name": "binderLibTest"
     },
     {
diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp
new file mode 100644
index 0000000..90a4502
--- /dev/null
+++ b/libs/binder/Utils.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Utils.h"
+
+#include <string.h>
+
+namespace android {
+
+void zeroMemory(uint8_t* data, size_t size) {
+    memset(data, 0, size);
+}
+
+}   // namespace android
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
new file mode 100644
index 0000000..f94b158
--- /dev/null
+++ b/libs/binder/Utils.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdint>
+#include <stddef.h>
+
+namespace android {
+
+// avoid optimizations
+void zeroMemory(uint8_t* data, size_t size);
+
+}   // namespace android
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index eea0e89..7b37e8d 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -64,6 +64,10 @@
         // Corresponds to TF_ONE_WAY -- an asynchronous call.
         FLAG_ONEWAY             = 0x00000001,
 
+        // Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call
+        // is made
+        FLAG_CLEAR_BUF          = 0x00000020,
+
         // Private userspace flag for transaction which is being requested from
         // a vendor context.
         FLAG_PRIVATE_VENDOR     = 0x10000000,
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index fbfd6c5..84bfe38 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -84,6 +84,13 @@
 
     bool                hasFileDescriptors() const;
 
+    // Zeros data when reallocating. Other mitigations may be added
+    // in the future.
+    //
+    // WARNING: some read methods may make additional copies of data.
+    // In order to verify this, heap dumps should be used.
+    void                markSensitive() const;
+
     // Writes the RPC header.
     status_t            writeInterfaceToken(const String16& interface);
     status_t            writeInterfaceToken(const char16_t* str, size_t len);
@@ -600,6 +607,10 @@
     mutable bool        mHasFds;
     bool                mAllowFds;
 
+    // if this parcelable is involved in a secure transaction, force the
+    // data to be overridden with zero when deallocated
+    mutable bool        mDeallocZero;
+
     release_func        mOwner;
     void*               mOwnerCookie;
 
diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h
index 7be8f7b..0fe7f5b 100644
--- a/libs/binder/include/private/binder/binder_module.h
+++ b/libs/binder/include/private/binder/binder_module.h
@@ -88,7 +88,9 @@
 };
 #endif //BINDER_GET_FROZEN_INFO
 
-
+enum transaction_flags_ext {
+    TF_CLEAR_BUF = 0x20, /* clear buffer on txn complete */
+};
 
 #ifdef __cplusplus
 }   // namespace android
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index d35debc..b927f6f 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -611,7 +611,7 @@
         return STATUS_UNKNOWN_TRANSACTION;
     }
 
-    constexpr binder_flags_t kAllFlags = FLAG_PRIVATE_VENDOR | FLAG_ONEWAY;
+    constexpr binder_flags_t kAllFlags = FLAG_PRIVATE_VENDOR | FLAG_ONEWAY | FLAG_CLEAR_BUF;
     if ((flags & ~kAllFlags) != 0) {
         LOG(ERROR) << __func__ << ": Unrecognized flags sent: " << flags;
         return STATUS_BAD_VALUE;
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 33763d5..ce3d1db 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -43,7 +43,6 @@
 
 #if __ANDROID_API__ >= 29
 
-// Also see TF_* in kernel's binder.h
 typedef uint32_t binder_flags_t;
 enum {
     /**
diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
index a99d555..e315c79 100644
--- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -20,6 +20,17 @@
 
 __BEGIN_DECLS
 
+// platform values for binder_flags_t
+enum {
+    /**
+     * The transaction and reply will be cleared by the kernel in read-only
+     * binder buffers storing transactions.
+     *
+     * Introduced in API level 31.
+     */
+    FLAG_CLEAR_BUF = 0x20,
+};
+
 /**
  * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This
  * must be called on a local binder server before it is sent out to any othe
diff --git a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
index 114a781..d54c1a1 100644
--- a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h
@@ -33,4 +33,15 @@
  */
 bool AParcel_getAllowFds(const AParcel*);
 
+/**
+ * Data written to the parcel will be zero'd before being deleted or realloced.
+ *
+ * The main use of this is marking a parcel that will be used in a transaction
+ * with FLAG_CLEAR_BUF. When FLAG_CLEAR_BUF is used, the reply parcel will
+ * automatically be marked as sensitive when it is created.
+ *
+ * \param parcel The parcel to clear associated data from.
+ */
+void AParcel_markSensitive(const AParcel* parcel);
+
 __END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 947cc98..6962f86 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -121,15 +121,16 @@
     AServiceManager_registerLazyService; # llndk
     AServiceManager_waitForService; # apex llndk
 
-    AParcel_reset;
-    AParcel_getDataSize;
     AParcel_appendFrom;
     AParcel_create;
+    AParcel_getDataSize;
+    AParcel_reset;
 };
 
 LIBBINDER_NDK_PLATFORM {
   global:
     AParcel_getAllowFds;
+    AParcel_markSensitive;
     extern "C++" {
         AIBinder_fromPlatformBinder*;
         AIBinder_toPlatformBinder*;
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 2f95318..3e3eda1 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -226,6 +226,10 @@
     return parcel->get()->dataPosition();
 }
 
+void AParcel_markSensitive(const AParcel* parcel) {
+    return parcel->get()->markSensitive();
+}
+
 binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) {
     sp<IBinder> writeBinder = binder != nullptr ? binder->getBinder() : nullptr;
     return parcel->get()->writeStrongBinder(writeBinder);
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 6d0a369..037ee95 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -33,8 +33,7 @@
 
 /// Additional operation flags.
 ///
-/// Can be either 0 for a normal RPC, or [`IBinder::FLAG_ONEWAY`] for a
-/// one-way RPC.
+/// `IBinder::FLAG_*` values.
 pub type TransactionFlags = u32;
 
 /// Super-trait for Binder interfaces.
@@ -91,6 +90,8 @@
 
     /// Corresponds to TF_ONE_WAY -- an asynchronous call.
     const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY;
+    /// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call is made.
+    const FLAG_CLEAR_BUF: TransactionFlags = sys::FLAG_CLEAR_BUF;
 
     /// Is this object still alive?
     fn is_binder_alive(&self) -> bool;
diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 2c1e5a4..6c34824 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -100,6 +100,14 @@
 
 // Data serialization methods
 impl Parcel {
+    /// Data written to parcelable is zero'd before being deleted or reallocated.
+    pub fn mark_sensitive(&mut self) {
+        unsafe {
+            // Safety: guaranteed to have a parcel object, and this method never fails
+            sys::AParcel_markSensitive(self.as_native())
+        }
+    }
+
     /// Write a type that implements [`Serialize`] to the `Parcel`.
     pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> {
         parcelable.serialize(self)
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
index 3f20a4f..ef142b5 100644
--- a/libs/binder/rust/sys/BinderBindings.hpp
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -18,6 +18,7 @@
 #include <android/binder_ibinder_platform.h>
 #include <android/binder_manager.h>
 #include <android/binder_parcel.h>
+#include <android/binder_parcel_platform.h>
 #include <android/binder_process.h>
 #include <android/binder_shell.h>
 #include <android/binder_status.h>
@@ -78,6 +79,7 @@
 
 enum {
     FLAG_ONEWAY = FLAG_ONEWAY,
+    FLAG_CLEAR_BUF = FLAG_CLEAR_BUF,
 };
 
 } // namespace consts
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index a03835b..87f1d45 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -60,6 +60,23 @@
     require_root: true,
 }
 
+// unit test only, which can run on host and doesn't use /dev/binder
+cc_test {
+    name: "binderParcelTest",
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+    srcs: ["binderParcelTest.cpp"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+    test_suites: ["general-tests"],
+}
+
 cc_test {
     name: "binderLibTest",
     defaults: ["binder_test_defaults"],
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 98f0868..ad4729d 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -402,6 +402,14 @@
     EXPECT_EQ(NO_ERROR, ret);
 }
 
+TEST_F(BinderLibTest, NopTransactionClear) {
+    status_t ret;
+    Parcel data, reply;
+    // make sure it accepts the transaction flag
+    ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_CLEAR_BUF);
+    EXPECT_EQ(NO_ERROR, ret);
+}
+
 TEST_F(BinderLibTest, Freeze) {
     status_t ret;
     Parcel data, reply, replypid;
diff --git a/libs/binder/tests/binderParcelTest.cpp b/libs/binder/tests/binderParcelTest.cpp
new file mode 100644
index 0000000..1764228
--- /dev/null
+++ b/libs/binder/tests/binderParcelTest.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <gtest/gtest.h>
+
+using android::IPCThreadState;
+using android::OK;
+using android::Parcel;
+using android::String16;
+using android::String8;
+using android::status_t;
+
+// Tests a second operation results in a parcel at the same location as it
+// started.
+void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) {
+    Parcel p;
+    a(&p);
+    size_t end = p.dataPosition();
+    p.setDataPosition(0);
+    b(&p);
+    EXPECT_EQ(end, p.dataPosition());
+}
+
+TEST(Parcel, InverseInterfaceToken) {
+    const String16 token = String16("asdf");
+    parcelOpSameLength([&] (Parcel* p) {
+        p->writeInterfaceToken(token);
+    }, [&] (Parcel* p) {
+        EXPECT_TRUE(p->enforceInterface(token, IPCThreadState::self()));
+    });
+}
+
+TEST(Parcel, Utf8FromUtf16Read) {
+    const char* token = "asdf";
+    parcelOpSameLength([&] (Parcel* p) {
+        p->writeString16(String16(token));
+    }, [&] (Parcel* p) {
+        std::string s;
+        EXPECT_EQ(OK, p->readUtf8FromUtf16(&s));
+        EXPECT_EQ(token, s);
+    });
+}
+
+TEST(Parcel, Utf8AsUtf16Write) {
+    std::string token = "asdf";
+    parcelOpSameLength([&] (Parcel* p) {
+        p->writeUtf8AsUtf16(token);
+    }, [&] (Parcel* p) {
+        String16 s;
+        EXPECT_EQ(OK, p->readString16(&s));
+        EXPECT_EQ(s, String16(token.c_str()));
+    });
+}
+
+template <typename T>
+using readFunc = status_t (Parcel::*)(T* out) const;
+template <typename T>
+using writeFunc = status_t (Parcel::*)(const T& in);
+template <typename T>
+using copyWriteFunc = status_t (Parcel::*)(T in);
+
+template <typename T, typename WRITE_FUNC>
+void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, WRITE_FUNC w) {
+    for (const T& value : ts) {
+        parcelOpSameLength([&] (Parcel* p) {
+            (*p.*w)(value);
+        }, [&] (Parcel* p) {
+            T outValue;
+            EXPECT_EQ(OK, (*p.*r)(&outValue));
+            EXPECT_EQ(value, outValue);
+        });
+    }
+}
+
+template <typename T>
+void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, writeFunc<T> w) {
+    readWriteInverse<T, writeFunc<T>>(std::move(ts), r, w);
+}
+template <typename T>
+void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, copyWriteFunc<T> w) {
+    readWriteInverse<T, copyWriteFunc<T>>(std::move(ts), r, w);
+}
+
+#define TEST_READ_WRITE_INVERSE(type, name, ...) \
+    TEST(Parcel, Inverse##name) { \
+        readWriteInverse<type>(__VA_ARGS__, &Parcel::read##name, &Parcel::write##name); \
+    }
+
+TEST_READ_WRITE_INVERSE(int32_t, Int32, {-2, -1, 0, 1, 2});
+TEST_READ_WRITE_INVERSE(uint32_t, Uint32, {0, 1, 2});
+TEST_READ_WRITE_INVERSE(int64_t, Int64, {-2, -1, 0, 1, 2});
+TEST_READ_WRITE_INVERSE(uint64_t, Uint64, {0, 1, 2});
+TEST_READ_WRITE_INVERSE(float, Float, {-1.0f, 0.0f, 3.14f});
+TEST_READ_WRITE_INVERSE(double, Double, {-1.0, 0.0, 3.14});
+TEST_READ_WRITE_INVERSE(bool, Bool, {true, false});
+TEST_READ_WRITE_INVERSE(char16_t, Char, {u'a', u'\0'});
+TEST_READ_WRITE_INVERSE(int8_t, Byte, {-1, 0, 1});
+TEST_READ_WRITE_INVERSE(String8, String8, {String8(), String8("a"), String8("asdf")});
+TEST_READ_WRITE_INVERSE(String16, String16, {String16(), String16("a"), String16("asdf")});
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index eb967ce..cd7f37b 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -78,6 +78,7 @@
         "skia/SkiaRenderEngine.cpp",
         "skia/SkiaGLRenderEngine.cpp",
         "skia/filters/BlurFilter.cpp",
+        "skia/filters/LinearEffect.cpp",
     ],
 }
 
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index e5f7539..902348b 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -16,6 +16,9 @@
 
 //#define LOG_NDEBUG 0
 #include <cstdint>
+
+#include "SkImageInfo.h"
+#include "system/graphics-base-v1.0.h"
 #undef LOG_TAG
 #define LOG_TAG "RenderEngine"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -50,6 +53,7 @@
 #include "../gl/GLExtensions.h"
 #include "SkiaGLRenderEngine.h"
 #include "filters/BlurFilter.h"
+#include "filters/LinearEffect.h"
 
 extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 
@@ -411,6 +415,32 @@
                          matrix[3][3], 0);
 }
 
+static bool needsToneMapping(ui::Dataspace sourceDataspace, ui::Dataspace destinationDataspace) {
+    int64_t sourceTransfer = sourceDataspace & HAL_DATASPACE_TRANSFER_MASK;
+    int64_t destTransfer = destinationDataspace & HAL_DATASPACE_TRANSFER_MASK;
+
+    // Treat unsupported dataspaces as srgb
+    if (destTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
+        destTransfer != HAL_DATASPACE_TRANSFER_HLG &&
+        destTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
+        destTransfer = HAL_DATASPACE_TRANSFER_SRGB;
+    }
+
+    if (sourceTransfer != HAL_DATASPACE_TRANSFER_LINEAR &&
+        sourceTransfer != HAL_DATASPACE_TRANSFER_HLG &&
+        sourceTransfer != HAL_DATASPACE_TRANSFER_ST2084) {
+        sourceTransfer = HAL_DATASPACE_TRANSFER_SRGB;
+    }
+
+    const bool isSourceLinear = sourceTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
+    const bool isSourceSRGB = sourceTransfer == HAL_DATASPACE_TRANSFER_SRGB;
+    const bool isDestLinear = destTransfer == HAL_DATASPACE_TRANSFER_LINEAR;
+    const bool isDestSRGB = destTransfer == HAL_DATASPACE_TRANSFER_SRGB;
+
+    return !(isSourceLinear && isDestSRGB) && !(isSourceSRGB && isDestLinear) &&
+            sourceTransfer != destTransfer;
+}
+
 void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
     std::lock_guard<std::mutex> lock(mRenderingMutex);
     mImageCache.erase(bufferId);
@@ -539,14 +569,20 @@
             if (iter != mImageCache.end()) {
                 image = iter->second;
             } else {
-                image = SkImage::MakeFromAHardwareBuffer(item.buffer->toAHardwareBuffer(),
-                                                         item.usePremultipliedAlpha
-                                                                 ? kPremul_SkAlphaType
-                                                                 : kUnpremul_SkAlphaType,
-                                                         mUseColorManagement
-                                                                 ? toColorSpace(
-                                                                           layer->sourceDataspace)
-                                                                 : SkColorSpace::MakeSRGB());
+                image = SkImage::MakeFromAHardwareBuffer(
+                        item.buffer->toAHardwareBuffer(),
+                        item.isOpaque ? kOpaque_SkAlphaType
+                                      : (item.usePremultipliedAlpha ? kPremul_SkAlphaType
+                                                                    : kUnpremul_SkAlphaType),
+                        mUseColorManagement
+                                ? (needsToneMapping(layer->sourceDataspace, display.outputDataspace)
+                                           // If we need to map to linear space, then
+                                           // mark the source image with the same
+                                           // colorspace as the destination surface so
+                                           // that Skia's color management is a no-op.
+                                           ? toColorSpace(display.outputDataspace)
+                                           : toColorSpace(layer->sourceDataspace))
+                                : SkColorSpace::MakeSRGB());
                 mImageCache.insert({item.buffer->getId(), image});
             }
 
@@ -594,7 +630,22 @@
 
             matrix.postConcat(texMatrix);
             matrix.postScale(rotatedBufferWidth, rotatedBufferHeight);
-            paint.setShader(image->makeShader(matrix));
+            sk_sp<SkShader> shader = image->makeShader(matrix);
+
+            if (mUseColorManagement &&
+                needsToneMapping(layer->sourceDataspace, display.outputDataspace)) {
+                LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace,
+                                                   .outputDataspace = display.outputDataspace,
+                                                   .undoPremultipliedAlpha = !item.isOpaque &&
+                                                           item.usePremultipliedAlpha};
+                sk_sp<SkRuntimeEffect> runtimeEffect = buildRuntimeEffect(effect);
+                paint.setShader(createLinearEffectShader(shader, effect, runtimeEffect,
+                                                         display.maxLuminance,
+                                                         layer->source.buffer.maxMasteringLuminance,
+                                                         layer->source.buffer.maxContentLuminance));
+            } else {
+                paint.setShader(shader);
+            }
         } else {
             ATRACE_NAME("DrawColor");
             const auto color = layer->source.solidColor;
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
new file mode 100644
index 0000000..376abdf
--- /dev/null
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LinearEffect.h"
+
+#include <SkString.h>
+
+#include <optional>
+
+#include "log/log.h"
+#include "math/mat4.h"
+#include "ui/ColorSpace.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+static void generateEOTF(ui::Dataspace dataspace, SkString& shader) {
+    switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            shader.append(R"(
+
+                float3 EOTF(float3 color) {
+                    float m1 = (2610.0 / 4096.0) / 4.0;
+                    float m2 = (2523.0 / 4096.0) * 128.0;
+                    float c1 = (3424.0 / 4096.0);
+                    float c2 = (2413.0 / 4096.0) * 32.0;
+                    float c3 = (2392.0 / 4096.0) * 32.0;
+
+                    float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2));
+                    tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
+                    return pow(tmp, 1.0 / float3(m1));
+                }
+            )");
+            break;
+        default:
+            shader.append(R"(
+
+                float EOTF_sRGB(float srgb) {
+                    return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+                }
+
+                float3 EOTF_sRGB(float3 srgb) {
+                    return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+                }
+
+                float3 EOTF(float3 srgb) {
+                    return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+                }
+            )");
+            break;
+    }
+}
+
+static void generateXYZTransforms(SkString& shader) {
+    shader.append(R"(
+        uniform float4x4 in_rgbToXyz;
+        uniform float4x4 in_xyzToRgb;
+        float3 ToXYZ(float3 rgb) {
+            return clamp((in_rgbToXyz * float4(rgb, 1.0)).rgb, 0.0, 1.0);
+        }
+
+        float3 ToRGB(float3 xyz) {
+            return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0);
+        }
+    )");
+}
+
+static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
+                         SkString& shader) {
+    shader.append(R"(
+            uniform float in_displayMaxLuminance;
+            uniform float in_inputMaxLuminance;
+            uniform float in_maxContentLuminance;
+        )");
+    switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            shader.append(R"(
+                    float3 ScaleLuminance(float3 xyz) {
+                        return xyz * 10000.0;
+                    }
+                )");
+
+            switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+                default:
+                    shader.append(R"(
+                            float3 ToneMap(float3 xyz) {
+                                float maxInLumi = in_inputMaxLuminance;
+                                float maxOutLumi = in_displayMaxLuminance;
+
+                                float nits = xyz.y;
+
+                                // clamp to max input luminance
+                                nits = clamp(nits, 0.0, maxInLumi);
+
+                                // scale [0.0, maxInLumi] to [0.0, maxOutLumi]
+                                if (maxInLumi <= maxOutLumi) {
+                                    return xyz * (maxOutLumi / maxInLumi);
+                                } else {
+                                    // three control points
+                                    const float x0 = 10.0;
+                                    const float y0 = 17.0;
+                                    float x1 = maxOutLumi * 0.75;
+                                    float y1 = x1;
+                                    float x2 = x1 + (maxInLumi - x1) / 2.0;
+                                    float y2 = y1 + (maxOutLumi - y1) * 0.75;
+
+                                    // horizontal distances between the last three control points
+                                    float h12 = x2 - x1;
+                                    float h23 = maxInLumi - x2;
+                                    // tangents at the last three control points
+                                    float m1 = (y2 - y1) / h12;
+                                    float m3 = (maxOutLumi - y2) / h23;
+                                    float m2 = (m1 + m3) / 2.0;
+
+                                    if (nits < x0) {
+                                        // scale [0.0, x0] to [0.0, y0] linearly
+                                        float slope = y0 / x0;
+                                        return xyz * slope;
+                                    } else if (nits < x1) {
+                                        // scale [x0, x1] to [y0, y1] linearly
+                                        float slope = (y1 - y0) / (x1 - x0);
+                                        nits = y0 + (nits - x0) * slope;
+                                    } else if (nits < x2) {
+                                        // scale [x1, x2] to [y1, y2] using Hermite interp
+                                        float t = (nits - x1) / h12;
+                                        nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) +
+                                                (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
+                                    } else {
+                                        // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
+                                        float t = (nits - x2) / h23;
+                                        nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) +
+                                                (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t;
+                                    }
+                                }
+
+                                // color.y is greater than x0 and is thus non-zero
+                                return xyz * (nits / xyz.y);
+                            }
+                        )");
+                    break;
+            }
+            break;
+        default:
+            shader.append(R"(
+                    float3 ScaleLuminance(float3 xyz) {
+                        return xyz * in_displayMaxLuminance;
+                    }
+
+                    float3 ToneMap(float3 xyz) {
+                        return xyz;
+                    }
+                )");
+            break;
+    }
+
+    switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            shader.append(R"(
+                    float3 NormalizeLuminance(float3 xyz) {
+                        return xyz / 10000.0;
+                    }
+                )");
+            break;
+        default:
+            shader.append(R"(
+                    float3 NormalizeLuminance(float3 xyz) {
+                        return xyz / in_displayMaxLuminance;
+                    }
+                )");
+            break;
+    }
+
+    shader.append(R"(
+            float3 OOTF(float3 xyz) {
+                return NormalizeLuminance(ToneMap(ScaleLuminance(xyz)));
+            }
+        )");
+}
+
+static void generateOETF(ui::Dataspace dataspace, SkString& shader) {
+    switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+        case HAL_DATASPACE_TRANSFER_ST2084:
+            shader.append(R"(
+
+                float3 OETF(float3 xyz) {
+                    float m1 = (2610.0 / 4096.0) / 4.0;
+                    float m2 = (2523.0 / 4096.0) * 128.0;
+                    float c1 = (3424.0 / 4096.0);
+                    float c2 = (2413.0 / 4096.0) * 32.0;
+                    float c3 = (2392.0 / 4096.0) * 32.0;
+
+                    float3 tmp = pow(xyz, float3(m1));
+                    tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+                    return pow(tmp, float3(m2));
+                }
+            )");
+            break;
+        default:
+            shader.append(R"(
+                float OETF_sRGB(float linear) {
+                    return linear <= 0.0031308 ?
+                            linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+                }
+
+                float3 OETF_sRGB(float3 linear) {
+                    return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+                }
+
+                float3 OETF(float3 linear) {
+                    return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+                }
+            )");
+            break;
+    }
+}
+
+static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader) {
+    shader.append(R"(
+        in shader input;
+        half4 main(float2 xy) {
+            float4 c = float4(sample(input, xy));
+    )");
+    if (undoPremultipliedAlpha) {
+        shader.append(R"(
+            c.rgb = c.rgb / (c.a + 0.0019);
+        )");
+    }
+    shader.append(R"(
+        c.rgb = OETF(ToRGB(OOTF(ToXYZ(EOTF(c.rgb)))));
+    )");
+    if (undoPremultipliedAlpha) {
+        shader.append(R"(
+            c.rgb = c.rgb * (c.a + 0.0019);
+        )");
+    }
+    shader.append(R"(
+            return c;
+        }
+    )");
+}
+static ColorSpace toColorSpace(ui::Dataspace dataspace) {
+    switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+        case HAL_DATASPACE_STANDARD_BT709:
+            return ColorSpace::sRGB();
+            break;
+        case HAL_DATASPACE_STANDARD_DCI_P3:
+            return ColorSpace::DisplayP3();
+            break;
+        case HAL_DATASPACE_STANDARD_BT2020:
+            return ColorSpace::BT2020();
+            break;
+        default:
+            return ColorSpace::sRGB();
+            break;
+    }
+}
+
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) {
+    SkString shaderString;
+    generateEOTF(linearEffect.inputDataspace, shaderString);
+    generateXYZTransforms(shaderString);
+    generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
+    generateOETF(linearEffect.outputDataspace, shaderString);
+    generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+
+    auto [shader, error] = SkRuntimeEffect::Make(shaderString);
+    if (!shader) {
+        LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
+    }
+    return shader;
+}
+
+sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEffect& linearEffect,
+                                         sk_sp<SkRuntimeEffect> runtimeEffect,
+                                         float maxDisplayLuminance, float maxMasteringLuminance,
+                                         float maxContentLuminance) {
+    SkRuntimeShaderBuilder effectBuilder(runtimeEffect);
+
+    effectBuilder.child("input") = shader;
+
+    ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace);
+    ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
+
+    effectBuilder.uniform("in_rgbToXyz") = mat4(inputColorSpace.getRGBtoXYZ());
+    effectBuilder.uniform("in_xyzToRgb") = mat4(outputColorSpace.getXYZtoRGB());
+    effectBuilder.uniform("in_displayMaxLuminance") = maxDisplayLuminance;
+    effectBuilder.uniform("in_inputMaxLuminance") =
+            std::min(maxMasteringLuminance, maxContentLuminance);
+    return effectBuilder.makeShader(nullptr, false);
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h
new file mode 100644
index 0000000..2615669
--- /dev/null
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2020 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 <optional>
+
+#include "SkColorMatrix.h"
+#include "SkRuntimeEffect.h"
+#include "SkShader.h"
+#include "ui/GraphicTypes.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * Arguments for creating an effect that applies color transformations in linear XYZ space.
+ * A linear effect is decomposed into the following steps when operating on an image:
+ * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended
+ * relative display brightness of the scene in nits for each RGB channel
+ * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display
+ * luminance.
+ * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone
+ * mapping to display SDR content alongside HDR content, or any number of subjective transformations
+ * 4. Transformation matrix from linear XYZ back to linear RGB brightness.
+ * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to
+ * output RGB colors.
+ *
+ * For further reading, consult the recommendation in ITU-R BT.2390-4:
+ * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf
+ *
+ * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is
+ * intended to be the output surface. However, Skia does not support complex tone mapping such as
+ * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied
+ * to the source colors. so that the tone mapping process is only applied once by this effect. Tone
+ * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions)
+ * alongside other content, whereby maximum input luminance is mapped to maximum output luminance
+ * and intermediate values are interpolated.
+ */
+struct LinearEffect {
+    // Input dataspace of the source colors.
+    const ui::Dataspace inputDataspace = ui::Dataspace::SRGB;
+
+    // Working dataspace for the output surface, for conversion from linear space.
+    const ui::Dataspace outputDataspace = ui::Dataspace::SRGB;
+
+    // Sets whether alpha premultiplication must be undone.
+    // This is required if the source colors use premultiplied alpha and is not opaque.
+    const bool undoPremultipliedAlpha = false;
+};
+
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect);
+
+// Generates a shader resulting from applying the a linear effect created from
+// LinearEffectARgs::buildEffect to an inputShader. We also provide additional HDR metadata upon
+// creating the shader:
+// * The max display luminance is the max luminance of the physical display in nits
+// * The max mastering luminance is provided as the max luminance from the SMPTE 2086
+// standard.
+// * The max content luminance is provided as the max light level from the CTA 861.3
+// standard.
+sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader,
+                                         const LinearEffect& linearEffect,
+                                         sk_sp<SkRuntimeEffect> runtimeEffect,
+                                         float maxDisplayLuminance, float maxMasteringLuminance,
+                                         float maxContentLuminance);
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index d6fa74d..e5d208a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2270,13 +2270,14 @@
 
 std::string InputDispatcher::dumpWindowForTouchOcclusion(const InputWindowInfo* info,
                                                          bool isTouchedWindow) const {
-    return StringPrintf(INDENT2 "* %stype=%s, package=%s/%" PRId32 ", mode=%s, alpha=%.2f, "
+    return StringPrintf(INDENT2 "* %stype=%s, package=%s/%" PRId32 ", id=%" PRId32
+                                ", mode=%s, alpha=%.2f, "
                                 "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
                                 "], touchableRegion=%s, window={%s}, applicationInfo=%s, "
                                 "flags={%s}, inputFeatures={%s}, hasToken=%s\n",
                         (isTouchedWindow) ? "[TOUCHED] " : "",
                         NamedEnum::string(info->type, "%" PRId32).c_str(),
-                        info->packageName.c_str(), info->ownerUid,
+                        info->packageName.c_str(), info->ownerUid, info->id,
                         toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft,
                         info->frameTop, info->frameRight, info->frameBottom,
                         dumpRegion(info->touchableRegion).c_str(), info->name.c_str(),
@@ -4486,19 +4487,19 @@
                     const sp<InputWindowHandle>& windowHandle = windowHandles[i];
                     const InputWindowInfo* windowInfo = windowHandle->getInfo();
 
-                    dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
+                    dump += StringPrintf(INDENT3 "%zu: name='%s', id=%" PRId32 ", displayId=%d, "
                                                  "portalToDisplayId=%d, paused=%s, focusable=%s, "
-                                                 "hasWallpaper=%s, visible=%s, "
+                                                 "hasWallpaper=%s, visible=%s, alpha=%.2f, "
                                                  "flags=%s, type=0x%08x, "
                                                  "frame=[%d,%d][%d,%d], globalScale=%f, "
                                                  "applicationInfo=%s, "
                                                  "touchableRegion=",
-                                         i, windowInfo->name.c_str(), windowInfo->displayId,
-                                         windowInfo->portalToDisplayId,
+                                         i, windowInfo->name.c_str(), windowInfo->id,
+                                         windowInfo->displayId, windowInfo->portalToDisplayId,
                                          toString(windowInfo->paused),
                                          toString(windowInfo->focusable),
                                          toString(windowInfo->hasWallpaper),
-                                         toString(windowInfo->visible),
+                                         toString(windowInfo->visible), windowInfo->alpha,
                                          windowInfo->flags.string().c_str(),
                                          static_cast<int32_t>(windowInfo->type),
                                          windowInfo->frameLeft, windowInfo->frameTop,
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index ea84835..17f37c3 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -3397,7 +3397,6 @@
 void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down,
                                              bool hovering) {
     int32_t metaState = getContext()->getGlobalMetaState();
-    int32_t displayId = mViewport.displayId;
 
     if (down || hovering) {
         mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
@@ -3407,7 +3406,7 @@
     } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
         mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
     }
-    displayId = mPointerController->getDisplayId();
+    int32_t displayId = mPointerController->getDisplayId();
 
     float xCursorPosition;
     float yCursorPosition;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 1343375..47a4f42 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -90,6 +90,7 @@
     {
         std::lock_guard lock(mVsync.mutex);
         mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
+        mVsync.mScheduled = false;
     }
     mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions(
                                          {targetWakeupTime, readyTime, vsyncTime}),
@@ -114,6 +115,10 @@
     ATRACE_CALL();
     std::lock_guard lock(mVsync.mutex);
     mVsync.workDuration = workDuration;
+    if (mVsync.mScheduled) {
+        mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
+                                       mVsync.lastCallbackTime.count()});
+    }
 }
 
 void MessageQueue::waitMessage() {
@@ -147,13 +152,10 @@
     if (mEvents) {
         mEvents->requestNextVsync();
     } else {
-        const auto [workDuration, lastVsyncCallback] = [&] {
-            std::lock_guard lock(mVsync.mutex);
-            std::chrono::nanoseconds mWorkDurationNanos = mVsync.workDuration;
-            return std::make_pair(mWorkDurationNanos.count(), mVsync.lastCallbackTime.count());
-        }();
-
-        mVsync.registration->schedule({workDuration, /*readyDuration=*/0, lastVsyncCallback});
+        std::lock_guard lock(mVsync.mutex);
+        mVsync.mScheduled = true;
+        mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
+                                       mVsync.lastCallbackTime.count()});
     }
 }
 
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 139b38e..99ce3a6 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -109,6 +109,7 @@
         TracedOrdinal<std::chrono::nanoseconds> workDuration
                 GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
         std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
+        bool mScheduled GUARDED_BY(mutex) = false;
         TracedOrdinal<int> value = {"VSYNC-sf", 0};
     };
 
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
index 49cf80c..eee4bec 100644
--- a/services/surfaceflinger/TracedOrdinal.h
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -57,7 +57,9 @@
         trace();
     }
 
-    operator T() const { return mData; }
+    T get() const { return mData; }
+
+    operator T() const { return get(); }
 
     TracedOrdinal& operator=(T other) {
         mData = other;