Merge "Revert^2 "Added update device info playback configuration""
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 6459805..72c03bf 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -68,15 +68,15 @@
                                        apex::kApexPackageVendorDir};
     for (const auto& dir : apex_dirs) {
         // Cast call to void to suppress warn_unused_result.
-        static_cast<void>(apex::scanPackagesDirAndActivate(dir));
+        static_cast<void>(apex::ScanPackagesDirAndActivate(dir));
     }
-    return apex::getActivePackages();
+    return apex::GetActivePackages();
 }
 
 static void DeactivateApexPackages(const std::vector<apex::ApexFile>& active_packages) {
     for (const apex::ApexFile& apex_file : active_packages) {
         const std::string& package_path = apex_file.GetPath();
-        base::Result<void> status = apex::deactivatePackage(package_path);
+        base::Result<void> status = apex::DeactivatePackage(package_path);
         if (!status.ok()) {
             LOG(ERROR) << "Failed to deactivate " << package_path << ": "
                        << status.error();
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp
index 1994e56..0cbb80f 100644
--- a/cmds/lshal/Android.bp
+++ b/cmds/lshal/Android.bp
@@ -16,6 +16,7 @@
     name: "liblshal",
     shared_libs: [
         "libbase",
+        "libbinderdebug",
         "libcutils",
         "libutils",
         "libhidlbase",
@@ -47,6 +48,7 @@
     name: "lshal_defaults",
     shared_libs: [
         "libbase",
+        "libbinderdebug",
         "libcutils",
         "libutils",
         "libhidlbase",
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index 92958d9..22268ac 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -29,7 +29,6 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/parseint.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <hidl-hash/Hash.h>
 #include <hidl-util/FQName.h>
@@ -203,97 +202,14 @@
             lshal::getVintfInfo(getFrameworkMatrix(), fqInstance, ta, FRAMEWORK_MATRIX);
 }
 
-static bool scanBinderContext(pid_t pid,
-        const std::string &contextName,
-        std::function<void(const std::string&)> eachLine) {
-    std::ifstream ifs("/dev/binderfs/binder_logs/proc/" + std::to_string(pid));
-    if (!ifs.is_open()) {
-        ifs.open("/d/binder/proc/" + std::to_string(pid));
-        if (!ifs.is_open()) {
-            return false;
-        }
-    }
-
-    static const std::regex kContextLine("^context (\\w+)$");
-
-    bool isDesiredContext = false;
-    std::string line;
-    std::smatch match;
-    while(getline(ifs, line)) {
-        if (std::regex_search(line, match, kContextLine)) {
-            isDesiredContext = match.str(1) == contextName;
-            continue;
-        }
-
-        if (!isDesiredContext) {
-            continue;
-        }
-
-        eachLine(line);
-    }
-    return true;
-}
-
 bool ListCommand::getPidInfo(
-        pid_t serverPid, PidInfo *pidInfo) const {
-    static const std::regex kReferencePrefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
-    static const std::regex kThreadPrefix("^\\s*thread \\d+:\\s+l\\s+(\\d)(\\d)");
-
-    std::smatch match;
-    return scanBinderContext(serverPid, "hwbinder", [&](const std::string& line) {
-        if (std::regex_search(line, match, kReferencePrefix)) {
-            const std::string &ptrString = "0x" + match.str(2); // use number after c
-            uint64_t ptr;
-            if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
-                // Should not reach here, but just be tolerant.
-                err() << "Could not parse number " << ptrString << std::endl;
-                return;
-            }
-            const std::string proc = " proc ";
-            auto pos = line.rfind(proc);
-            if (pos != std::string::npos) {
-                for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
-                    int32_t pid;
-                    if (!::android::base::ParseInt(pidStr, &pid)) {
-                        err() << "Could not parse number " << pidStr << std::endl;
-                        return;
-                    }
-                    pidInfo->refPids[ptr].push_back(pid);
-                }
-            }
-
-            return;
-        }
-
-        if (std::regex_search(line, match, kThreadPrefix)) {
-            // "1" is waiting in binder driver
-            // "2" is poll. It's impossible to tell if these are in use.
-            //     and HIDL default code doesn't use it.
-            bool isInUse = match.str(1) != "1";
-            // "0" is a thread that has called into binder
-            // "1" is looper thread
-            // "2" is main looper thread
-            bool isHwbinderThread = match.str(2) != "0";
-
-            if (!isHwbinderThread) {
-                return;
-            }
-
-            if (isInUse) {
-                pidInfo->threadUsage++;
-            }
-
-            pidInfo->threadCount++;
-            return;
-        }
-
-        // not reference or thread line
-        return;
-    });
+        pid_t serverPid, BinderPidInfo *pidInfo) const {
+    const auto& status = getBinderPidInfo(BinderDebugContext::HWBINDER, serverPid, pidInfo);
+    return status == OK;
 }
 
-const PidInfo* ListCommand::getPidInfoCached(pid_t serverPid) {
-    auto pair = mCachedPidInfos.insert({serverPid, PidInfo{}});
+const BinderPidInfo* ListCommand::getPidInfoCached(pid_t serverPid) {
+    auto pair = mCachedPidInfos.insert({serverPid, BinderPidInfo{}});
     if (pair.second /* did insertion take place? */) {
         if (!getPidInfo(serverPid, &pair.first->second)) {
             return nullptr;
@@ -727,7 +643,7 @@
         entry->arch = fromBaseArchitecture(debugInfo.arch);
 
         if (debugInfo.pid != NO_PID) {
-            const PidInfo* pidInfo = getPidInfoCached(debugInfo.pid);
+            const BinderPidInfo* pidInfo = getPidInfoCached(debugInfo.pid);
             if (pidInfo == nullptr) {
                 handleError(IO_ERROR,
                             "no information for PID " + std::to_string(debugInfo.pid) +
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
index 412aadd..561f9cb 100644
--- a/cmds/lshal/ListCommand.h
+++ b/cmds/lshal/ListCommand.h
@@ -25,6 +25,7 @@
 
 #include <android-base/macros.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
+#include <binderdebug/BinderDebug.h>
 #include <hidl-util/FqInstance.h>
 #include <vintf/HalManifest.h>
 #include <vintf/VintfObject.h>
@@ -40,12 +41,6 @@
 
 class Lshal;
 
-struct PidInfo {
-    std::map<uint64_t, Pids> refPids; // pids that are referenced
-    uint32_t threadUsage; // number of threads in use
-    uint32_t threadCount; // number of threads total
-};
-
 enum class HalType {
     BINDERIZED_SERVICES = 0,
     PASSTHROUGH_CLIENTS,
@@ -110,9 +105,9 @@
     // Get relevant information for a PID by parsing files under
     // /dev/binderfs/binder_logs or /d/binder.
     // It is a virtual member function so that it can be mocked.
-    virtual bool getPidInfo(pid_t serverPid, PidInfo *info) const;
+    virtual bool getPidInfo(pid_t serverPid, BinderPidInfo *info) const;
     // Retrieve from mCachedPidInfos and call getPidInfo if necessary.
-    const PidInfo* getPidInfoCached(pid_t serverPid);
+    const BinderPidInfo* getPidInfoCached(pid_t serverPid);
 
     void dumpTable(const NullableOStream<std::ostream>& out) const;
     void dumpVintf(const NullableOStream<std::ostream>& out) const;
@@ -191,7 +186,7 @@
     std::map<pid_t, std::string> mCmdlines;
 
     // Cache for getPidInfo.
-    std::map<pid_t, PidInfo> mCachedPidInfos;
+    std::map<pid_t, BinderPidInfo> mCachedPidInfos;
 
     // Cache for getPartition.
     std::map<pid_t, Partition> mPartitions;
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h
index 3c36813..476aa04 100644
--- a/cmds/lshal/TableEntry.h
+++ b/cmds/lshal/TableEntry.h
@@ -32,7 +32,7 @@
 namespace lshal {
 
 using android::procpartition::Partition;
-using Pids = std::vector<int32_t>;
+using Pids = std::vector<pid_t>;
 
 enum class TableColumnType : unsigned int {
     INTERFACE_NAME = 0,
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index ba6cdf1..b6ff28d 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -233,12 +233,12 @@
         return ListCommand::dumpVintf(out);
     }
     void internalPostprocess() { ListCommand::postprocess(); }
-    const PidInfo* getPidInfoCached(pid_t serverPid) {
+    const BinderPidInfo* getPidInfoCached(pid_t serverPid) {
         return ListCommand::getPidInfoCached(serverPid);
     }
 
     MOCK_METHOD0(postprocess, void());
-    MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, PidInfo*));
+    MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, BinderPidInfo*));
     MOCK_CONST_METHOD1(parseCmdline, std::string(pid_t));
     MOCK_METHOD1(getPartition, Partition(pid_t));
 
@@ -299,8 +299,8 @@
 static std::vector<pid_t> getClients(pid_t serverId) {
     return {serverId + 1, serverId + 3};
 }
-static PidInfo getPidInfoFromId(pid_t serverId) {
-    PidInfo info;
+static BinderPidInfo getPidInfoFromId(pid_t serverId) {
+    BinderPidInfo info;
     info.refPids[getPtr(serverId)] = getClients(serverId);
     info.threadUsage = 10 + serverId;
     info.threadCount = 20 + serverId;
@@ -363,7 +363,7 @@
     void initMockList() {
         mockList = std::make_unique<NiceMock<MockListCommand>>(lshal.get());
         ON_CALL(*mockList, getPidInfo(_,_)).WillByDefault(Invoke(
-            [](pid_t serverPid, PidInfo* info) {
+            [](pid_t serverPid, BinderPidInfo* info) {
                 *info = getPidInfoFromId(serverPid);
                 return true;
             }));
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 7aac7da..b21010d 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -494,7 +494,7 @@
     sp<BpBinder> bpBinder = binder->remoteBinder();
     if (bpBinder == nullptr) return -1;
 
-    return ProcessState::self()->getStrongRefCountForNodeByHandle(bpBinder->handle());
+    return ProcessState::self()->getStrongRefCountForNode(bpBinder);
 }
 
 void ServiceManager::handleClientCallbacks() {
diff --git a/data/etc/pc_core_hardware.xml b/data/etc/pc_core_hardware.xml
new file mode 100644
index 0000000..c62da0a
--- /dev/null
+++ b/data/etc/pc_core_hardware.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<!-- These are the hardware components that all handheld devices
+     must include. Devices with optional hardware must also include extra
+     hardware files, per the comments below.
+
+     Handheld devices include phones, mobile Internet devices (MIDs),
+     Personal Media Players (PMPs), small tablets (7" or less), and similar
+     devices.
+-->
+<permissions>
+    <!-- This is Android and fully CTS compatible.  Basically this is for CTS tests to use. -->
+    <feature name="android.software.cts" />
+
+    <feature name="android.hardware.audio.output" />
+    <feature name="android.hardware.bluetooth" />
+    <feature name="android.hardware.microphone" />
+    <feature name="android.hardware.screen.portrait" />
+    <feature name="android.hardware.screen.landscape" />
+    <feature name="android.hardware.location" />
+    <feature name="android.hardware.location.network" />
+
+    <!-- basic system services -->
+    <feature name="android.software.app_widgets" />
+    <feature name="android.software.voice_recognizers" />
+    <feature name="android.software.backup" />
+    <feature name="android.software.home_screen" />
+    <feature name="android.software.input_methods" />
+    <feature name="android.software.picture_in_picture" />
+    <feature name="android.software.activities_on_secondary_displays" />
+    <feature name="android.software.print" />
+    <feature name="android.software.companion_device_setup" />
+    <feature name="android.software.autofill" />
+    <feature name="android.software.cant_save_state" />
+    <feature name="android.software.secure_lock_screen" />
+
+    <!-- Feature to specify if the device supports adding device admins. -->
+    <feature name="android.software.device_admin" />
+
+    <!-- Feature to specify if the device support managed users. -->
+    <feature name="android.software.managed_users" />
+
+    <!-- Feature to specify if the device supports controls. -->
+    <feature name="android.software.controls" />
+
+    <!-- Feature to specify if the device supports freeform. -->
+    <feature name="android.software.freeform_window_management" />
+    <feature name="android.hardware.type.pc" />
+</permissions>
\ No newline at end of file
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index 3dd1534..49a616f 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -57,13 +57,15 @@
 
 struct AAsset;
 
-#if __ANDROID_API__ >= 30
-
 /**
- *  {@link AImageDecoder} functions result code. Many functions will return one of these
- *  to indicate success ({@link ANDROID_IMAGE_DECODER_SUCCESS}) or the reason
- *  for the failure. On failure, any out-parameters should be considered
- *  uninitialized, except where specified.
+ *  {@link AImageDecoder} functions result code.
+ *
+ *  Introduced in API 30.
+ *
+ *  Many functions will return this to indicate success
+ *  ({@link ANDROID_IMAGE_DECODER_SUCCESS}) or the reason for the failure. On
+ *  failure, any out-parameters should be considered uninitialized, except where
+ *  specified.
  */
 enum {
     /**
@@ -115,6 +117,8 @@
 /**
  * Opaque handle for decoding images.
  *
+ * Introduced in API 30
+ *
  * Create using one of the following:
  * - {@link AImageDecoder_createFromAAsset}
  * - {@link AImageDecoder_createFromFd}
@@ -130,6 +134,8 @@
  */
 typedef struct AImageDecoder AImageDecoder;
 
+#if __ANDROID_API__ >= 30
+
 /**
  * Create a new {@link AImageDecoder} from an {@link AAsset}.
  *
@@ -331,7 +337,6 @@
 int AImageDecoder_setTargetSize(AImageDecoder* _Nonnull decoder, int32_t width,
                                 int32_t height) __INTRODUCED_IN(30);
 
-
 /**
  * Compute the dimensions to use for a given sampleSize.
  *
@@ -361,6 +366,7 @@
 int AImageDecoder_computeSampledSize(const AImageDecoder* _Nonnull decoder, int sampleSize,
                                      int32_t* _Nonnull width, int32_t* _Nonnull height)
         __INTRODUCED_IN(30);
+
 /**
  * Specify how to crop the output after scaling (if any).
  *
@@ -388,15 +394,22 @@
  */
 int AImageDecoder_setCrop(AImageDecoder* _Nonnull decoder, ARect crop) __INTRODUCED_IN(30);
 
+#endif // __ANDROID_API__ >= 30
+
 struct AImageDecoderHeaderInfo;
 /**
- * Opaque handle for representing information about the encoded image. Retrieved
- * using {@link AImageDecoder_getHeaderInfo} and passed to methods like
- * {@link AImageDecoderHeaderInfo_getWidth} and
+ * Opaque handle for representing information about the encoded image.
+ *
+ * Introduced in API 30
+ *
+ * Retrieved using {@link AImageDecoder_getHeaderInfo} and passed to methods
+ * like {@link AImageDecoderHeaderInfo_getWidth} and
  * {@link AImageDecoderHeaderInfo_getHeight}.
  */
 typedef struct AImageDecoderHeaderInfo AImageDecoderHeaderInfo;
 
+#if __ANDROID_API__ >= 30
+
 /**
  * Return an opaque handle for reading header info.
  *
@@ -557,14 +570,20 @@
 bool AImageDecoder_isAnimated(AImageDecoder* _Nonnull decoder)
         __INTRODUCED_IN(31);
 
+#endif // __ANDROID_API__ >= 31
+
 enum {
     /*
      * Reported by {@link AImageDecoder_getRepeatCount} if the
      * animation should repeat forever.
+     *
+     * Introduced in API 31
      */
     ANDROID_IMAGE_DECODER_INFINITE = INT32_MAX,
 };
 
+#if __ANDROID_API__ >= 31
+
 /**
  * Report how many times the animation should repeat.
  *
diff --git a/include/android/input.h b/include/android/input.h
index 38af89a..b5d399e 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -856,6 +856,8 @@
     AINPUT_SOURCE_TOUCH_NAVIGATION = 0x00200000 | AINPUT_SOURCE_CLASS_NONE,
     /** joystick */
     AINPUT_SOURCE_JOYSTICK = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK,
+    /** sensor */
+    AINPUT_SOURCE_SENSOR = 0x02000000 | AINPUT_SOURCE_CLASS_JOYSTICK,
     /** rotary encoder */
     AINPUT_SOURCE_ROTARY_ENCODER = 0x00400000 | AINPUT_SOURCE_CLASS_NONE,
 
diff --git a/include/ftl/.clang-format b/include/ftl/.clang-format
new file mode 120000
index 0000000..86b1593
--- /dev/null
+++ b/include/ftl/.clang-format
@@ -0,0 +1 @@
+../../../../build/soong/scripts/system-clang-format-2
\ No newline at end of file
diff --git a/include/ftl/ArrayTraits.h b/include/ftl/ArrayTraits.h
deleted file mode 100644
index ff685c5..0000000
--- a/include/ftl/ArrayTraits.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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 <algorithm>
-#include <iterator>
-#include <new>
-#include <type_traits>
-
-#define FTL_ARRAY_TRAIT(T, U) using U = typename ArrayTraits<T>::U
-
-namespace android::ftl {
-
-template <typename T>
-struct ArrayTraits {
-    using value_type = T;
-    using size_type = size_t;
-    using difference_type = ptrdiff_t;
-
-    using pointer = value_type*;
-    using reference = value_type&;
-    using iterator = pointer;
-    using reverse_iterator = std::reverse_iterator<iterator>;
-
-    using const_pointer = const value_type*;
-    using const_reference = const value_type&;
-    using const_iterator = const_pointer;
-    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-
-    template <typename... Args>
-    static pointer construct_at(const_iterator it, Args&&... args) {
-        void* const ptr = const_cast<void*>(static_cast<const void*>(it));
-        if constexpr (std::is_constructible_v<value_type, Args...>) {
-            // TODO: Replace with std::construct_at in C++20.
-            return new (ptr) value_type(std::forward<Args>(args)...);
-        } else {
-            // Fall back to list initialization.
-            return new (ptr) value_type{std::forward<Args>(args)...};
-        }
-    }
-};
-
-// CRTP mixin to define iterator functions in terms of non-const Self::begin and Self::end.
-template <typename Self, typename T>
-class ArrayIterators {
-    FTL_ARRAY_TRAIT(T, size_type);
-
-    FTL_ARRAY_TRAIT(T, reference);
-    FTL_ARRAY_TRAIT(T, iterator);
-    FTL_ARRAY_TRAIT(T, reverse_iterator);
-
-    FTL_ARRAY_TRAIT(T, const_reference);
-    FTL_ARRAY_TRAIT(T, const_iterator);
-    FTL_ARRAY_TRAIT(T, const_reverse_iterator);
-
-    Self& self() const { return *const_cast<Self*>(static_cast<const Self*>(this)); }
-
-public:
-    const_iterator begin() const { return cbegin(); }
-    const_iterator cbegin() const { return self().begin(); }
-
-    const_iterator end() const { return cend(); }
-    const_iterator cend() const { return self().end(); }
-
-    reverse_iterator rbegin() { return std::make_reverse_iterator(self().end()); }
-    const_reverse_iterator rbegin() const { return crbegin(); }
-    const_reverse_iterator crbegin() const { return self().rbegin(); }
-
-    reverse_iterator rend() { return std::make_reverse_iterator(self().begin()); }
-    const_reverse_iterator rend() const { return crend(); }
-    const_reverse_iterator crend() const { return self().rend(); }
-
-    iterator last() { return self().end() - 1; }
-    const_iterator last() const { return self().last(); }
-
-    reference front() { return *self().begin(); }
-    const_reference front() const { return self().front(); }
-
-    reference back() { return *last(); }
-    const_reference back() const { return self().back(); }
-
-    reference operator[](size_type i) { return *(self().begin() + i); }
-    const_reference operator[](size_type i) const { return self()[i]; }
-};
-
-// Mixin to define comparison operators for an array-like template.
-// TODO: Replace with operator<=> in C++20.
-template <template <typename, size_t> class Array>
-struct ArrayComparators {
-    template <typename T, size_t N, size_t M>
-    friend bool operator==(const Array<T, N>& lhs, const Array<T, M>& rhs) {
-        return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
-    }
-
-    template <typename T, size_t N, size_t M>
-    friend bool operator<(const Array<T, N>& lhs, const Array<T, M>& rhs) {
-        return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
-    }
-
-    template <typename T, size_t N, size_t M>
-    friend bool operator>(const Array<T, N>& lhs, const Array<T, M>& rhs) {
-        return rhs < lhs;
-    }
-
-    template <typename T, size_t N, size_t M>
-    friend bool operator!=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
-        return !(lhs == rhs);
-    }
-
-    template <typename T, size_t N, size_t M>
-    friend bool operator>=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
-        return !(lhs < rhs);
-    }
-
-    template <typename T, size_t N, size_t M>
-    friend bool operator<=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
-        return !(lhs > rhs);
-    }
-};
-
-} // namespace android::ftl
diff --git a/include/ftl/InitializerList.h b/include/ftl/InitializerList.h
deleted file mode 100644
index bb99280..0000000
--- a/include/ftl/InitializerList.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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 <tuple>
-#include <utility>
-
-namespace android::ftl {
-
-// Compile-time counterpart of std::initializer_list<T> that stores per-element constructor
-// arguments with heterogeneous types. For a container with elements of type T, given Sizes
-// (S0, S1, ..., SN), N elements are initialized: the first element is initialized with the
-// first S0 arguments, the second element is initialized with the next S1 arguments, and so
-// on. The list of Types (T0, ..., TM) is flattened, so M is equal to the sum of the Sizes.
-//
-// The InitializerList is created using ftl::init::list, and is consumed by constructors of
-// containers. The function call operator is overloaded such that arguments are accumulated
-// in a tuple with each successive call. For instance, the following calls initialize three
-// strings using different constructors, i.e. string literal, default, and count/character:
-//
-//     ... = ftl::init::list<std::string>("abc")()(3u, '?');
-//
-// The following syntax is a shorthand for key-value pairs, where the first argument is the
-// key, and the rest construct the value. The types of the key and value are deduced if the
-// first pair contains exactly two arguments:
-//
-//     ... = ftl::init::map<int, std::string>(-1, "abc")(-2)(-3, 3u, '?');
-//
-//     ... = ftl::init::map(0, 'a')(1, 'b')(2, 'c');
-//
-// WARNING: The InitializerList returned by an ftl::init::list expression must be consumed
-// immediately, since temporary arguments are destroyed after the full expression. Storing
-// an InitializerList results in dangling references.
-//
-template <typename T, typename Sizes = std::index_sequence<>, typename... Types>
-struct InitializerList;
-
-template <typename T, size_t... Sizes, typename... Types>
-struct InitializerList<T, std::index_sequence<Sizes...>, Types...> {
-    // Creates a superset InitializerList by appending the number of arguments to Sizes, and
-    // expanding Types with forwarding references for each argument.
-    template <typename... Args>
-    [[nodiscard]] constexpr auto operator()(Args&&... args) && -> InitializerList<
-            T, std::index_sequence<Sizes..., sizeof...(Args)>, Types..., Args&&...> {
-        return {std::tuple_cat(std::move(tuple),
-                               std::forward_as_tuple(std::forward<Args>(args)...))};
-    }
-
-    // The temporary InitializerList returned by operator() is bound to an rvalue reference in
-    // container constructors, which extends the lifetime of any temporary arguments that this
-    // tuple refers to until the completion of the full expression containing the construction.
-    std::tuple<Types...> tuple;
-};
-
-template <typename K, typename V>
-struct KeyValue {};
-
-// Shorthand for key-value pairs that assigns the first argument to the key, and the rest to the
-// value. The specialization is on KeyValue rather than std::pair, so that ftl::init::list works
-// with the latter.
-template <typename K, typename V, size_t... Sizes, typename... Types>
-struct InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...> {
-    // Accumulate the three arguments to std::pair's piecewise constructor.
-    template <typename... Args>
-    [[nodiscard]] constexpr auto operator()(K&& k, Args&&... args) && -> InitializerList<
-            KeyValue<K, V>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t,
-            std::tuple<K&&>, std::tuple<Args&&...>> {
-        return {std::tuple_cat(std::move(tuple),
-                               std::forward_as_tuple(std::piecewise_construct,
-                                                     std::forward_as_tuple(std::forward<K>(k)),
-                                                     std::forward_as_tuple(
-                                                             std::forward<Args>(args)...)))};
-    }
-
-    std::tuple<Types...> tuple;
-};
-
-namespace init {
-
-template <typename T, typename... Args>
-[[nodiscard]] constexpr auto list(Args&&... args) {
-    return InitializerList<T>{}(std::forward<Args>(args)...);
-}
-
-template <typename K, typename V, typename... Args>
-[[nodiscard]] constexpr auto map(Args&&... args) {
-    return list<KeyValue<K, V>>(std::forward<Args>(args)...);
-}
-
-template <typename K, typename V>
-[[nodiscard]] constexpr auto map(K&& k, V&& v) {
-    return list<KeyValue<K, V>>(std::forward<K>(k), std::forward<V>(v));
-}
-
-} // namespace init
-} // namespace android::ftl
diff --git a/include/ftl/SmallMap.h b/include/ftl/SmallMap.h
deleted file mode 100644
index 87ae99c..0000000
--- a/include/ftl/SmallMap.h
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * 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 <ftl/InitializerList.h>
-#include <ftl/SmallVector.h>
-
-#include <functional>
-#include <optional>
-#include <type_traits>
-#include <utility>
-
-namespace android::ftl {
-
-// Associative container with unique, unordered keys. Unlike std::unordered_map, key-value pairs are
-// stored in contiguous storage for cache efficiency. The map is allocated statically until its size
-// exceeds N, at which point mappings are relocated to dynamic memory.
-//
-// SmallMap<K, V, 0> unconditionally allocates on the heap.
-//
-// Example usage:
-//
-//    ftl::SmallMap<int, std::string, 3> map;
-//    assert(map.empty());
-//    assert(!map.dynamic());
-//
-//    map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
-//    assert(map.size() == 3u);
-//    assert(!map.dynamic());
-//
-//    assert(map.contains(123));
-//    assert(map.find(42, [](const std::string& s) { return s.size(); }) == 3u);
-//
-//    const auto opt = map.find(-1);
-//    assert(opt);
-//
-//    std::string& ref = *opt;
-//    assert(ref.empty());
-//    ref = "xyz";
-//
-//    assert(map == SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
-//
-template <typename K, typename V, size_t N>
-class SmallMap final {
-    using Map = SmallVector<std::pair<const K, V>, N>;
-
-public:
-    using key_type = K;
-    using mapped_type = V;
-
-    using value_type = typename Map::value_type;
-    using size_type = typename Map::size_type;
-    using difference_type = typename Map::difference_type;
-
-    using reference = typename Map::reference;
-    using iterator = typename Map::iterator;
-
-    using const_reference = typename Map::const_reference;
-    using const_iterator = typename Map::const_iterator;
-
-    // Creates an empty map.
-    SmallMap() = default;
-
-    // Constructs at most N key-value pairs in place by forwarding per-pair constructor arguments.
-    // The template arguments K, V, and N are inferred using the deduction guide defined below.
-    // The syntax for listing pairs is as follows:
-    //
-    //     ftl::SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
-    //
-    //     static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, std::string, 3>>);
-    //     assert(map.size() == 3u);
-    //     assert(map.contains(-1) && map.find(-1)->get().empty());
-    //     assert(map.contains(42) && map.find(42)->get() == "???");
-    //     assert(map.contains(123) && map.find(123)->get() == "abc");
-    //
-    // The types of the key and value are deduced if the first pair contains exactly two arguments:
-    //
-    //     ftl::SmallMap map = ftl::init::map(0, 'a')(1, 'b')(2, 'c');
-    //     static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, char, 3>>);
-    //
-    template <typename U, size_t... Sizes, typename... Types>
-    SmallMap(InitializerList<U, std::index_sequence<Sizes...>, Types...>&& init)
-          : mMap(std::move(init)) {
-        // TODO: Enforce unique keys.
-    }
-
-    size_type max_size() const { return mMap.max_size(); }
-    size_type size() const { return mMap.size(); }
-    bool empty() const { return mMap.empty(); }
-
-    // Returns whether the map is backed by static or dynamic storage.
-    bool dynamic() const { return mMap.dynamic(); }
-
-    iterator begin() { return mMap.begin(); }
-    const_iterator begin() const { return cbegin(); }
-    const_iterator cbegin() const { return mMap.cbegin(); }
-
-    iterator end() { return mMap.end(); }
-    const_iterator end() const { return cend(); }
-    const_iterator cend() const { return mMap.cend(); }
-
-    // Returns whether a mapping exists for the given key.
-    bool contains(const key_type& key) const {
-        return find(key, [](const mapped_type&) {});
-    }
-
-    // Returns a reference to the value for the given key, or std::nullopt if the key was not found.
-    //
-    //     ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
-    //
-    //     const auto opt = map.find('c');
-    //     assert(opt == 'C');
-    //
-    //     char d = 'd';
-    //     const auto ref = map.find('d').value_or(std::ref(d));
-    //     ref.get() = 'D';
-    //     assert(d == 'D');
-    //
-    auto find(const key_type& key) const
-            -> std::optional<std::reference_wrapper<const mapped_type>> {
-        return find(key, [](const mapped_type& v) { return std::cref(v); });
-    }
-
-    auto find(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> {
-        return find(key, [](mapped_type& v) { return std::ref(v); });
-    }
-
-    // Returns the result R of a unary operation F on (a constant or mutable reference to) the value
-    // for the given key, or std::nullopt if the key was not found. If F has a return type of void,
-    // then the Boolean result indicates whether the key was found.
-    //
-    //     ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
-    //
-    //     assert(map.find('c', [](char c) { return std::toupper(c); }) == 'Z');
-    //     assert(map.find('c', [](char& c) { c = std::toupper(c); }));
-    //
-    template <typename F, typename R = std::invoke_result_t<F, const mapped_type&>>
-    auto find(const key_type& key, F f) const
-            -> std::conditional_t<std::is_void_v<R>, bool, std::optional<R>> {
-        for (auto& [k, v] : *this) {
-            if (k == key) {
-                if constexpr (std::is_void_v<R>) {
-                    f(v);
-                    return true;
-                } else {
-                    return f(v);
-                }
-            }
-        }
-
-        return {};
-    }
-
-    template <typename F>
-    auto find(const key_type& key, F f) {
-        return std::as_const(*this).find(key, [&f](const mapped_type& v) {
-            return f(const_cast<mapped_type&>(v));
-        });
-    }
-
-private:
-    Map mMap;
-};
-
-// Deduction guide for in-place constructor.
-template <typename K, typename V, size_t... Sizes, typename... Types>
-SmallMap(InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...>&&)
-        -> SmallMap<K, V, sizeof...(Sizes)>;
-
-// Returns whether the key-value pairs of two maps are equal.
-template <typename K, typename V, size_t N, typename Q, typename W, size_t M>
-bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
-    if (lhs.size() != rhs.size()) return false;
-
-    for (const auto& [k, v] : lhs) {
-        const auto& lv = v;
-        if (!rhs.find(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-// TODO: Remove in C++20.
-template <typename K, typename V, size_t N, typename Q, typename W, size_t M>
-inline bool operator!=(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
-    return !(lhs == rhs);
-}
-
-} // namespace android::ftl
diff --git a/include/ftl/SmallVector.h b/include/ftl/SmallVector.h
deleted file mode 100644
index 2f05a9b..0000000
--- a/include/ftl/SmallVector.h
+++ /dev/null
@@ -1,391 +0,0 @@
-/*
- * 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 <ftl/ArrayTraits.h>
-#include <ftl/StaticVector.h>
-
-#include <algorithm>
-#include <iterator>
-#include <type_traits>
-#include <utility>
-#include <variant>
-#include <vector>
-
-namespace android::ftl {
-
-template <typename>
-struct IsSmallVector;
-
-// ftl::StaticVector that promotes to std::vector when full. SmallVector is a drop-in replacement
-// for std::vector with statically allocated storage for N elements, whose goal is to improve run
-// time by avoiding heap allocation and increasing probability of cache hits. The standard API is
-// augmented by an unstable_erase operation that does not preserve order, and a replace operation
-// that destructively emplaces.
-//
-// SmallVector<T, 0> is a specialization that thinly wraps std::vector.
-//
-// Example usage:
-//
-//     ftl::SmallVector<char, 3> vector;
-//     assert(vector.empty());
-//     assert(!vector.dynamic());
-//
-//     vector = {'a', 'b', 'c'};
-//     assert(vector.size() == 3u);
-//     assert(!vector.dynamic());
-//
-//     vector.push_back('d');
-//     assert(vector.dynamic());
-//
-//     vector.unstable_erase(vector.begin());
-//     assert(vector == (ftl::SmallVector{'d', 'b', 'c'}));
-//
-//     vector.pop_back();
-//     assert(vector.back() == 'b');
-//     assert(vector.dynamic());
-//
-//     const char array[] = "hi";
-//     vector = ftl::SmallVector(array);
-//     assert(vector == (ftl::SmallVector{'h', 'i', '\0'}));
-//     assert(!vector.dynamic());
-//
-//     ftl::SmallVector strings = ftl::init::list<std::string>("abc")
-//                                                            ("123456", 3u)
-//                                                            (3u, '?');
-//     assert(strings.size() == 3u);
-//     assert(!strings.dynamic());
-//
-//     assert(strings[0] == "abc");
-//     assert(strings[1] == "123");
-//     assert(strings[2] == "???");
-//
-template <typename T, size_t N>
-class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> {
-    using Static = StaticVector<T, N>;
-    using Dynamic = SmallVector<T, 0>;
-
-    // TODO: Replace with std::remove_cvref_t in C++20.
-    template <typename U>
-    using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<U>>;
-
-public:
-    FTL_ARRAY_TRAIT(T, value_type);
-    FTL_ARRAY_TRAIT(T, size_type);
-    FTL_ARRAY_TRAIT(T, difference_type);
-
-    FTL_ARRAY_TRAIT(T, pointer);
-    FTL_ARRAY_TRAIT(T, reference);
-    FTL_ARRAY_TRAIT(T, iterator);
-    FTL_ARRAY_TRAIT(T, reverse_iterator);
-
-    FTL_ARRAY_TRAIT(T, const_pointer);
-    FTL_ARRAY_TRAIT(T, const_reference);
-    FTL_ARRAY_TRAIT(T, const_iterator);
-    FTL_ARRAY_TRAIT(T, const_reverse_iterator);
-
-    // Creates an empty vector.
-    SmallVector() = default;
-
-    // Constructs at most N elements. See StaticVector for underlying constructors.
-    template <typename Arg, typename... Args,
-              typename = std::enable_if_t<!IsSmallVector<remove_cvref_t<Arg>>{}>>
-    SmallVector(Arg&& arg, Args&&... args)
-          : mVector(std::in_place_type<Static>, std::forward<Arg>(arg),
-                    std::forward<Args>(args)...) {}
-
-    // Copies at most N elements from a smaller convertible vector.
-    template <typename U, size_t M, typename = std::enable_if_t<M <= N>>
-    SmallVector(const SmallVector<U, M>& other)
-          : SmallVector(IteratorRange, other.begin(), other.end()) {}
-
-    void swap(SmallVector& other) { mVector.swap(other.mVector); }
-
-    // Returns whether the vector is backed by static or dynamic storage.
-    bool dynamic() const { return std::holds_alternative<Dynamic>(mVector); }
-
-    // Avoid std::visit as it generates a dispatch table.
-#define DISPATCH(T, F, ...)                                                                \
-    T F() __VA_ARGS__ {                                                                    \
-        return dynamic() ? std::get<Dynamic>(mVector).F() : std::get<Static>(mVector).F(); \
-    }
-
-    DISPATCH(size_type, max_size, const)
-    DISPATCH(size_type, size, const)
-    DISPATCH(bool, empty, const)
-
-    // noexcept to suppress warning about zero variadic macro arguments.
-    DISPATCH(iterator, begin, noexcept)
-    DISPATCH(const_iterator, begin, const)
-    DISPATCH(const_iterator, cbegin, const)
-
-    DISPATCH(iterator, end, noexcept)
-    DISPATCH(const_iterator, end, const)
-    DISPATCH(const_iterator, cend, const)
-
-    DISPATCH(reverse_iterator, rbegin, noexcept)
-    DISPATCH(const_reverse_iterator, rbegin, const)
-    DISPATCH(const_reverse_iterator, crbegin, const)
-
-    DISPATCH(reverse_iterator, rend, noexcept)
-    DISPATCH(const_reverse_iterator, rend, const)
-    DISPATCH(const_reverse_iterator, crend, const)
-
-    DISPATCH(iterator, last, noexcept)
-    DISPATCH(const_iterator, last, const)
-
-    DISPATCH(reference, front, noexcept)
-    DISPATCH(const_reference, front, const)
-
-    DISPATCH(reference, back, noexcept)
-    DISPATCH(const_reference, back, const)
-
-#undef DISPATCH
-
-    reference operator[](size_type i) {
-        return dynamic() ? std::get<Dynamic>(mVector)[i] : std::get<Static>(mVector)[i];
-    }
-
-    const_reference operator[](size_type i) const { return const_cast<SmallVector&>(*this)[i]; }
-
-    // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
-    // replacing at end() is erroneous.
-    //
-    // The element is emplaced via move constructor, so type T does not need to define copy/move
-    // assignment, e.g. its data members may be const.
-    //
-    // The arguments may directly or indirectly refer to the element being replaced.
-    //
-    // Iterators to the replaced element point to its replacement, and others remain valid.
-    //
-    template <typename... Args>
-    reference replace(const_iterator it, Args&&... args) {
-        if (dynamic()) {
-            return std::get<Dynamic>(mVector).replace(it, std::forward<Args>(args)...);
-        } else {
-            return std::get<Static>(mVector).replace(it, std::forward<Args>(args)...);
-        }
-    }
-
-    // Appends an element, and returns a reference to it.
-    //
-    // If the vector reaches its static or dynamic capacity, then all iterators are invalidated.
-    // Otherwise, only the end() iterator is invalidated.
-    //
-    template <typename... Args>
-    reference emplace_back(Args&&... args) {
-        constexpr auto insertStatic = &Static::template emplace_back<Args...>;
-        constexpr auto insertDynamic = &Dynamic::template emplace_back<Args...>;
-        return *insert<insertStatic, insertDynamic>(std::forward<Args>(args)...);
-    }
-
-    // Appends an element.
-    //
-    // If the vector reaches its static or dynamic capacity, then all iterators are invalidated.
-    // Otherwise, only the end() iterator is invalidated.
-    //
-    void push_back(const value_type& v) {
-        constexpr auto insertStatic =
-                static_cast<bool (Static::*)(const value_type&)>(&Static::push_back);
-        constexpr auto insertDynamic =
-                static_cast<bool (Dynamic::*)(const value_type&)>(&Dynamic::push_back);
-        insert<insertStatic, insertDynamic>(v);
-    }
-
-    void push_back(value_type&& v) {
-        constexpr auto insertStatic =
-                static_cast<bool (Static::*)(value_type&&)>(&Static::push_back);
-        constexpr auto insertDynamic =
-                static_cast<bool (Dynamic::*)(value_type&&)>(&Dynamic::push_back);
-        insert<insertStatic, insertDynamic>(std::move(v));
-    }
-
-    // Removes the last element. The vector must not be empty, or the call is erroneous.
-    //
-    // The last() and end() iterators are invalidated.
-    //
-    void pop_back() {
-        if (dynamic()) {
-            std::get<Dynamic>(mVector).pop_back();
-        } else {
-            std::get<Static>(mVector).pop_back();
-        }
-    }
-
-    // Erases an element, but does not preserve order. Rather than shifting subsequent elements,
-    // this moves the last element to the slot of the erased element.
-    //
-    // The last() and end() iterators, as well as those to the erased element, are invalidated.
-    //
-    void unstable_erase(iterator it) {
-        if (dynamic()) {
-            std::get<Dynamic>(mVector).unstable_erase(it);
-        } else {
-            std::get<Static>(mVector).unstable_erase(it);
-        }
-    }
-
-private:
-    template <auto insertStatic, auto insertDynamic, typename... Args>
-    auto insert(Args&&... args) {
-        if (Dynamic* const vector = std::get_if<Dynamic>(&mVector)) {
-            return (vector->*insertDynamic)(std::forward<Args>(args)...);
-        }
-
-        auto& vector = std::get<Static>(mVector);
-        if (vector.full()) {
-            return (promote(vector).*insertDynamic)(std::forward<Args>(args)...);
-        } else {
-            return (vector.*insertStatic)(std::forward<Args>(args)...);
-        }
-    }
-
-    Dynamic& promote(Static& staticVector) {
-        assert(staticVector.full());
-
-        // Allocate double capacity to reduce probability of reallocation.
-        Dynamic vector;
-        vector.reserve(Static::max_size() * 2);
-        std::move(staticVector.begin(), staticVector.end(), std::back_inserter(vector));
-
-        return mVector.template emplace<Dynamic>(std::move(vector));
-    }
-
-    std::variant<Static, Dynamic> mVector;
-};
-
-// Partial specialization without static storage.
-template <typename T>
-class SmallVector<T, 0> final : ArrayTraits<T>,
-                                ArrayIterators<SmallVector<T, 0>, T>,
-                                std::vector<T> {
-    using ArrayTraits<T>::construct_at;
-
-    using Iter = ArrayIterators<SmallVector, T>;
-    using Impl = std::vector<T>;
-
-    friend Iter;
-
-public:
-    FTL_ARRAY_TRAIT(T, value_type);
-    FTL_ARRAY_TRAIT(T, size_type);
-    FTL_ARRAY_TRAIT(T, difference_type);
-
-    FTL_ARRAY_TRAIT(T, pointer);
-    FTL_ARRAY_TRAIT(T, reference);
-    FTL_ARRAY_TRAIT(T, iterator);
-    FTL_ARRAY_TRAIT(T, reverse_iterator);
-
-    FTL_ARRAY_TRAIT(T, const_pointer);
-    FTL_ARRAY_TRAIT(T, const_reference);
-    FTL_ARRAY_TRAIT(T, const_iterator);
-    FTL_ARRAY_TRAIT(T, const_reverse_iterator);
-
-    using Impl::Impl;
-
-    using Impl::empty;
-    using Impl::max_size;
-    using Impl::size;
-
-    using Impl::reserve;
-
-    // std::vector iterators are not necessarily raw pointers.
-    iterator begin() { return Impl::data(); }
-    iterator end() { return Impl::data() + size(); }
-
-    using Iter::begin;
-    using Iter::end;
-
-    using Iter::cbegin;
-    using Iter::cend;
-
-    using Iter::rbegin;
-    using Iter::rend;
-
-    using Iter::crbegin;
-    using Iter::crend;
-
-    using Iter::last;
-
-    using Iter::back;
-    using Iter::front;
-
-    using Iter::operator[];
-
-    template <typename... Args>
-    reference replace(const_iterator it, Args&&... args) {
-        value_type element{std::forward<Args>(args)...};
-        std::destroy_at(it);
-        // This is only safe because exceptions are disabled.
-        return *construct_at(it, std::move(element));
-    }
-
-    template <typename... Args>
-    iterator emplace_back(Args&&... args) {
-        return &Impl::emplace_back(std::forward<Args>(args)...);
-    }
-
-    bool push_back(const value_type& v) {
-        Impl::push_back(v);
-        return true;
-    }
-
-    bool push_back(value_type&& v) {
-        Impl::push_back(std::move(v));
-        return true;
-    }
-
-    using Impl::pop_back;
-
-    void unstable_erase(iterator it) {
-        if (it != last()) std::iter_swap(it, last());
-        pop_back();
-    }
-
-    void swap(SmallVector& other) { Impl::swap(other); }
-};
-
-template <typename>
-struct IsSmallVector : std::false_type {};
-
-template <typename T, size_t N>
-struct IsSmallVector<SmallVector<T, N>> : std::true_type {};
-
-// Deduction guide for array constructor.
-template <typename T, size_t N>
-SmallVector(T (&)[N]) -> SmallVector<std::remove_cv_t<T>, N>;
-
-// Deduction guide for variadic constructor.
-template <typename T, typename... Us, typename V = std::decay_t<T>,
-          typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>>
-SmallVector(T&&, Us&&...) -> SmallVector<V, 1 + sizeof...(Us)>;
-
-// Deduction guide for in-place constructor.
-template <typename T, size_t... Sizes, typename... Types>
-SmallVector(InitializerList<T, std::index_sequence<Sizes...>, Types...>&&)
-        -> SmallVector<T, sizeof...(Sizes)>;
-
-// Deduction guide for StaticVector conversion.
-template <typename T, size_t N>
-SmallVector(StaticVector<T, N>&&) -> SmallVector<T, N>;
-
-template <typename T, size_t N>
-inline void swap(SmallVector<T, N>& lhs, SmallVector<T, N>& rhs) {
-    lhs.swap(rhs);
-}
-
-} // namespace android::ftl
diff --git a/include/ftl/StaticVector.h b/include/ftl/StaticVector.h
deleted file mode 100644
index c132556..0000000
--- a/include/ftl/StaticVector.h
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * 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 <ftl/ArrayTraits.h>
-#include <ftl/InitializerList.h>
-
-#include <algorithm>
-#include <cassert>
-#include <iterator>
-#include <memory>
-#include <type_traits>
-#include <utility>
-
-namespace android::ftl {
-
-constexpr struct IteratorRangeTag {} IteratorRange;
-
-// Fixed-capacity, statically allocated counterpart of std::vector. Akin to std::array, StaticVector
-// allocates contiguous storage for N elements of type T at compile time, but stores at most (rather
-// than exactly) N elements. Unlike std::array, its default constructor does not require T to have a
-// default constructor, since elements are constructed in place as the vector grows. Operations that
-// insert an element (emplace_back, push_back, etc.) fail when the vector is full. The API otherwise
-// adheres to standard containers, except the unstable_erase operation that does not preserve order,
-// and the replace operation that destructively emplaces.
-//
-// StaticVector<T, 1> is analogous to an iterable std::optional, but StaticVector<T, 0> is an error.
-//
-// Example usage:
-//
-//     ftl::StaticVector<char, 3> vector;
-//     assert(vector.empty());
-//
-//     vector = {'a', 'b'};
-//     assert(vector.size() == 2u);
-//
-//     vector.push_back('c');
-//     assert(vector.full());
-//
-//     assert(!vector.push_back('d'));
-//     assert(vector.size() == 3u);
-//
-//     vector.unstable_erase(vector.begin());
-//     assert(vector == (ftl::StaticVector{'c', 'b'}));
-//
-//     vector.pop_back();
-//     assert(vector.back() == 'c');
-//
-//     const char array[] = "hi";
-//     vector = ftl::StaticVector(array);
-//     assert(vector == (ftl::StaticVector{'h', 'i', '\0'}));
-//
-//     ftl::StaticVector strings = ftl::init::list<std::string>("abc")
-//                                                             ("123456", 3u)
-//                                                             (3u, '?');
-//     assert(strings.size() == 3u);
-//     assert(strings[0] == "abc");
-//     assert(strings[1] == "123");
-//     assert(strings[2] == "???");
-//
-template <typename T, size_t N>
-class StaticVector final : ArrayTraits<T>,
-                           ArrayIterators<StaticVector<T, N>, T>,
-                           ArrayComparators<StaticVector> {
-    static_assert(N > 0);
-
-    using ArrayTraits<T>::construct_at;
-
-    using Iter = ArrayIterators<StaticVector, T>;
-    friend Iter;
-
-    // There is ambiguity when constructing from two iterator-like elements like pointers:
-    // they could be an iterator range, or arguments for in-place construction. Assume the
-    // latter unless they are input iterators and cannot be used to construct elements. If
-    // the former is intended, the caller can pass an IteratorRangeTag to disambiguate.
-    template <typename I, typename Traits = std::iterator_traits<I>>
-    using IsInputIterator = std::conjunction<
-            std::is_base_of<std::input_iterator_tag, typename Traits::iterator_category>,
-            std::negation<std::is_constructible<T, I>>>;
-
-public:
-    FTL_ARRAY_TRAIT(T, value_type);
-    FTL_ARRAY_TRAIT(T, size_type);
-    FTL_ARRAY_TRAIT(T, difference_type);
-
-    FTL_ARRAY_TRAIT(T, pointer);
-    FTL_ARRAY_TRAIT(T, reference);
-    FTL_ARRAY_TRAIT(T, iterator);
-    FTL_ARRAY_TRAIT(T, reverse_iterator);
-
-    FTL_ARRAY_TRAIT(T, const_pointer);
-    FTL_ARRAY_TRAIT(T, const_reference);
-    FTL_ARRAY_TRAIT(T, const_iterator);
-    FTL_ARRAY_TRAIT(T, const_reverse_iterator);
-
-    // Creates an empty vector.
-    StaticVector() = default;
-
-    // Copies and moves a vector, respectively.
-    StaticVector(const StaticVector& other)
-          : StaticVector(IteratorRange, other.begin(), other.end()) {}
-    StaticVector(StaticVector&& other) { swap<Empty>(other); }
-
-    // Copies at most N elements from a smaller convertible vector.
-    template <typename U, size_t M, typename = std::enable_if_t<M <= N>>
-    StaticVector(const StaticVector<U, M>& other)
-          : StaticVector(IteratorRange, other.begin(), other.end()) {}
-
-    // Copies at most N elements from an array.
-    template <typename U, size_t M>
-    explicit StaticVector(U (&array)[M])
-          : StaticVector(IteratorRange, std::begin(array), std::end(array)) {}
-
-    // Copies at most N elements from the range [first, last).
-    //
-    // IteratorRangeTag disambiguates with initialization from two iterator-like elements.
-    //
-    template <typename Iterator, typename = std::enable_if_t<IsInputIterator<Iterator>{}>>
-    StaticVector(Iterator first, Iterator last) : StaticVector(IteratorRange, first, last) {
-        using V = typename std::iterator_traits<Iterator>::value_type;
-        static_assert(std::is_constructible_v<value_type, V>, "Incompatible iterator range");
-    }
-
-    template <typename Iterator>
-    StaticVector(IteratorRangeTag, Iterator first, Iterator last)
-          : mSize(std::min(max_size(), static_cast<size_type>(std::distance(first, last)))) {
-        std::uninitialized_copy(first, first + mSize, begin());
-    }
-
-    // Constructs at most N elements. The template arguments T and N are inferred using the
-    // deduction guide defined below. Note that T is determined from the first element, and
-    // subsequent elements must have convertible types:
-    //
-    //     ftl::StaticVector vector = {1, 2, 3};
-    //     static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<int, 3>>);
-    //
-    //     const auto copy = "quince"s;
-    //     auto move = "tart"s;
-    //     ftl::StaticVector vector = {copy, std::move(move)};
-    //
-    //     static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 2>>);
-    //
-    template <typename E, typename... Es,
-              typename = std::enable_if_t<std::is_constructible_v<value_type, E>>>
-    StaticVector(E&& element, Es&&... elements)
-          : StaticVector(std::index_sequence<0>{}, std::forward<E>(element),
-                         std::forward<Es>(elements)...) {
-        static_assert(sizeof...(elements) < N, "Too many elements");
-    }
-
-    // Constructs at most N elements in place by forwarding per-element constructor arguments. The
-    // template arguments T and N are inferred using the deduction guide defined below. The syntax
-    // for listing arguments is as follows:
-    //
-    //     ftl::StaticVector vector = ftl::init::list<std::string>("abc")()(3u, '?');
-    //
-    //     static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 3>>);
-    //     assert(vector.full());
-    //     assert(vector[0] == "abc");
-    //     assert(vector[1].empty());
-    //     assert(vector[2] == "???");
-    //
-    template <typename U, size_t Size, size_t... Sizes, typename... Types>
-    StaticVector(InitializerList<U, std::index_sequence<Size, Sizes...>, Types...>&& init)
-          : StaticVector(std::index_sequence<0, 0, Size>{}, std::make_index_sequence<Size>{},
-                         std::index_sequence<Sizes...>{}, init.tuple) {}
-
-    ~StaticVector() { std::destroy(begin(), end()); }
-
-    StaticVector& operator=(const StaticVector& other) {
-        StaticVector copy(other);
-        swap(copy);
-        return *this;
-    }
-
-    StaticVector& operator=(StaticVector&& other) {
-        std::destroy(begin(), end());
-        mSize = 0;
-        swap<Empty>(other);
-        return *this;
-    }
-
-    template <typename = void>
-    void swap(StaticVector&);
-
-    static constexpr size_type max_size() { return N; }
-    size_type size() const { return mSize; }
-
-    bool empty() const { return size() == 0; }
-    bool full() const { return size() == max_size(); }
-
-    iterator begin() { return std::launder(reinterpret_cast<pointer>(mData)); }
-    iterator end() { return begin() + size(); }
-
-    using Iter::begin;
-    using Iter::end;
-
-    using Iter::cbegin;
-    using Iter::cend;
-
-    using Iter::rbegin;
-    using Iter::rend;
-
-    using Iter::crbegin;
-    using Iter::crend;
-
-    using Iter::last;
-
-    using Iter::back;
-    using Iter::front;
-
-    using Iter::operator[];
-
-    // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
-    // replacing at end() is erroneous.
-    //
-    // The element is emplaced via move constructor, so type T does not need to define copy/move
-    // assignment, e.g. its data members may be const.
-    //
-    // The arguments may directly or indirectly refer to the element being replaced.
-    //
-    // Iterators to the replaced element point to its replacement, and others remain valid.
-    //
-    template <typename... Args>
-    reference replace(const_iterator it, Args&&... args) {
-        value_type element{std::forward<Args>(args)...};
-        std::destroy_at(it);
-        // This is only safe because exceptions are disabled.
-        return *construct_at(it, std::move(element));
-    }
-
-    // Appends an element, and returns an iterator to it. If the vector is full, the element is not
-    // inserted, and the end() iterator is returned.
-    //
-    // On success, the end() iterator is invalidated.
-    //
-    template <typename... Args>
-    iterator emplace_back(Args&&... args) {
-        if (full()) return end();
-        const iterator it = construct_at(end(), std::forward<Args>(args)...);
-        ++mSize;
-        return it;
-    }
-
-    // Appends an element unless the vector is full, and returns whether the element was inserted.
-    //
-    // On success, the end() iterator is invalidated.
-    //
-    bool push_back(const value_type& v) {
-        // Two statements for sequence point.
-        const iterator it = emplace_back(v);
-        return it != end();
-    }
-
-    bool push_back(value_type&& v) {
-        // Two statements for sequence point.
-        const iterator it = emplace_back(std::move(v));
-        return it != end();
-    }
-
-    // Removes the last element. The vector must not be empty, or the call is erroneous.
-    //
-    // The last() and end() iterators are invalidated.
-    //
-    void pop_back() { unstable_erase(last()); }
-
-    // Erases an element, but does not preserve order. Rather than shifting subsequent elements,
-    // this moves the last element to the slot of the erased element.
-    //
-    // The last() and end() iterators, as well as those to the erased element, are invalidated.
-    //
-    void unstable_erase(const_iterator it) {
-        std::destroy_at(it);
-        if (it != last()) {
-            // Move last element and destroy its source for destructor side effects. This is only
-            // safe because exceptions are disabled.
-            construct_at(it, std::move(back()));
-            std::destroy_at(last());
-        }
-        --mSize;
-    }
-
-private:
-    struct Empty {};
-
-    // Recursion for variadic constructor.
-    template <size_t I, typename E, typename... Es>
-    StaticVector(std::index_sequence<I>, E&& element, Es&&... elements)
-          : StaticVector(std::index_sequence<I + 1>{}, std::forward<Es>(elements)...) {
-        construct_at(begin() + I, std::forward<E>(element));
-    }
-
-    // Base case for variadic constructor.
-    template <size_t I>
-    explicit StaticVector(std::index_sequence<I>) : mSize(I) {}
-
-    // Recursion for in-place constructor.
-    //
-    // Construct element I by extracting its arguments from the InitializerList tuple. ArgIndex
-    // is the position of its first argument in Args, and ArgCount is the number of arguments.
-    // The Indices sequence corresponds to [0, ArgCount).
-    //
-    // The Sizes sequence lists the argument counts for elements after I, so Size is the ArgCount
-    // for the next element. The recursion stops when Sizes is empty for the last element.
-    //
-    template <size_t I, size_t ArgIndex, size_t ArgCount, size_t... Indices, size_t Size,
-              size_t... Sizes, typename... Args>
-    StaticVector(std::index_sequence<I, ArgIndex, ArgCount>, std::index_sequence<Indices...>,
-                 std::index_sequence<Size, Sizes...>, std::tuple<Args...>& tuple)
-          : StaticVector(std::index_sequence<I + 1, ArgIndex + ArgCount, Size>{},
-                         std::make_index_sequence<Size>{}, std::index_sequence<Sizes...>{}, tuple) {
-        construct_at(begin() + I, std::move(std::get<ArgIndex + Indices>(tuple))...);
-    }
-
-    // Base case for in-place constructor.
-    template <size_t I, size_t ArgIndex, size_t ArgCount, size_t... Indices, typename... Args>
-    StaticVector(std::index_sequence<I, ArgIndex, ArgCount>, std::index_sequence<Indices...>,
-                 std::index_sequence<>, std::tuple<Args...>& tuple)
-          : mSize(I + 1) {
-        construct_at(begin() + I, std::move(std::get<ArgIndex + Indices>(tuple))...);
-    }
-
-    size_type mSize = 0;
-    std::aligned_storage_t<sizeof(value_type), alignof(value_type)> mData[N];
-};
-
-// Deduction guide for array constructor.
-template <typename T, size_t N>
-StaticVector(T (&)[N]) -> StaticVector<std::remove_cv_t<T>, N>;
-
-// Deduction guide for variadic constructor.
-template <typename T, typename... Us, typename V = std::decay_t<T>,
-          typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>>
-StaticVector(T&&, Us&&...) -> StaticVector<V, 1 + sizeof...(Us)>;
-
-// Deduction guide for in-place constructor.
-template <typename T, size_t... Sizes, typename... Types>
-StaticVector(InitializerList<T, std::index_sequence<Sizes...>, Types...>&&)
-        -> StaticVector<T, sizeof...(Sizes)>;
-
-template <typename T, size_t N>
-template <typename E>
-void StaticVector<T, N>::swap(StaticVector& other) {
-    auto [to, from] = std::make_pair(this, &other);
-    if (from == this) return;
-
-    // Assume this vector has fewer elements, so the excess of the other vector will be moved to it.
-    auto [min, max] = std::make_pair(size(), other.size());
-
-    // No elements to swap if moving into an empty vector.
-    if constexpr (std::is_same_v<E, Empty>) {
-        assert(min == 0);
-    } else {
-        if (min > max) {
-            std::swap(from, to);
-            std::swap(min, max);
-        }
-
-        // Swap elements [0, min).
-        std::swap_ranges(begin(), begin() + min, other.begin());
-
-        // No elements to move if sizes are equal.
-        if (min == max) return;
-    }
-
-    // Move elements [min, max) and destroy their source for destructor side effects.
-    const auto [first, last] = std::make_pair(from->begin() + min, from->begin() + max);
-    std::uninitialized_move(first, last, to->begin() + min);
-    std::destroy(first, last);
-
-    std::swap(mSize, other.mSize);
-}
-
-template <typename T, size_t N>
-inline void swap(StaticVector<T, N>& lhs, StaticVector<T, N>& rhs) {
-    lhs.swap(rhs);
-}
-
-} // namespace android::ftl
diff --git a/include/ftl/array_traits.h b/include/ftl/array_traits.h
new file mode 100644
index 0000000..1265fa1
--- /dev/null
+++ b/include/ftl/array_traits.h
@@ -0,0 +1,135 @@
+/*
+ * 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 <algorithm>
+#include <iterator>
+#include <new>
+#include <type_traits>
+
+#define FTL_ARRAY_TRAIT(T, U) using U = typename ArrayTraits<T>::U
+
+namespace android::ftl {
+
+template <typename T>
+struct ArrayTraits {
+  using value_type = T;
+  using size_type = std::size_t;
+  using difference_type = std::ptrdiff_t;
+
+  using pointer = value_type*;
+  using reference = value_type&;
+  using iterator = pointer;
+  using reverse_iterator = std::reverse_iterator<iterator>;
+
+  using const_pointer = const value_type*;
+  using const_reference = const value_type&;
+  using const_iterator = const_pointer;
+  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+  template <typename... Args>
+  static pointer construct_at(const_iterator it, Args&&... args) {
+    void* const ptr = const_cast<void*>(static_cast<const void*>(it));
+    if constexpr (std::is_constructible_v<value_type, Args...>) {
+      // TODO: Replace with std::construct_at in C++20.
+      return new (ptr) value_type(std::forward<Args>(args)...);
+    } else {
+      // Fall back to list initialization.
+      return new (ptr) value_type{std::forward<Args>(args)...};
+    }
+  }
+};
+
+// CRTP mixin to define iterator functions in terms of non-const Self::begin and Self::end.
+template <typename Self, typename T>
+class ArrayIterators {
+  FTL_ARRAY_TRAIT(T, size_type);
+
+  FTL_ARRAY_TRAIT(T, reference);
+  FTL_ARRAY_TRAIT(T, iterator);
+  FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+  FTL_ARRAY_TRAIT(T, const_reference);
+  FTL_ARRAY_TRAIT(T, const_iterator);
+  FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+  Self& self() const { return *const_cast<Self*>(static_cast<const Self*>(this)); }
+
+ public:
+  const_iterator begin() const { return cbegin(); }
+  const_iterator cbegin() const { return self().begin(); }
+
+  const_iterator end() const { return cend(); }
+  const_iterator cend() const { return self().end(); }
+
+  reverse_iterator rbegin() { return std::make_reverse_iterator(self().end()); }
+  const_reverse_iterator rbegin() const { return crbegin(); }
+  const_reverse_iterator crbegin() const { return self().rbegin(); }
+
+  reverse_iterator rend() { return std::make_reverse_iterator(self().begin()); }
+  const_reverse_iterator rend() const { return crend(); }
+  const_reverse_iterator crend() const { return self().rend(); }
+
+  iterator last() { return self().end() - 1; }
+  const_iterator last() const { return self().last(); }
+
+  reference front() { return *self().begin(); }
+  const_reference front() const { return self().front(); }
+
+  reference back() { return *last(); }
+  const_reference back() const { return self().back(); }
+
+  reference operator[](size_type i) { return *(self().begin() + i); }
+  const_reference operator[](size_type i) const { return self()[i]; }
+};
+
+// Mixin to define comparison operators for an array-like template.
+// TODO: Replace with operator<=> in C++20.
+template <template <typename, std::size_t> class Array>
+struct ArrayComparators {
+  template <typename T, std::size_t N, std::size_t M>
+  friend bool operator==(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+    return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
+  }
+
+  template <typename T, std::size_t N, std::size_t M>
+  friend bool operator<(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+    return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+  }
+
+  template <typename T, std::size_t N, std::size_t M>
+  friend bool operator>(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+    return rhs < lhs;
+  }
+
+  template <typename T, std::size_t N, std::size_t M>
+  friend bool operator!=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+    return !(lhs == rhs);
+  }
+
+  template <typename T, std::size_t N, std::size_t M>
+  friend bool operator>=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+    return !(lhs < rhs);
+  }
+
+  template <typename T, std::size_t N, std::size_t M>
+  friend bool operator<=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+    return !(lhs > rhs);
+  }
+};
+
+}  // namespace android::ftl
diff --git a/include/ftl/future.h b/include/ftl/future.h
new file mode 100644
index 0000000..dd6358f
--- /dev/null
+++ b/include/ftl/future.h
@@ -0,0 +1,109 @@
+/*
+ * 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 <future>
+#include <type_traits>
+#include <utility>
+
+namespace android::ftl {
+
+// Creates a future that defers a function call until its result is queried.
+//
+//   auto future = ftl::defer([](int x) { return x + 1; }, 99);
+//   assert(future.get() == 100);
+//
+template <typename F, typename... Args>
+inline auto defer(F&& f, Args&&... args) {
+  return std::async(std::launch::deferred, std::forward<F>(f), std::forward<Args>(args)...);
+}
+
+// Creates a future that wraps a value.
+//
+//   auto future = ftl::yield(42);
+//   assert(future.get() == 42);
+//
+//   auto ptr = std::make_unique<char>('!');
+//   auto future = ftl::yield(std::move(ptr));
+//   assert(*future.get() == '!');
+//
+template <typename T>
+inline std::future<T> yield(T&& v) {
+  return defer([](T&& v) { return std::forward<T>(v); }, std::forward<T>(v));
+}
+
+namespace details {
+
+template <typename T>
+struct future_result {
+  using type = T;
+};
+
+template <typename T>
+struct future_result<std::future<T>> {
+  using type = T;
+};
+
+template <typename T>
+using future_result_t = typename future_result<T>::type;
+
+// Attaches a continuation to a future. The continuation is a function that maps T to either R or
+// std::future<R>. In the former case, the chain wraps the result in a future as if by ftl::yield.
+//
+//   auto future = ftl::yield(123);
+//   std::future<char> futures[] = {ftl::yield('a'), ftl::yield('b')};
+//
+//   std::future<char> chain =
+//       ftl::chain(std::move(future))
+//           .then([](int x) { return static_cast<std::size_t>(x % 2); })
+//           .then([&futures](std::size_t i) { return std::move(futures[i]); });
+//
+//   assert(chain.get() == 'b');
+//
+template <typename T>
+struct Chain {
+  // Implicit conversion.
+  Chain(std::future<T>&& f) : future(std::move(f)) {}
+  operator std::future<T>&&() && { return std::move(future); }
+
+  T get() && { return future.get(); }
+
+  template <typename F, typename R = std::invoke_result_t<F, T>>
+  auto then(F&& op) && -> Chain<future_result_t<R>> {
+    return defer(
+        [](auto&& f, F&& op) {
+          R r = op(f.get());
+          if constexpr (std::is_same_v<R, future_result_t<R>>) {
+            return r;
+          } else {
+            return r.get();
+          }
+        },
+        std::move(future), std::forward<F>(op));
+  }
+
+  std::future<T> future;
+};
+
+}  // namespace details
+
+template <typename T>
+inline auto chain(std::future<T>&& f) -> details::Chain<T> {
+  return std::move(f);
+}
+
+}  // namespace android::ftl
diff --git a/include/ftl/initializer_list.h b/include/ftl/initializer_list.h
new file mode 100644
index 0000000..769c09f
--- /dev/null
+++ b/include/ftl/initializer_list.h
@@ -0,0 +1,108 @@
+/*
+ * 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 <tuple>
+#include <utility>
+
+namespace android::ftl {
+
+// Compile-time counterpart of std::initializer_list<T> that stores per-element constructor
+// arguments with heterogeneous types. For a container with elements of type T, given Sizes
+// (S0, S1, ..., SN), N elements are initialized: the first element is initialized with the
+// first S0 arguments, the second element is initialized with the next S1 arguments, and so
+// on. The list of Types (T0, ..., TM) is flattened, so M is equal to the sum of the Sizes.
+//
+// An InitializerList is created using ftl::init::list, and is consumed by constructors of
+// containers. The function call operator is overloaded such that arguments are accumulated
+// in a tuple with each successive call. For instance, the following calls initialize three
+// strings using different constructors, i.e. string literal, default, and count/character:
+//
+//   ... = ftl::init::list<std::string>("abc")()(3u, '?');
+//
+// The following syntax is a shorthand for key-value pairs, where the first argument is the
+// key, and the rest construct the value. The types of the key and value are deduced if the
+// first pair contains exactly two arguments:
+//
+//   ... = ftl::init::map<int, std::string>(-1, "abc")(-2)(-3, 3u, '?');
+//
+//   ... = ftl::init::map(0, 'a')(1, 'b')(2, 'c');
+//
+// WARNING: The InitializerList returned by an ftl::init::list expression must be consumed
+// immediately, since temporary arguments are destroyed after the full expression. Storing
+// an InitializerList results in dangling references.
+//
+template <typename T, typename Sizes = std::index_sequence<>, typename... Types>
+struct InitializerList;
+
+template <typename T, std::size_t... Sizes, typename... Types>
+struct InitializerList<T, std::index_sequence<Sizes...>, Types...> {
+  // Creates a superset InitializerList by appending the number of arguments to Sizes, and
+  // expanding Types with forwarding references for each argument.
+  template <typename... Args>
+  [[nodiscard]] constexpr auto operator()(Args&&... args) && -> InitializerList<
+      T, std::index_sequence<Sizes..., sizeof...(Args)>, Types..., Args&&...> {
+    return {std::tuple_cat(std::move(tuple), std::forward_as_tuple(std::forward<Args>(args)...))};
+  }
+
+  // The temporary InitializerList returned by operator() is bound to an rvalue reference in
+  // container constructors, which extends the lifetime of any temporary arguments that this
+  // tuple refers to until the completion of the full expression containing the construction.
+  std::tuple<Types...> tuple;
+};
+
+template <typename K, typename V>
+struct KeyValue {};
+
+// Shorthand for key-value pairs that assigns the first argument to the key, and the rest to the
+// value. The specialization is on KeyValue rather than std::pair, so that ftl::init::list works
+// with the latter.
+template <typename K, typename V, std::size_t... Sizes, typename... Types>
+struct InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...> {
+  // Accumulate the three arguments to std::pair's piecewise constructor.
+  template <typename... Args>
+  [[nodiscard]] constexpr auto operator()(K&& k, Args&&... args) && -> InitializerList<
+      KeyValue<K, V>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t,
+      std::tuple<K&&>, std::tuple<Args&&...>> {
+    return {std::tuple_cat(
+        std::move(tuple),
+        std::forward_as_tuple(std::piecewise_construct, std::forward_as_tuple(std::forward<K>(k)),
+                              std::forward_as_tuple(std::forward<Args>(args)...)))};
+  }
+
+  std::tuple<Types...> tuple;
+};
+
+namespace init {
+
+template <typename T, typename... Args>
+[[nodiscard]] constexpr auto list(Args&&... args) {
+  return InitializerList<T>{}(std::forward<Args>(args)...);
+}
+
+template <typename K, typename V, typename... Args>
+[[nodiscard]] constexpr auto map(Args&&... args) {
+  return list<KeyValue<K, V>>(std::forward<Args>(args)...);
+}
+
+template <typename K, typename V>
+[[nodiscard]] constexpr auto map(K&& k, V&& v) {
+  return list<KeyValue<K, V>>(std::forward<K>(k), std::forward<V>(v));
+}
+
+}  // namespace init
+}  // namespace android::ftl
diff --git a/include/ftl/small_map.h b/include/ftl/small_map.h
new file mode 100644
index 0000000..84c15eb
--- /dev/null
+++ b/include/ftl/small_map.h
@@ -0,0 +1,203 @@
+/*
+ * 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 <ftl/initializer_list.h>
+#include <ftl/small_vector.h>
+
+#include <functional>
+#include <optional>
+#include <type_traits>
+#include <utility>
+
+namespace android::ftl {
+
+// Associative container with unique, unordered keys. Unlike std::unordered_map, key-value pairs are
+// stored in contiguous storage for cache efficiency. The map is allocated statically until its size
+// exceeds N, at which point mappings are relocated to dynamic memory.
+//
+// SmallMap<K, V, 0> unconditionally allocates on the heap.
+//
+// Example usage:
+//
+//   ftl::SmallMap<int, std::string, 3> map;
+//   assert(map.empty());
+//   assert(!map.dynamic());
+//
+//   map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+//   assert(map.size() == 3u);
+//   assert(!map.dynamic());
+//
+//   assert(map.contains(123));
+//   assert(map.find(42, [](const std::string& s) { return s.size(); }) == 3u);
+//
+//   const auto opt = map.find(-1);
+//   assert(opt);
+//
+//   std::string& ref = *opt;
+//   assert(ref.empty());
+//   ref = "xyz";
+//
+//   assert(map == SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
+//
+template <typename K, typename V, std::size_t N>
+class SmallMap final {
+  using Map = SmallVector<std::pair<const K, V>, N>;
+
+ public:
+  using key_type = K;
+  using mapped_type = V;
+
+  using value_type = typename Map::value_type;
+  using size_type = typename Map::size_type;
+  using difference_type = typename Map::difference_type;
+
+  using reference = typename Map::reference;
+  using iterator = typename Map::iterator;
+
+  using const_reference = typename Map::const_reference;
+  using const_iterator = typename Map::const_iterator;
+
+  // Creates an empty map.
+  SmallMap() = default;
+
+  // Constructs at most N key-value pairs in place by forwarding per-pair constructor arguments.
+  // The template arguments K, V, and N are inferred using the deduction guide defined below.
+  // The syntax for listing pairs is as follows:
+  //
+  //   ftl::SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+  //
+  //   static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, std::string, 3>>);
+  //   assert(map.size() == 3u);
+  //   assert(map.contains(-1) && map.find(-1)->get().empty());
+  //   assert(map.contains(42) && map.find(42)->get() == "???");
+  //   assert(map.contains(123) && map.find(123)->get() == "abc");
+  //
+  // The types of the key and value are deduced if the first pair contains exactly two arguments:
+  //
+  //   ftl::SmallMap map = ftl::init::map(0, 'a')(1, 'b')(2, 'c');
+  //   static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, char, 3>>);
+  //
+  template <typename U, std::size_t... Sizes, typename... Types>
+  SmallMap(InitializerList<U, std::index_sequence<Sizes...>, Types...>&& list)
+      : map_(std::move(list)) {
+    // TODO: Enforce unique keys.
+  }
+
+  size_type max_size() const { return map_.max_size(); }
+  size_type size() const { return map_.size(); }
+  bool empty() const { return map_.empty(); }
+
+  // Returns whether the map is backed by static or dynamic storage.
+  bool dynamic() const { return map_.dynamic(); }
+
+  iterator begin() { return map_.begin(); }
+  const_iterator begin() const { return cbegin(); }
+  const_iterator cbegin() const { return map_.cbegin(); }
+
+  iterator end() { return map_.end(); }
+  const_iterator end() const { return cend(); }
+  const_iterator cend() const { return map_.cend(); }
+
+  // Returns whether a mapping exists for the given key.
+  bool contains(const key_type& key) const {
+    return find(key, [](const mapped_type&) {});
+  }
+
+  // Returns a reference to the value for the given key, or std::nullopt if the key was not found.
+  //
+  //   ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+  //
+  //   const auto opt = map.find('c');
+  //   assert(opt == 'C');
+  //
+  //   char d = 'd';
+  //   const auto ref = map.find('d').value_or(std::ref(d));
+  //   ref.get() = 'D';
+  //   assert(d == 'D');
+  //
+  auto find(const key_type& key) const -> std::optional<std::reference_wrapper<const mapped_type>> {
+    return find(key, [](const mapped_type& v) { return std::cref(v); });
+  }
+
+  auto find(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> {
+    return find(key, [](mapped_type& v) { return std::ref(v); });
+  }
+
+  // Returns the result R of a unary operation F on (a constant or mutable reference to) the value
+  // for the given key, or std::nullopt if the key was not found. If F has a return type of void,
+  // then the Boolean result indicates whether the key was found.
+  //
+  //   ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+  //
+  //   assert(map.find('c', [](char c) { return std::toupper(c); }) == 'Z');
+  //   assert(map.find('c', [](char& c) { c = std::toupper(c); }));
+  //
+  template <typename F, typename R = std::invoke_result_t<F, const mapped_type&>>
+  auto find(const key_type& key, F f) const
+      -> std::conditional_t<std::is_void_v<R>, bool, std::optional<R>> {
+    for (auto& [k, v] : *this) {
+      if (k == key) {
+        if constexpr (std::is_void_v<R>) {
+          f(v);
+          return true;
+        } else {
+          return f(v);
+        }
+      }
+    }
+
+    return {};
+  }
+
+  template <typename F>
+  auto find(const key_type& key, F f) {
+    return std::as_const(*this).find(
+        key, [&f](const mapped_type& v) { return f(const_cast<mapped_type&>(v)); });
+  }
+
+ private:
+  Map map_;
+};
+
+// Deduction guide for in-place constructor.
+template <typename K, typename V, std::size_t... Sizes, typename... Types>
+SmallMap(InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...>&&)
+    -> SmallMap<K, V, sizeof...(Sizes)>;
+
+// Returns whether the key-value pairs of two maps are equal.
+template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M>
+bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
+  if (lhs.size() != rhs.size()) return false;
+
+  for (const auto& [k, v] : lhs) {
+    const auto& lv = v;
+    if (!rhs.find(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+// TODO: Remove in C++20.
+template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M>
+inline bool operator!=(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
+  return !(lhs == rhs);
+}
+
+}  // namespace android::ftl
diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h
new file mode 100644
index 0000000..cb0ae35
--- /dev/null
+++ b/include/ftl/small_vector.h
@@ -0,0 +1,387 @@
+/*
+ * 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 <ftl/array_traits.h>
+#include <ftl/static_vector.h>
+
+#include <algorithm>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+#include <variant>
+#include <vector>
+
+namespace android::ftl {
+
+template <typename>
+struct is_small_vector;
+
+// ftl::StaticVector that promotes to std::vector when full. SmallVector is a drop-in replacement
+// for std::vector with statically allocated storage for N elements, whose goal is to improve run
+// time by avoiding heap allocation and increasing probability of cache hits. The standard API is
+// augmented by an unstable_erase operation that does not preserve order, and a replace operation
+// that destructively emplaces.
+//
+// SmallVector<T, 0> is a specialization that thinly wraps std::vector.
+//
+// Example usage:
+//
+//   ftl::SmallVector<char, 3> vector;
+//   assert(vector.empty());
+//   assert(!vector.dynamic());
+//
+//   vector = {'a', 'b', 'c'};
+//   assert(vector.size() == 3u);
+//   assert(!vector.dynamic());
+//
+//   vector.push_back('d');
+//   assert(vector.dynamic());
+//
+//   vector.unstable_erase(vector.begin());
+//   assert(vector == (ftl::SmallVector{'d', 'b', 'c'}));
+//
+//   vector.pop_back();
+//   assert(vector.back() == 'b');
+//   assert(vector.dynamic());
+//
+//   const char array[] = "hi";
+//   vector = ftl::SmallVector(array);
+//   assert(vector == (ftl::SmallVector{'h', 'i', '\0'}));
+//   assert(!vector.dynamic());
+//
+//   ftl::SmallVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?');
+//   assert(strings.size() == 3u);
+//   assert(!strings.dynamic());
+//
+//   assert(strings[0] == "abc");
+//   assert(strings[1] == "123");
+//   assert(strings[2] == "???");
+//
+template <typename T, std::size_t N>
+class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> {
+  using Static = StaticVector<T, N>;
+  using Dynamic = SmallVector<T, 0>;
+
+  // TODO: Replace with std::remove_cvref_t in C++20.
+  template <typename U>
+  using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<U>>;
+
+ public:
+  FTL_ARRAY_TRAIT(T, value_type);
+  FTL_ARRAY_TRAIT(T, size_type);
+  FTL_ARRAY_TRAIT(T, difference_type);
+
+  FTL_ARRAY_TRAIT(T, pointer);
+  FTL_ARRAY_TRAIT(T, reference);
+  FTL_ARRAY_TRAIT(T, iterator);
+  FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+  FTL_ARRAY_TRAIT(T, const_pointer);
+  FTL_ARRAY_TRAIT(T, const_reference);
+  FTL_ARRAY_TRAIT(T, const_iterator);
+  FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+  // Creates an empty vector.
+  SmallVector() = default;
+
+  // Constructs at most N elements. See StaticVector for underlying constructors.
+  template <typename Arg, typename... Args,
+            typename = std::enable_if_t<!is_small_vector<remove_cvref_t<Arg>>{}>>
+  SmallVector(Arg&& arg, Args&&... args)
+      : vector_(std::in_place_type<Static>, std::forward<Arg>(arg), std::forward<Args>(args)...) {}
+
+  // Copies at most N elements from a smaller convertible vector.
+  template <typename U, std::size_t M, typename = std::enable_if_t<M <= N>>
+  SmallVector(const SmallVector<U, M>& other)
+      : SmallVector(kIteratorRange, other.begin(), other.end()) {}
+
+  void swap(SmallVector& other) { vector_.swap(other.vector_); }
+
+  // Returns whether the vector is backed by static or dynamic storage.
+  bool dynamic() const { return std::holds_alternative<Dynamic>(vector_); }
+
+  // Avoid std::visit as it generates a dispatch table.
+#define DISPATCH(T, F, ...)                                                            \
+  T F() __VA_ARGS__ {                                                                  \
+    return dynamic() ? std::get<Dynamic>(vector_).F() : std::get<Static>(vector_).F(); \
+  }
+
+  DISPATCH(size_type, max_size, const)
+  DISPATCH(size_type, size, const)
+  DISPATCH(bool, empty, const)
+
+  // noexcept to suppress warning about zero variadic macro arguments.
+  DISPATCH(iterator, begin, noexcept)
+  DISPATCH(const_iterator, begin, const)
+  DISPATCH(const_iterator, cbegin, const)
+
+  DISPATCH(iterator, end, noexcept)
+  DISPATCH(const_iterator, end, const)
+  DISPATCH(const_iterator, cend, const)
+
+  DISPATCH(reverse_iterator, rbegin, noexcept)
+  DISPATCH(const_reverse_iterator, rbegin, const)
+  DISPATCH(const_reverse_iterator, crbegin, const)
+
+  DISPATCH(reverse_iterator, rend, noexcept)
+  DISPATCH(const_reverse_iterator, rend, const)
+  DISPATCH(const_reverse_iterator, crend, const)
+
+  DISPATCH(iterator, last, noexcept)
+  DISPATCH(const_iterator, last, const)
+
+  DISPATCH(reference, front, noexcept)
+  DISPATCH(const_reference, front, const)
+
+  DISPATCH(reference, back, noexcept)
+  DISPATCH(const_reference, back, const)
+
+#undef DISPATCH
+
+  reference operator[](size_type i) {
+    return dynamic() ? std::get<Dynamic>(vector_)[i] : std::get<Static>(vector_)[i];
+  }
+
+  const_reference operator[](size_type i) const { return const_cast<SmallVector&>(*this)[i]; }
+
+  // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
+  // replacing at end() is erroneous.
+  //
+  // The element is emplaced via move constructor, so type T does not need to define copy/move
+  // assignment, e.g. its data members may be const.
+  //
+  // The arguments may directly or indirectly refer to the element being replaced.
+  //
+  // Iterators to the replaced element point to its replacement, and others remain valid.
+  //
+  template <typename... Args>
+  reference replace(const_iterator it, Args&&... args) {
+    if (dynamic()) {
+      return std::get<Dynamic>(vector_).replace(it, std::forward<Args>(args)...);
+    } else {
+      return std::get<Static>(vector_).replace(it, std::forward<Args>(args)...);
+    }
+  }
+
+  // Appends an element, and returns a reference to it.
+  //
+  // If the vector reaches its static or dynamic capacity, then all iterators are invalidated.
+  // Otherwise, only the end() iterator is invalidated.
+  //
+  template <typename... Args>
+  reference emplace_back(Args&&... args) {
+    constexpr auto kInsertStatic = &Static::template emplace_back<Args...>;
+    constexpr auto kInsertDynamic = &Dynamic::template emplace_back<Args...>;
+    return *insert<kInsertStatic, kInsertDynamic>(std::forward<Args>(args)...);
+  }
+
+  // Appends an element.
+  //
+  // If the vector reaches its static or dynamic capacity, then all iterators are invalidated.
+  // Otherwise, only the end() iterator is invalidated.
+  //
+  void push_back(const value_type& v) {
+    constexpr auto kInsertStatic =
+        static_cast<bool (Static::*)(const value_type&)>(&Static::push_back);
+    constexpr auto kInsertDynamic =
+        static_cast<bool (Dynamic::*)(const value_type&)>(&Dynamic::push_back);
+    insert<kInsertStatic, kInsertDynamic>(v);
+  }
+
+  void push_back(value_type&& v) {
+    constexpr auto kInsertStatic = static_cast<bool (Static::*)(value_type &&)>(&Static::push_back);
+    constexpr auto kInsertDynamic =
+        static_cast<bool (Dynamic::*)(value_type &&)>(&Dynamic::push_back);
+    insert<kInsertStatic, kInsertDynamic>(std::move(v));
+  }
+
+  // Removes the last element. The vector must not be empty, or the call is erroneous.
+  //
+  // The last() and end() iterators are invalidated.
+  //
+  void pop_back() {
+    if (dynamic()) {
+      std::get<Dynamic>(vector_).pop_back();
+    } else {
+      std::get<Static>(vector_).pop_back();
+    }
+  }
+
+  // Erases an element, but does not preserve order. Rather than shifting subsequent elements,
+  // this moves the last element to the slot of the erased element.
+  //
+  // The last() and end() iterators, as well as those to the erased element, are invalidated.
+  //
+  void unstable_erase(iterator it) {
+    if (dynamic()) {
+      std::get<Dynamic>(vector_).unstable_erase(it);
+    } else {
+      std::get<Static>(vector_).unstable_erase(it);
+    }
+  }
+
+ private:
+  template <auto InsertStatic, auto InsertDynamic, typename... Args>
+  auto insert(Args&&... args) {
+    if (Dynamic* const vector = std::get_if<Dynamic>(&vector_)) {
+      return (vector->*InsertDynamic)(std::forward<Args>(args)...);
+    }
+
+    auto& vector = std::get<Static>(vector_);
+    if (vector.full()) {
+      return (promote(vector).*InsertDynamic)(std::forward<Args>(args)...);
+    } else {
+      return (vector.*InsertStatic)(std::forward<Args>(args)...);
+    }
+  }
+
+  Dynamic& promote(Static& static_vector) {
+    assert(static_vector.full());
+
+    // Allocate double capacity to reduce probability of reallocation.
+    Dynamic vector;
+    vector.reserve(Static::max_size() * 2);
+    std::move(static_vector.begin(), static_vector.end(), std::back_inserter(vector));
+
+    return vector_.template emplace<Dynamic>(std::move(vector));
+  }
+
+  std::variant<Static, Dynamic> vector_;
+};
+
+// Partial specialization without static storage.
+template <typename T>
+class SmallVector<T, 0> final : ArrayTraits<T>,
+                                ArrayIterators<SmallVector<T, 0>, T>,
+                                std::vector<T> {
+  using ArrayTraits<T>::construct_at;
+
+  using Iter = ArrayIterators<SmallVector, T>;
+  using Impl = std::vector<T>;
+
+  friend Iter;
+
+ public:
+  FTL_ARRAY_TRAIT(T, value_type);
+  FTL_ARRAY_TRAIT(T, size_type);
+  FTL_ARRAY_TRAIT(T, difference_type);
+
+  FTL_ARRAY_TRAIT(T, pointer);
+  FTL_ARRAY_TRAIT(T, reference);
+  FTL_ARRAY_TRAIT(T, iterator);
+  FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+  FTL_ARRAY_TRAIT(T, const_pointer);
+  FTL_ARRAY_TRAIT(T, const_reference);
+  FTL_ARRAY_TRAIT(T, const_iterator);
+  FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+  using Impl::Impl;
+
+  using Impl::empty;
+  using Impl::max_size;
+  using Impl::size;
+
+  using Impl::reserve;
+
+  // std::vector iterators are not necessarily raw pointers.
+  iterator begin() { return Impl::data(); }
+  iterator end() { return Impl::data() + size(); }
+
+  using Iter::begin;
+  using Iter::end;
+
+  using Iter::cbegin;
+  using Iter::cend;
+
+  using Iter::rbegin;
+  using Iter::rend;
+
+  using Iter::crbegin;
+  using Iter::crend;
+
+  using Iter::last;
+
+  using Iter::back;
+  using Iter::front;
+
+  using Iter::operator[];
+
+  template <typename... Args>
+  reference replace(const_iterator it, Args&&... args) {
+    value_type element{std::forward<Args>(args)...};
+    std::destroy_at(it);
+    // This is only safe because exceptions are disabled.
+    return *construct_at(it, std::move(element));
+  }
+
+  template <typename... Args>
+  iterator emplace_back(Args&&... args) {
+    return &Impl::emplace_back(std::forward<Args>(args)...);
+  }
+
+  bool push_back(const value_type& v) {
+    Impl::push_back(v);
+    return true;
+  }
+
+  bool push_back(value_type&& v) {
+    Impl::push_back(std::move(v));
+    return true;
+  }
+
+  using Impl::pop_back;
+
+  void unstable_erase(iterator it) {
+    if (it != last()) std::iter_swap(it, last());
+    pop_back();
+  }
+
+  void swap(SmallVector& other) { Impl::swap(other); }
+};
+
+template <typename>
+struct is_small_vector : std::false_type {};
+
+template <typename T, std::size_t N>
+struct is_small_vector<SmallVector<T, N>> : std::true_type {};
+
+// Deduction guide for array constructor.
+template <typename T, std::size_t N>
+SmallVector(T (&)[N]) -> SmallVector<std::remove_cv_t<T>, N>;
+
+// Deduction guide for variadic constructor.
+template <typename T, typename... Us, typename V = std::decay_t<T>,
+          typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>>
+SmallVector(T&&, Us&&...) -> SmallVector<V, 1 + sizeof...(Us)>;
+
+// Deduction guide for in-place constructor.
+template <typename T, std::size_t... Sizes, typename... Types>
+SmallVector(InitializerList<T, std::index_sequence<Sizes...>, Types...>&&)
+    -> SmallVector<T, sizeof...(Sizes)>;
+
+// Deduction guide for StaticVector conversion.
+template <typename T, std::size_t N>
+SmallVector(StaticVector<T, N>&&) -> SmallVector<T, N>;
+
+template <typename T, std::size_t N>
+inline void swap(SmallVector<T, N>& lhs, SmallVector<T, N>& rhs) {
+  lhs.swap(rhs);
+}
+
+}  // namespace android::ftl
diff --git a/include/ftl/static_vector.h b/include/ftl/static_vector.h
new file mode 100644
index 0000000..96a1ae8
--- /dev/null
+++ b/include/ftl/static_vector.h
@@ -0,0 +1,394 @@
+/*
+ * 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 <ftl/array_traits.h>
+#include <ftl/initializer_list.h>
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace android::ftl {
+
+constexpr struct IteratorRangeTag {
+} kIteratorRange;
+
+// Fixed-capacity, statically allocated counterpart of std::vector. Like std::array, StaticVector
+// allocates contiguous storage for N elements of type T at compile time, but stores at most (rather
+// than exactly) N elements. Unlike std::array, its default constructor does not require T to have a
+// default constructor, since elements are constructed in place as the vector grows. Operations that
+// insert an element (emplace_back, push_back, etc.) fail when the vector is full. The API otherwise
+// adheres to standard containers, except the unstable_erase operation that does not preserve order,
+// and the replace operation that destructively emplaces.
+//
+// StaticVector<T, 1> is analogous to an iterable std::optional.
+// StaticVector<T, 0> is an error.
+//
+// Example usage:
+//
+//   ftl::StaticVector<char, 3> vector;
+//   assert(vector.empty());
+//
+//   vector = {'a', 'b'};
+//   assert(vector.size() == 2u);
+//
+//   vector.push_back('c');
+//   assert(vector.full());
+//
+//   assert(!vector.push_back('d'));
+//   assert(vector.size() == 3u);
+//
+//   vector.unstable_erase(vector.begin());
+//   assert(vector == (ftl::StaticVector{'c', 'b'}));
+//
+//   vector.pop_back();
+//   assert(vector.back() == 'c');
+//
+//   const char array[] = "hi";
+//   vector = ftl::StaticVector(array);
+//   assert(vector == (ftl::StaticVector{'h', 'i', '\0'}));
+//
+//   ftl::StaticVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?');
+//   assert(strings.size() == 3u);
+//   assert(strings[0] == "abc");
+//   assert(strings[1] == "123");
+//   assert(strings[2] == "???");
+//
+template <typename T, std::size_t N>
+class StaticVector final : ArrayTraits<T>,
+                           ArrayIterators<StaticVector<T, N>, T>,
+                           ArrayComparators<StaticVector> {
+  static_assert(N > 0);
+
+  using ArrayTraits<T>::construct_at;
+
+  using Iter = ArrayIterators<StaticVector, T>;
+  friend Iter;
+
+  // There is ambiguity when constructing from two iterator-like elements like pointers:
+  // they could be an iterator range, or arguments for in-place construction. Assume the
+  // latter unless they are input iterators and cannot be used to construct elements. If
+  // the former is intended, the caller can pass an IteratorRangeTag to disambiguate.
+  template <typename I, typename Traits = std::iterator_traits<I>>
+  using is_input_iterator =
+      std::conjunction<std::is_base_of<std::input_iterator_tag, typename Traits::iterator_category>,
+                       std::negation<std::is_constructible<T, I>>>;
+
+ public:
+  FTL_ARRAY_TRAIT(T, value_type);
+  FTL_ARRAY_TRAIT(T, size_type);
+  FTL_ARRAY_TRAIT(T, difference_type);
+
+  FTL_ARRAY_TRAIT(T, pointer);
+  FTL_ARRAY_TRAIT(T, reference);
+  FTL_ARRAY_TRAIT(T, iterator);
+  FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+  FTL_ARRAY_TRAIT(T, const_pointer);
+  FTL_ARRAY_TRAIT(T, const_reference);
+  FTL_ARRAY_TRAIT(T, const_iterator);
+  FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+  // Creates an empty vector.
+  StaticVector() = default;
+
+  // Copies and moves a vector, respectively.
+  StaticVector(const StaticVector& other)
+      : StaticVector(kIteratorRange, other.begin(), other.end()) {}
+
+  StaticVector(StaticVector&& other) { swap<true>(other); }
+
+  // Copies at most N elements from a smaller convertible vector.
+  template <typename U, std::size_t M, typename = std::enable_if_t<M <= N>>
+  StaticVector(const StaticVector<U, M>& other)
+      : StaticVector(kIteratorRange, other.begin(), other.end()) {}
+
+  // Copies at most N elements from an array.
+  template <typename U, std::size_t M>
+  explicit StaticVector(U (&array)[M])
+      : StaticVector(kIteratorRange, std::begin(array), std::end(array)) {}
+
+  // Copies at most N elements from the range [first, last).
+  //
+  // IteratorRangeTag disambiguates with initialization from two iterator-like elements.
+  //
+  template <typename Iterator, typename = std::enable_if_t<is_input_iterator<Iterator>{}>>
+  StaticVector(Iterator first, Iterator last) : StaticVector(kIteratorRange, first, last) {
+    using V = typename std::iterator_traits<Iterator>::value_type;
+    static_assert(std::is_constructible_v<value_type, V>, "Incompatible iterator range");
+  }
+
+  template <typename Iterator>
+  StaticVector(IteratorRangeTag, Iterator first, Iterator last)
+      : size_(std::min(max_size(), static_cast<size_type>(std::distance(first, last)))) {
+    std::uninitialized_copy(first, first + size_, begin());
+  }
+
+  // Constructs at most N elements. The template arguments T and N are inferred using the
+  // deduction guide defined below. Note that T is determined from the first element, and
+  // subsequent elements must have convertible types:
+  //
+  //   ftl::StaticVector vector = {1, 2, 3};
+  //   static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<int, 3>>);
+  //
+  //   const auto copy = "quince"s;
+  //   auto move = "tart"s;
+  //   ftl::StaticVector vector = {copy, std::move(move)};
+  //
+  //   static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 2>>);
+  //
+  template <typename E, typename... Es,
+            typename = std::enable_if_t<std::is_constructible_v<value_type, E>>>
+  StaticVector(E&& element, Es&&... elements)
+      : StaticVector(std::index_sequence<0>{}, std::forward<E>(element),
+                     std::forward<Es>(elements)...) {
+    static_assert(sizeof...(elements) < N, "Too many elements");
+  }
+
+  // Constructs at most N elements in place by forwarding per-element constructor arguments. The
+  // template arguments T and N are inferred using the deduction guide defined below. The syntax
+  // for listing arguments is as follows:
+  //
+  //   ftl::StaticVector vector = ftl::init::list<std::string>("abc")()(3u, '?');
+  //
+  //   static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 3>>);
+  //   assert(vector.full());
+  //   assert(vector[0] == "abc");
+  //   assert(vector[1].empty());
+  //   assert(vector[2] == "???");
+  //
+  template <typename U, std::size_t Size, std::size_t... Sizes, typename... Types>
+  StaticVector(InitializerList<U, std::index_sequence<Size, Sizes...>, Types...>&& list)
+      : StaticVector(std::index_sequence<0, 0, Size>{}, std::make_index_sequence<Size>{},
+                     std::index_sequence<Sizes...>{}, list.tuple) {}
+
+  ~StaticVector() { std::destroy(begin(), end()); }
+
+  StaticVector& operator=(const StaticVector& other) {
+    StaticVector copy(other);
+    swap(copy);
+    return *this;
+  }
+
+  StaticVector& operator=(StaticVector&& other) {
+    std::destroy(begin(), end());
+    size_ = 0;
+    swap<true>(other);
+    return *this;
+  }
+
+  // IsEmpty enables a fast path when the vector is known to be empty at compile time.
+  template <bool IsEmpty = false>
+  void swap(StaticVector&);
+
+  static constexpr size_type max_size() { return N; }
+  size_type size() const { return size_; }
+
+  bool empty() const { return size() == 0; }
+  bool full() const { return size() == max_size(); }
+
+  iterator begin() { return std::launder(reinterpret_cast<pointer>(data_)); }
+  iterator end() { return begin() + size(); }
+
+  using Iter::begin;
+  using Iter::end;
+
+  using Iter::cbegin;
+  using Iter::cend;
+
+  using Iter::rbegin;
+  using Iter::rend;
+
+  using Iter::crbegin;
+  using Iter::crend;
+
+  using Iter::last;
+
+  using Iter::back;
+  using Iter::front;
+
+  using Iter::operator[];
+
+  // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
+  // replacing at end() is erroneous.
+  //
+  // The element is emplaced via move constructor, so type T does not need to define copy/move
+  // assignment, e.g. its data members may be const.
+  //
+  // The arguments may directly or indirectly refer to the element being replaced.
+  //
+  // Iterators to the replaced element point to its replacement, and others remain valid.
+  //
+  template <typename... Args>
+  reference replace(const_iterator it, Args&&... args) {
+    value_type element{std::forward<Args>(args)...};
+    std::destroy_at(it);
+    // This is only safe because exceptions are disabled.
+    return *construct_at(it, std::move(element));
+  }
+
+  // Appends an element, and returns an iterator to it. If the vector is full, the element is not
+  // inserted, and the end() iterator is returned.
+  //
+  // On success, the end() iterator is invalidated.
+  //
+  template <typename... Args>
+  iterator emplace_back(Args&&... args) {
+    if (full()) return end();
+    const iterator it = construct_at(end(), std::forward<Args>(args)...);
+    ++size_;
+    return it;
+  }
+
+  // Appends an element unless the vector is full, and returns whether the element was inserted.
+  //
+  // On success, the end() iterator is invalidated.
+  //
+  bool push_back(const value_type& v) {
+    // Two statements for sequence point.
+    const iterator it = emplace_back(v);
+    return it != end();
+  }
+
+  bool push_back(value_type&& v) {
+    // Two statements for sequence point.
+    const iterator it = emplace_back(std::move(v));
+    return it != end();
+  }
+
+  // Removes the last element. The vector must not be empty, or the call is erroneous.
+  //
+  // The last() and end() iterators are invalidated.
+  //
+  void pop_back() { unstable_erase(last()); }
+
+  // Erases an element, but does not preserve order. Rather than shifting subsequent elements,
+  // this moves the last element to the slot of the erased element.
+  //
+  // The last() and end() iterators, as well as those to the erased element, are invalidated.
+  //
+  void unstable_erase(const_iterator it) {
+    std::destroy_at(it);
+    if (it != last()) {
+      // Move last element and destroy its source for destructor side effects. This is only
+      // safe because exceptions are disabled.
+      construct_at(it, std::move(back()));
+      std::destroy_at(last());
+    }
+    --size_;
+  }
+
+ private:
+  // Recursion for variadic constructor.
+  template <std::size_t I, typename E, typename... Es>
+  StaticVector(std::index_sequence<I>, E&& element, Es&&... elements)
+      : StaticVector(std::index_sequence<I + 1>{}, std::forward<Es>(elements)...) {
+    construct_at(begin() + I, std::forward<E>(element));
+  }
+
+  // Base case for variadic constructor.
+  template <std::size_t I>
+  explicit StaticVector(std::index_sequence<I>) : size_(I) {}
+
+  // Recursion for in-place constructor.
+  //
+  // Construct element I by extracting its arguments from the InitializerList tuple. ArgIndex
+  // is the position of its first argument in Args, and ArgCount is the number of arguments.
+  // The Indices sequence corresponds to [0, ArgCount).
+  //
+  // The Sizes sequence lists the argument counts for elements after I, so Size is the ArgCount
+  // for the next element. The recursion stops when Sizes is empty for the last element.
+  //
+  template <std::size_t I, std::size_t ArgIndex, std::size_t ArgCount, std::size_t... Indices,
+            std::size_t Size, std::size_t... Sizes, typename... Args>
+  StaticVector(std::index_sequence<I, ArgIndex, ArgCount>, std::index_sequence<Indices...>,
+               std::index_sequence<Size, Sizes...>, std::tuple<Args...>& tuple)
+      : StaticVector(std::index_sequence<I + 1, ArgIndex + ArgCount, Size>{},
+                     std::make_index_sequence<Size>{}, std::index_sequence<Sizes...>{}, tuple) {
+    construct_at(begin() + I, std::move(std::get<ArgIndex + Indices>(tuple))...);
+  }
+
+  // Base case for in-place constructor.
+  template <std::size_t I, std::size_t ArgIndex, std::size_t ArgCount, std::size_t... Indices,
+            typename... Args>
+  StaticVector(std::index_sequence<I, ArgIndex, ArgCount>, std::index_sequence<Indices...>,
+               std::index_sequence<>, std::tuple<Args...>& tuple)
+      : size_(I + 1) {
+    construct_at(begin() + I, std::move(std::get<ArgIndex + Indices>(tuple))...);
+  }
+
+  size_type size_ = 0;
+  std::aligned_storage_t<sizeof(value_type), alignof(value_type)> data_[N];
+};
+
+// Deduction guide for array constructor.
+template <typename T, std::size_t N>
+StaticVector(T (&)[N]) -> StaticVector<std::remove_cv_t<T>, N>;
+
+// Deduction guide for variadic constructor.
+template <typename T, typename... Us, typename V = std::decay_t<T>,
+          typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>>
+StaticVector(T&&, Us&&...) -> StaticVector<V, 1 + sizeof...(Us)>;
+
+// Deduction guide for in-place constructor.
+template <typename T, std::size_t... Sizes, typename... Types>
+StaticVector(InitializerList<T, std::index_sequence<Sizes...>, Types...>&&)
+    -> StaticVector<T, sizeof...(Sizes)>;
+
+template <typename T, std::size_t N>
+template <bool IsEmpty>
+void StaticVector<T, N>::swap(StaticVector& other) {
+  auto [to, from] = std::make_pair(this, &other);
+  if (from == this) return;
+
+  // Assume this vector has fewer elements, so the excess of the other vector will be moved to it.
+  auto [min, max] = std::make_pair(size(), other.size());
+
+  // No elements to swap if moving into an empty vector.
+  if constexpr (IsEmpty) {
+    assert(min == 0);
+  } else {
+    if (min > max) {
+      std::swap(from, to);
+      std::swap(min, max);
+    }
+
+    // Swap elements [0, min).
+    std::swap_ranges(begin(), begin() + min, other.begin());
+
+    // No elements to move if sizes are equal.
+    if (min == max) return;
+  }
+
+  // Move elements [min, max) and destroy their source for destructor side effects.
+  const auto [first, last] = std::make_pair(from->begin() + min, from->begin() + max);
+  std::uninitialized_move(first, last, to->begin() + min);
+  std::destroy(first, last);
+
+  std::swap(size_, other.size_);
+}
+
+template <typename T, std::size_t N>
+inline void swap(StaticVector<T, N>& lhs, StaticVector<T, N>& rhs) {
+  lhs.swap(rhs);
+}
+
+}  // namespace android::ftl
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 60638ca..23692e9 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -17,8 +17,10 @@
 #ifndef _LIBINPUT_INPUT_DEVICE_H
 #define _LIBINPUT_INPUT_DEVICE_H
 
+#include <android/sensor.h>
 #include <input/Input.h>
 #include <input/KeyCharacterMap.h>
+#include <unordered_map>
 #include <vector>
 
 namespace android {
@@ -63,6 +65,97 @@
     std::string getCanonicalName() const;
 };
 
+/* Types of input device sensors. Keep sync with core/java/android/hardware/Sensor.java */
+enum class InputDeviceSensorType : int32_t {
+    ACCELEROMETER = ASENSOR_TYPE_ACCELEROMETER,
+    MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD,
+    ORIENTATION = 3,
+    GYROSCOPE = ASENSOR_TYPE_GYROSCOPE,
+    LIGHT = ASENSOR_TYPE_LIGHT,
+    PRESSURE = ASENSOR_TYPE_PRESSURE,
+    TEMPERATURE = 7,
+    PROXIMITY = ASENSOR_TYPE_PROXIMITY,
+    GRAVITY = ASENSOR_TYPE_GRAVITY,
+    LINEAR_ACCELERATION = ASENSOR_TYPE_LINEAR_ACCELERATION,
+    ROTATION_VECTOR = ASENSOR_TYPE_ROTATION_VECTOR,
+    RELATIVE_HUMIDITY = ASENSOR_TYPE_RELATIVE_HUMIDITY,
+    AMBIENT_TEMPERATURE = ASENSOR_TYPE_AMBIENT_TEMPERATURE,
+    MAGNETIC_FIELD_UNCALIBRATED = ASENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED,
+    GAME_ROTATION_VECTOR = ASENSOR_TYPE_GAME_ROTATION_VECTOR,
+    GYROSCOPE_UNCALIBRATED = ASENSOR_TYPE_GYROSCOPE_UNCALIBRATED,
+    SIGNIFICANT_MOTION = ASENSOR_TYPE_SIGNIFICANT_MOTION,
+};
+
+enum class InputDeviceSensorAccuracy : int32_t {
+    ACCURACY_NONE = 0,
+    ACCURACY_LOW = 1,
+    ACCURACY_MEDIUM = 2,
+    ACCURACY_HIGH = 3,
+};
+
+enum class InputDeviceSensorReportingMode : int32_t {
+    CONTINUOUS = 0,
+    ON_CHANGE = 1,
+    ONE_SHOT = 2,
+    SPECIAL_TRIGGER = 3,
+};
+
+struct InputDeviceSensorInfo {
+    explicit InputDeviceSensorInfo(std::string name, std::string vendor, int32_t version,
+                                   InputDeviceSensorType type, InputDeviceSensorAccuracy accuracy,
+                                   float maxRange, float resolution, float power, int32_t minDelay,
+                                   int32_t fifoReservedEventCount, int32_t fifoMaxEventCount,
+                                   std::string stringType, int32_t maxDelay, int32_t flags,
+                                   int32_t id)
+          : name(name),
+            vendor(vendor),
+            version(version),
+            type(type),
+            accuracy(accuracy),
+            maxRange(maxRange),
+            resolution(resolution),
+            power(power),
+            minDelay(minDelay),
+            fifoReservedEventCount(fifoReservedEventCount),
+            fifoMaxEventCount(fifoMaxEventCount),
+            stringType(stringType),
+            maxDelay(maxDelay),
+            flags(flags),
+            id(id) {}
+    // Name string of the sensor.
+    std::string name;
+    // Vendor string of this sensor.
+    std::string vendor;
+    // Version of the sensor's module.
+    int32_t version;
+    // Generic type of this sensor.
+    InputDeviceSensorType type;
+    // The current accuracy of sensor event.
+    InputDeviceSensorAccuracy accuracy;
+    // Maximum range of the sensor in the sensor's unit.
+    float maxRange;
+    // Resolution of the sensor in the sensor's unit.
+    float resolution;
+    // The power in mA used by this sensor while in use.
+    float power;
+    // The minimum delay allowed between two events in microsecond or zero if this sensor only
+    // returns a value when the data it's measuring changes.
+    int32_t minDelay;
+    // Number of events reserved for this sensor in the batch mode FIFO.
+    int32_t fifoReservedEventCount;
+    // Maximum number of events of this sensor that could be batched.
+    int32_t fifoMaxEventCount;
+    // The type of this sensor as a string.
+    std::string stringType;
+    // The delay between two sensor events corresponding to the lowest frequency that this sensor
+    // supports.
+    int32_t maxDelay;
+    // Sensor flags
+    int32_t flags;
+    // Sensor id, same as the input device ID it belongs to.
+    int32_t id;
+};
+
 /*
  * Describes the characteristics and capabilities of an input device.
  */
@@ -104,6 +197,7 @@
     void addMotionRange(int32_t axis, uint32_t source,
             float min, float max, float flat, float fuzz, float resolution);
     void addMotionRange(const MotionRange& range);
+    void addSensorInfo(const InputDeviceSensorInfo& info);
 
     inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
     inline int32_t getKeyboardType() const { return mKeyboardType; }
@@ -122,10 +216,17 @@
     inline void setButtonUnderPad(bool hasButton) { mHasButtonUnderPad = hasButton; }
     inline bool hasButtonUnderPad() const { return mHasButtonUnderPad; }
 
+    inline void setHasSensor(bool hasSensor) { mHasSensor = hasSensor; }
+    inline bool hasSensor() const { return mHasSensor; }
+
     inline const std::vector<MotionRange>& getMotionRanges() const {
         return mMotionRanges;
     }
 
+    const InputDeviceSensorInfo* getSensorInfo(InputDeviceSensorType type);
+
+    const std::vector<InputDeviceSensorType> getSensorTypes();
+
 private:
     int32_t mId;
     int32_t mGeneration;
@@ -139,8 +240,10 @@
     std::shared_ptr<KeyCharacterMap> mKeyCharacterMap;
     bool mHasVibrator;
     bool mHasButtonUnderPad;
+    bool mHasSensor;
 
     std::vector<MotionRange> mMotionRanges;
+    std::unordered_map<InputDeviceSensorType, InputDeviceSensorInfo> mSensors;
 };
 
 /* Types of input device configuration files. */
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index 23f8ddf..451ca3c 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -142,6 +142,8 @@
     void writeToParcel(Parcel* parcel) const;
 #endif
 
+    bool operator==(const KeyCharacterMap& other) const;
+
     KeyCharacterMap(const KeyCharacterMap& other);
 
     virtual ~KeyCharacterMap();
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index 872dd45..b2bd535 100644
--- a/include/input/KeyLayoutMap.h
+++ b/include/input/KeyLayoutMap.h
@@ -24,6 +24,8 @@
 #include <utils/RefBase.h>
 #include <utils/Tokenizer.h>
 
+#include <input/InputDevice.h>
+
 namespace android {
 
 struct AxisInfo {
@@ -76,6 +78,8 @@
 
     status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const;
     const std::string getLoadFileName() const;
+    // Return pair of sensor type and sensor data index, for the input device abs code
+    base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t absCode);
 
     virtual ~KeyLayoutMap();
 
@@ -89,12 +93,17 @@
         int32_t ledCode;
     };
 
+    struct Sensor {
+        InputDeviceSensorType sensorType;
+        int32_t sensorDataIndex;
+    };
 
     KeyedVector<int32_t, Key> mKeysByScanCode;
     KeyedVector<int32_t, Key> mKeysByUsageCode;
     KeyedVector<int32_t, AxisInfo> mAxes;
     KeyedVector<int32_t, Led> mLedsByScanCode;
     KeyedVector<int32_t, Led> mLedsByUsageCode;
+    std::unordered_map<int32_t, Sensor> mSensorsByAbsCode;
     std::string mLoadFileName;
 
     KeyLayoutMap();
@@ -114,6 +123,7 @@
         status_t parseKey();
         status_t parseAxis();
         status_t parseLed();
+        status_t parseSensor();
     };
 };
 
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
index dae6eeb..15bd5c3 100644
--- a/libs/adbd_auth/adbd_auth.cpp
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -282,9 +282,8 @@
             LOG(FATAL) << "adbd_auth: unhandled packet type?";
         }
 
-        output_queue_.pop_front();
-
         ssize_t rc = writev(framework_fd_.get(), iovs, iovcnt);
+        output_queue_.pop_front();
         if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
             PLOG(ERROR) << "adbd_auth: failed to write to framework fd";
             ReplaceFrameworkFd(unique_fd());
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 90feedd..feaea63 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -83,9 +83,6 @@
     // or dessert updates. Instead, apex users should use libbinder_ndk.
     apex_available: [
         "//apex_available:platform",
-        // TODO(b/166468760) remove these three
-        "com.android.media.swcodec",
-        "test_com.android.media.swcodec",
     ],
 
     srcs: [
@@ -183,6 +180,14 @@
     ],
     tidy_checks_as_errors: [
         "*",
+        "-clang-analyzer-core.CallAndMessage",
+        "-clang-analyzer-core.uninitialized.Assign",
+        "-clang-analyzer-unix.Malloc,",
+        "-clang-analyzer-deadcode.DeadStores",
+        "-clang-analyzer-optin.cplusplus.UninitializedObject",
+        "-misc-no-recursion",
+        "-misc-redundant-expression",
+        "-misc-unused-using-decls",
     ],
 }
 
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 2e15e50..1173138 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "log/log_main.h"
 #define LOG_TAG "AidlLazyServiceRegistrar"
 
 #include <binder/LazyServiceRegistrar.h>
@@ -37,6 +38,13 @@
                          bool allowIsolated, int dumpFlags);
     void forcePersist(bool persist);
 
+    void setActiveServicesCountCallback(const std::function<bool(int)>&
+                                        activeServicesCountCallback);
+
+    bool tryUnregister();
+
+    void reRegister();
+
 protected:
     Status onClients(const sp<IBinder>& service, bool clients) override;
 
@@ -49,6 +57,7 @@
         // whether, based on onClients calls, we know we have a client for this
         // service or not
         bool clients = false;
+        bool registered = true;
     };
 
     /**
@@ -62,6 +71,14 @@
      */
     void tryShutdown();
 
+    /**
+     * Try to shutdown the process, unless:
+     * - 'forcePersist' is 'true', or
+     * - The active services count callback returns 'true', or
+     * - Some services have clients.
+     */
+    void maybeTryShutdown();
+
     // count of services with clients
     size_t mNumConnectedServices;
 
@@ -69,6 +86,9 @@
     std::map<std::string, Service> mRegisteredServices;
 
     bool mForcePersist;
+
+    // Callback used to report the number of services with clients
+    std::function<bool(int)> mActiveServicesCountCallback;
 };
 
 class ClientCounterCallback {
@@ -83,6 +103,13 @@
      */
     void forcePersist(bool persist);
 
+    void setActiveServicesCountCallback(const std::function<bool(int)>&
+                                        activeServicesCountCallback);
+
+    bool tryUnregister();
+
+    void reRegister();
+
 private:
     sp<ClientCounterCallbackImpl> mImpl;
 };
@@ -131,8 +158,60 @@
 
 void ClientCounterCallbackImpl::forcePersist(bool persist) {
     mForcePersist = persist;
-    if(!mForcePersist) {
+    if (!mForcePersist && mNumConnectedServices == 0) {
         // Attempt a shutdown in case the number of clients hit 0 while the flag was on
+        maybeTryShutdown();
+    }
+}
+
+bool ClientCounterCallbackImpl::tryUnregister() {
+    auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
+
+    for (auto& [name, entry] : mRegisteredServices) {
+        bool success = manager->tryUnregisterService(name, entry.service).isOk();
+
+        if (!success) {
+            ALOGI("Failed to unregister service %s", name.c_str());
+            return false;
+        }
+        entry.registered = false;
+    }
+
+    return true;
+}
+
+void ClientCounterCallbackImpl::reRegister() {
+    for (auto& [name, entry] : mRegisteredServices) {
+        // re-register entry if not already registered
+        if (entry.registered) {
+            continue;
+        }
+
+        if (!registerService(entry.service, name, entry.allowIsolated,
+                             entry.dumpFlags)) {
+            // Must restart. Otherwise, clients will never be able to get a hold of this service.
+            LOG_ALWAYS_FATAL("Bad state: could not re-register services");
+        }
+
+        entry.registered = true;
+    }
+}
+
+void ClientCounterCallbackImpl::maybeTryShutdown() {
+    if (mForcePersist) {
+        ALOGI("Shutdown prevented by forcePersist override flag.");
+        return;
+    }
+
+    bool handledInCallback = false;
+    if (mActiveServicesCountCallback != nullptr) {
+        handledInCallback = mActiveServicesCountCallback(mNumConnectedServices);
+    }
+
+    // If there is no callback defined or the callback did not handle this
+    // client count change event, try to shutdown the process if its services
+    // have no clients.
+    if (!handledInCallback && mNumConnectedServices == 0) {
         tryShutdown();
     }
 }
@@ -162,53 +241,24 @@
     ALOGI("Process has %zu (of %zu available) client(s) in use after notification %s has clients: %d",
           mNumConnectedServices, mRegisteredServices.size(), name.c_str(), clients);
 
-    tryShutdown();
+    maybeTryShutdown();
     return Status::ok();
 }
 
-void ClientCounterCallbackImpl::tryShutdown() {
-    if(mNumConnectedServices > 0) {
-        // Should only shut down if there are no clients
-        return;
-    }
+ void ClientCounterCallbackImpl::tryShutdown() {
+     ALOGI("Trying to shut down the service. No clients in use for any service in process.");
 
-    if(mForcePersist) {
-        ALOGI("Shutdown prevented by forcePersist override flag.");
-        return;
-    }
+    if (tryUnregister()) {
+         ALOGI("Unregistered all clients and exiting");
+         exit(EXIT_SUCCESS);
+     }
 
-    ALOGI("Trying to shut down the service. No clients in use for any service in process.");
+    reRegister();
+}
 
-    auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
-
-    auto unRegisterIt = mRegisteredServices.begin();
-    for (; unRegisterIt != mRegisteredServices.end(); ++unRegisterIt) {
-        auto& entry = (*unRegisterIt);
-
-        bool success = manager->tryUnregisterService(entry.first, entry.second.service).isOk();
-
-        if (!success) {
-            ALOGI("Failed to unregister service %s", entry.first.c_str());
-            break;
-        }
-    }
-
-    if (unRegisterIt == mRegisteredServices.end()) {
-        ALOGI("Unregistered all clients and exiting");
-        exit(EXIT_SUCCESS);
-    }
-
-    for (auto reRegisterIt = mRegisteredServices.begin(); reRegisterIt != unRegisterIt;
-         reRegisterIt++) {
-        auto& entry = (*reRegisterIt);
-
-        // re-register entry
-        if (!registerService(entry.second.service, entry.first, entry.second.allowIsolated,
-                             entry.second.dumpFlags)) {
-            // Must restart. Otherwise, clients will never be able to get a hold of this service.
-            ALOGE("Bad state: could not re-register services");
-        }
-    }
+void ClientCounterCallbackImpl::setActiveServicesCountCallback(const std::function<bool(int)>&
+                                                               activeServicesCountCallback) {
+    mActiveServicesCountCallback = activeServicesCountCallback;
 }
 
 ClientCounterCallback::ClientCounterCallback() {
@@ -224,6 +274,19 @@
     mImpl->forcePersist(persist);
 }
 
+void ClientCounterCallback::setActiveServicesCountCallback(const std::function<bool(int)>&
+                                                           activeServicesCountCallback) {
+    mImpl->setActiveServicesCountCallback(activeServicesCountCallback);
+}
+
+bool ClientCounterCallback::tryUnregister() {
+    return mImpl->tryUnregister();
+}
+
+void ClientCounterCallback::reRegister() {
+    mImpl->reRegister();
+}
+
 }  // namespace internal
 
 LazyServiceRegistrar::LazyServiceRegistrar() {
@@ -247,5 +310,18 @@
     mClientCC->forcePersist(persist);
 }
 
+void LazyServiceRegistrar::setActiveServicesCountCallback(const std::function<bool(int)>&
+                                                          activeServicesCountCallback) {
+    mClientCC->setActiveServicesCountCallback(activeServicesCountCallback);
+}
+
+bool LazyServiceRegistrar::tryUnregister() {
+    return mClientCC->tryUnregister();
+}
+
+void LazyServiceRegistrar::reRegister() {
+    mClientCC->reRegister();
+}
+
 }  // namespace hardware
 }  // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 91e465f..87eab52 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -206,7 +206,7 @@
             if (proxy == nullptr) {
                 ALOGE("null proxy");
             }
-            const int32_t handle = proxy ? proxy->handle() : 0;
+            const int32_t handle = proxy ? proxy->getPrivateAccessorForHandle().handle() : 0;
             obj.hdr.type = BINDER_TYPE_HANDLE;
             obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
             obj.handle = handle;
@@ -2509,19 +2509,15 @@
 void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
     const binder_size_t* objects, size_t objectsCount, release_func relFunc)
 {
-    binder_size_t minOffset = 0;
-    freeDataNoInit();
-    mError = NO_ERROR;
+    freeData();
+
     mData = const_cast<uint8_t*>(data);
     mDataSize = mDataCapacity = dataSize;
-    //ALOGI("setDataReference Setting data size of %p to %lu (pid=%d)", this, mDataSize, getpid());
-    mDataPos = 0;
-    ALOGV("setDataReference Setting data pos of %p to %zu", this, mDataPos);
     mObjects = const_cast<binder_size_t*>(objects);
     mObjectsSize = mObjectsCapacity = objectsCount;
-    mNextObjectHint = 0;
-    mObjectsSorted = false;
     mOwner = relFunc;
+
+    binder_size_t minOffset = 0;
     for (size_t i = 0; i < mObjectsSize; i++) {
         binder_size_t offset = mObjects[i];
         if (offset < minOffset) {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 9aedf28..b5e4dfe 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -124,14 +124,14 @@
 {
     sp<IBinder> context = getStrongProxyForHandle(0);
 
-    if (context == nullptr) {
-       ALOGW("Not able to get context object on %s.", mDriverName.c_str());
+    if (context) {
+        // The root object is special since we get it directly from the driver, it is never
+        // written by Parcell::writeStrongBinder.
+        internal::Stability::markCompilationUnit(context.get());
+    } else {
+        ALOGW("Not able to get context object on %s.", mDriverName.c_str());
     }
 
-    // The root object is special since we get it directly from the driver, it is never
-    // written by Parcell::writeStrongBinder.
-    internal::Stability::markCompilationUnit(context.get());
-
     return context;
 }
 
@@ -204,11 +204,11 @@
 // that the handle points to. Can only be used by the servicemanager.
 //
 // Returns -1 in case of failure, otherwise the strong reference count.
-ssize_t ProcessState::getStrongRefCountForNodeByHandle(int32_t handle) {
+ssize_t ProcessState::getStrongRefCountForNode(const sp<BpBinder>& binder) {
     binder_node_info_for_ref info;
     memset(&info, 0, sizeof(binder_node_info_for_ref));
 
-    info.handle = handle;
+    info.handle = binder->getPrivateAccessorForHandle().handle();
 
     status_t result = ioctl(mDriverFD, BINDER_GET_NODE_INFO_FOR_REF, &info);
 
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 2735315..22300ac 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -29,6 +29,7 @@
 namespace internal {
 class Stability;
 }
+class ProcessState;
 
 using binder_proxy_limit_callback = void(*)(int);
 
@@ -37,8 +38,6 @@
 public:
     static BpBinder*    create(int32_t handle);
 
-    int32_t             handle() const;
-
     virtual const String16&    getInterfaceDescriptor() const;
     virtual bool        isBinderAlive() const;
     virtual status_t    pingBinder();
@@ -109,7 +108,23 @@
         KeyedVector<const void*, entry_t> mObjects;
     };
 
+    class PrivateAccessorForHandle {
+    private:
+        friend BpBinder;
+        friend ::android::Parcel;
+        friend ::android::ProcessState;
+        explicit PrivateAccessorForHandle(const BpBinder* binder) : mBinder(binder) {}
+        int32_t handle() const { return mBinder->handle(); }
+        const BpBinder* mBinder;
+    };
+    const PrivateAccessorForHandle getPrivateAccessorForHandle() const {
+        return PrivateAccessorForHandle(this);
+    }
+
 private:
+    friend PrivateAccessorForHandle;
+
+    int32_t             handle() const;
                         BpBinder(int32_t handle,int32_t trackedUid);
     virtual             ~BpBinder();
     virtual void        onFirstRef();
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index d18c88e..73893c7 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <functional>
+
 #include <binder/IServiceManager.h>
 #include <binder/Status.h>
 #include <utils/StrongPointer.h>
@@ -53,6 +55,40 @@
       */
      void forcePersist(bool persist);
 
+     /**
+      * Set a callback that is executed when the total number of services with
+      * clients changes.
+      * The callback takes an argument, which is the number of registered
+      * lazy services for this process which have clients.
+      *
+      * Callback return value:
+      * - false: Default behavior for lazy services (shut down the process if there
+      *          are no clients).
+      * - true:  Don't shut down the process even if there are no clients.
+      *
+      * This callback gives a chance to:
+      * 1 - Perform some additional operations before exiting;
+      * 2 - Prevent the process from exiting by returning "true" from the
+      *     callback.
+      *
+      * This method should be called before 'registerService' to avoid races.
+      */
+     void setActiveServicesCountCallback(const std::function<bool(int)>&
+                                         activeServicesCountCallback);
+
+    /**
+      * Try to unregister all services previously registered with 'registerService'.
+      * Returns 'true' if successful.
+      */
+     bool tryUnregister();
+
+     /**
+      * Re-register services that were unregistered by 'tryUnregister'.
+      * This method should be called in the case 'tryUnregister' fails
+      * (and should be called on the same thread).
+      */
+     void reRegister();
+
    private:
      std::shared_ptr<internal::ClientCounterCallback> mClientCC;
      LazyServiceRegistrar();
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 9f5260a..b49951b 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -504,9 +504,6 @@
                                             const binder_size_t* objects, size_t objectsCount,
                                             release_func relFunc);
 
-                        Parcel(const Parcel& o);
-    Parcel&             operator=(const Parcel& o);
-    
     status_t            finishWrite(size_t len);
     void                releaseObjects();
     void                acquireObjects();
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
index ce5027e..ff0a686 100644
--- a/libs/binder/include/binder/ParcelableHolder.h
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -52,80 +52,80 @@
     }
 
     template <typename T>
-    bool setParcelable(T&& p) {
+    status_t setParcelable(T&& p) {
         using Tt = typename std::decay<T>::type;
         return setParcelable<Tt>(std::make_shared<Tt>(std::forward<T>(p)));
     }
 
     template <typename T>
-    bool setParcelable(std::shared_ptr<T> p) {
+    status_t setParcelable(std::shared_ptr<T> p) {
         static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
         if (p && this->getStability() > p->getStability()) {
-            return false;
+            return android::BAD_VALUE;
         }
         this->mParcelable = p;
         this->mParcelableName = T::getParcelableDescriptor();
         this->mParcelPtr = nullptr;
-        return true;
+        return android::OK;
     }
 
     template <typename T>
-    std::shared_ptr<T> getParcelable() const {
+    status_t getParcelable(std::shared_ptr<T>* ret) const {
         static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
         const std::string& parcelableDesc = T::getParcelableDescriptor();
         if (!this->mParcelPtr) {
             if (!this->mParcelable || !this->mParcelableName) {
                 ALOGD("empty ParcelableHolder");
-                return nullptr;
+                *ret = nullptr;
+                return android::OK;
             } else if (parcelableDesc != *mParcelableName) {
                 ALOGD("extension class name mismatch expected:%s actual:%s",
                       mParcelableName->c_str(), parcelableDesc.c_str());
-                return nullptr;
+                *ret = nullptr;
+                return android::BAD_VALUE;
             }
-            return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+            *ret = std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+            return android::OK;
         }
         this->mParcelPtr->setDataPosition(0);
         status_t status = this->mParcelPtr->readUtf8FromUtf16(&this->mParcelableName);
         if (status != android::OK || parcelableDesc != this->mParcelableName) {
             this->mParcelableName = std::nullopt;
-            return nullptr;
+            *ret = nullptr;
+            return status;
         }
         this->mParcelable = std::make_shared<T>();
         status = mParcelable.get()->readFromParcel(this->mParcelPtr.get());
         if (status != android::OK) {
             this->mParcelableName = std::nullopt;
             this->mParcelable = nullptr;
-            return nullptr;
+            *ret = nullptr;
+            return status;
         }
         this->mParcelPtr = nullptr;
-        return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+        *ret = std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
+        return android::OK;
     }
 
     Stability getStability() const override { return mStability; }
 
     inline bool operator!=(const ParcelableHolder& rhs) const {
-        return std::tie(mParcelable, mParcelPtr, mStability) !=
-                std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+        return this != &rhs;
     }
     inline bool operator<(const ParcelableHolder& rhs) const {
-        return std::tie(mParcelable, mParcelPtr, mStability) <
-                std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+        return this < &rhs;
     }
     inline bool operator<=(const ParcelableHolder& rhs) const {
-        return std::tie(mParcelable, mParcelPtr, mStability) <=
-                std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+        return this <= &rhs;
     }
     inline bool operator==(const ParcelableHolder& rhs) const {
-        return std::tie(mParcelable, mParcelPtr, mStability) ==
-                std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+        return this == &rhs;
     }
     inline bool operator>(const ParcelableHolder& rhs) const {
-        return std::tie(mParcelable, mParcelPtr, mStability) >
-                std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+        return this > &rhs;
     }
     inline bool operator>=(const ParcelableHolder& rhs) const {
-        return std::tie(mParcelable, mParcelPtr, mStability) >=
-                std::tie(rhs.mParcelable, rhs.mParcelPtr, rhs.mStability);
+        return this >= &rhs;
     }
 
 private:
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 46457cd..bab6469 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -70,7 +70,7 @@
                                 // 2. Temporary strong references held by the kernel during a
                                 //    transaction on the node.
                                 // It does NOT include local strong references to the node
-            ssize_t             getStrongRefCountForNodeByHandle(int32_t handle);
+            ssize_t             getStrongRefCountForNode(const sp<BpBinder>& binder);
 
             enum class CallRestriction {
                 // all calls okay
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index a57beee..bdb74dc 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -105,6 +105,14 @@
     ],
     tidy_checks_as_errors: [
         "*",
+        "-clang-analyzer-core.CallAndMessage",
+        "-clang-analyzer-core.uninitialized.Assign",
+        "-clang-analyzer-unix.Malloc,",
+        "-clang-analyzer-deadcode.DeadStores",
+        "-clang-analyzer-optin.cplusplus.UninitializedObject",
+        "-misc-no-recursion",
+        "-misc-redundant-expression",
+        "-misc-unused-using-decls",
     ],
 }
 
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 04167f7..53871f2 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -27,6 +27,7 @@
 #pragma once
 
 #include <android/binder_ibinder.h>
+#include <android/binder_internal_logging.h>
 #include <android/binder_parcel.h>
 #include <android/binder_status.h>
 
@@ -130,7 +131,7 @@
 /**
  * This baseclass owns a single object, used to make various classes RAII.
  */
-template <typename T, typename R, R (*Destroy)(T), T DEFAULT>
+template <typename T, void (*Destroy)(T), T DEFAULT>
 class ScopedAResource {
    public:
     /**
@@ -198,7 +199,7 @@
 /**
  * Convenience wrapper. See AParcel.
  */
-class ScopedAParcel : public impl::ScopedAResource<AParcel*, void, AParcel_delete, nullptr> {
+class ScopedAParcel : public impl::ScopedAResource<AParcel*, AParcel_delete, nullptr> {
    public:
     /**
      * Takes ownership of a.
@@ -219,7 +220,7 @@
 /**
  * Convenience wrapper. See AStatus.
  */
-class ScopedAStatus : public impl::ScopedAResource<AStatus*, void, AStatus_delete, nullptr> {
+class ScopedAStatus : public impl::ScopedAResource<AStatus*, AStatus_delete, nullptr> {
    public:
     /**
      * Takes ownership of a.
@@ -291,7 +292,7 @@
  * Convenience wrapper. See AIBinder_DeathRecipient.
  */
 class ScopedAIBinder_DeathRecipient
-    : public impl::ScopedAResource<AIBinder_DeathRecipient*, void, AIBinder_DeathRecipient_delete,
+    : public impl::ScopedAResource<AIBinder_DeathRecipient*, AIBinder_DeathRecipient_delete,
                                    nullptr> {
    public:
     /**
@@ -308,7 +309,7 @@
  * Convenience wrapper. See AIBinder_Weak.
  */
 class ScopedAIBinder_Weak
-    : public impl::ScopedAResource<AIBinder_Weak*, void, AIBinder_Weak_delete, nullptr> {
+    : public impl::ScopedAResource<AIBinder_Weak*, AIBinder_Weak_delete, nullptr> {
    public:
     /**
      * Takes ownership of a.
@@ -324,10 +325,22 @@
     SpAIBinder promote() { return SpAIBinder(AIBinder_Weak_promote(get())); }
 };
 
+namespace internal {
+
+static void closeWithError(int fd) {
+    if (fd == -1) return;
+    int ret = close(fd);
+    if (ret != 0) {
+        syslog(LOG_ERR, "Could not close FD %d: %s", fd, strerror(errno));
+    }
+}
+
+}  // namespace internal
+
 /**
  * Convenience wrapper for a file descriptor.
  */
-class ScopedFileDescriptor : public impl::ScopedAResource<int, int, close, -1> {
+class ScopedFileDescriptor : public impl::ScopedAResource<int, internal::closeWithError, -1> {
    public:
     /**
      * Takes ownership of a.
diff --git a/libs/binder/ndk/include_cpp/android/binder_internal_logging.h b/libs/binder/ndk/include_cpp/android/binder_internal_logging.h
new file mode 100644
index 0000000..88c6443
--- /dev/null
+++ b/libs/binder/ndk/include_cpp/android/binder_internal_logging.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+/**
+ * @addtogroup NdkBinder
+ * @{
+ */
+
+/**
+ * @file binder_internal_logging.h
+ * @brief This provides the ability to use syslog from binder headers, since
+ * other logging functionality might be inaccessable.
+ */
+
+#pragma once
+
+// defined differently by liblog
+#pragma push_macro("LOG_PRI")
+#ifdef LOG_PRI
+#undef LOG_PRI
+#endif
+#include <syslog.h>
+#pragma pop_macro("LOG_PRI")
+
+/** @} */
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 054aebe..83190aa 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -27,6 +27,7 @@
 #pragma once
 
 #include <android/binder_auto_utils.h>
+#include <android/binder_internal_logging.h>
 #include <android/binder_parcel.h>
 
 #include <optional>
@@ -179,6 +180,7 @@
 static inline binder_status_t AParcel_writeRequiredStrongBinder(AParcel* parcel,
                                                                 const SpAIBinder& binder) {
     if (binder.get() == nullptr) {
+        syslog(LOG_ERR, "Passing null binder object as non-@nullable AIDL IBinder");
         return STATUS_UNEXPECTED_NULL;
     }
     return AParcel_writeStrongBinder(parcel, binder.get());
@@ -228,6 +230,7 @@
 static inline binder_status_t AParcel_writeRequiredParcelFileDescriptor(
         AParcel* parcel, const ScopedFileDescriptor& fd) {
     if (fd.get() < 0) {
+        syslog(LOG_ERR, "Passing -1 file descriptor as non-@nullable AIDL ParcelFileDescriptor");
         return STATUS_UNEXPECTED_NULL;
     }
     return AParcel_writeParcelFileDescriptor(parcel, fd.get());
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index cf2b9e9..6636a41 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -82,56 +82,47 @@
     }
 
     template <typename T>
-    bool setParcelable(const T& p) {
+    binder_status_t setParcelable(const T& p) {
         if (this->mStability > T::_aidl_stability) {
-            return false;
+            return STATUS_BAD_VALUE;
         }
         AParcel_reset(mParcel.get());
         AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor));
         p.writeToParcel(mParcel.get());
-        return true;
+        return STATUS_OK;
     }
 
     template <typename T>
-    std::unique_ptr<T> getParcelable() const {
+    binder_status_t getParcelable(std::optional<T>* ret) const {
         const std::string parcelableDesc(T::descriptor);
         AParcel_setDataPosition(mParcel.get(), 0);
         if (AParcel_getDataSize(mParcel.get()) == 0) {
-            return nullptr;
+            *ret = std::nullopt;
+            return STATUS_OK;
         }
         std::string parcelableDescInParcel;
         binder_status_t status = AParcel_readString(mParcel.get(), &parcelableDescInParcel);
         if (status != STATUS_OK || parcelableDesc != parcelableDescInParcel) {
-            return nullptr;
+            *ret = std::nullopt;
+            return status;
         }
-        std::unique_ptr<T> ret = std::make_unique<T>();
-        status = ret->readFromParcel(this->mParcel.get());
+        *ret = std::make_optional<T>();
+        status = (*ret)->readFromParcel(this->mParcel.get());
         if (status != STATUS_OK) {
-            return nullptr;
+            *ret = std::nullopt;
+            return status;
         }
-        return std::move(ret);
+        return STATUS_OK;
     }
 
     void reset() { AParcel_reset(mParcel.get()); }
 
-    inline bool operator!=(const AParcelableHolder& rhs) const {
-        return std::tie(mParcel, mStability) != std::tie(rhs.mParcel, rhs.mStability);
-    }
-    inline bool operator<(const AParcelableHolder& rhs) const {
-        return std::tie(mParcel, mStability) < std::tie(rhs.mParcel, rhs.mStability);
-    }
-    inline bool operator<=(const AParcelableHolder& rhs) const {
-        return std::tie(mParcel, mStability) <= std::tie(rhs.mParcel, rhs.mStability);
-    }
-    inline bool operator==(const AParcelableHolder& rhs) const {
-        return std::tie(mParcel, mStability) == std::tie(rhs.mParcel, rhs.mStability);
-    }
-    inline bool operator>(const AParcelableHolder& rhs) const {
-        return std::tie(mParcel, mStability) > std::tie(rhs.mParcel, rhs.mStability);
-    }
-    inline bool operator>=(const AParcelableHolder& rhs) const {
-        return std::tie(mParcel, mStability) >= std::tie(rhs.mParcel, rhs.mStability);
-    }
+    inline bool operator!=(const AParcelableHolder& rhs) const { return this != &rhs; }
+    inline bool operator<(const AParcelableHolder& rhs) const { return this < &rhs; }
+    inline bool operator<=(const AParcelableHolder& rhs) const { return this <= &rhs; }
+    inline bool operator==(const AParcelableHolder& rhs) const { return this == &rhs; }
+    inline bool operator>(const AParcelableHolder& rhs) const { return this > &rhs; }
+    inline bool operator>=(const AParcelableHolder& rhs) const { return this >= &rhs; }
 
    private:
     mutable ndk::ScopedAParcel mParcel;
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 8ee6a62..edfb56a 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -122,7 +122,7 @@
     pub use super::parcel::ParcelFileDescriptor;
     pub use super::{add_service, get_interface};
     pub use super::{
-        ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode,
+        ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode, WpIBinder,
     };
 
     /// Binder result containing a [`Status`] on error.
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 485bb42..17af099 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -102,6 +102,11 @@
             class.as_ref().map(|p| InterfaceClass::from_ptr(p))
         }
     }
+
+    /// Creates a new weak reference to this binder object.
+    pub fn downgrade(&mut self) -> WpIBinder {
+        WpIBinder::new(self)
+    }
 }
 
 /// An object that can be associate with an [`InterfaceClass`].
@@ -370,15 +375,25 @@
 
 /// A weak reference to a Binder remote object.
 ///
-/// This struct encapsulates the C++ `wp<IBinder>` class. However, this wrapper
-/// is untyped, so properly typed versions implementing a particular binder
-/// interface should be crated with [`declare_binder_interface!`].
+/// This struct encapsulates the generic C++ `wp<IBinder>` class. This wrapper
+/// is untyped; typed interface access is implemented by the AIDL compiler.
 pub struct WpIBinder(*mut sys::AIBinder_Weak);
 
+impl fmt::Debug for WpIBinder {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.pad("WpIBinder")
+    }
+}
+
+/// # Safety
+///
+/// A `WpIBinder` is a handle to a C++ IBinder, which is thread-safe.
+unsafe impl Send for WpIBinder {}
+
 impl WpIBinder {
     /// Create a new weak reference from an object that can be converted into a
     /// raw `AIBinder` pointer.
-    pub fn new<B: AsNative<sys::AIBinder>>(binder: &mut B) -> WpIBinder {
+    fn new<B: AsNative<sys::AIBinder>>(binder: &mut B) -> WpIBinder {
         let ptr = unsafe {
             // Safety: `SpIBinder` guarantees that `binder` always contains a
             // valid pointer to an `AIBinder`.
@@ -401,6 +416,16 @@
     }
 }
 
+impl Drop for WpIBinder {
+    fn drop(&mut self) {
+        unsafe {
+            // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, so we
+            // know this pointer is safe to pass to `AIBinder_Weak_delete` here.
+            sys::AIBinder_Weak_delete(self.0);
+        }
+    }
+}
+
 /// Rust wrapper around DeathRecipient objects.
 #[repr(C)]
 pub struct DeathRecipient {
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
index c685b41..6ca0e2f 100644
--- a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
+++ b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
@@ -48,9 +48,7 @@
 static const std::vector<std::function<void(FuzzedDataProvider*, const sp<BpBinder>&,
                                             const sp<IBinder::DeathRecipient>&)>>
         gBPBinderOperations =
-                {[](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
-                    const sp<IBinder::DeathRecipient>&) -> void { bpbinder->handle(); },
-                 [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+                {[](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
                     const sp<IBinder::DeathRecipient>& s_recipient) -> void {
                      // Clean up possible leftover memory.
                      wp<IBinder::DeathRecipient> outRecipient(nullptr);
diff --git a/libs/binderdebug/Android.bp b/libs/binderdebug/Android.bp
new file mode 100644
index 0000000..343246a
--- /dev/null
+++ b/libs/binderdebug/Android.bp
@@ -0,0 +1,28 @@
+// 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.
+
+cc_library {
+    name: "libbinderdebug",
+    vendor_available: true,
+    shared_libs: [
+        "libbase",
+        "libbinder",
+    ],
+    srcs: [
+        "BinderDebug.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+}
diff --git a/libs/binderdebug/BinderDebug.cpp b/libs/binderdebug/BinderDebug.cpp
new file mode 100644
index 0000000..b435dba
--- /dev/null
+++ b/libs/binderdebug/BinderDebug.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <binder/Binder.h>
+#include <sys/types.h>
+#include <fstream>
+#include <regex>
+
+#include <binderdebug/BinderDebug.h>
+
+namespace android {
+
+static std::string contextToString(BinderDebugContext context) {
+    switch (context) {
+        case BinderDebugContext::BINDER:
+            return "binder";
+        case BinderDebugContext::HWBINDER:
+            return "hwbinder";
+        case BinderDebugContext::VNDBINDER:
+            return "vndbinder";
+        default:
+            return std::string();
+    }
+}
+
+static status_t scanBinderContext(pid_t pid, const std::string& contextName,
+                                  std::function<void(const std::string&)> eachLine) {
+    std::ifstream ifs("/dev/binderfs/binder_logs/proc/" + std::to_string(pid));
+    if (!ifs.is_open()) {
+        ifs.open("/d/binder/proc/" + std::to_string(pid));
+        if (!ifs.is_open()) {
+            return -errno;
+        }
+    }
+    static const std::regex kContextLine("^context (\\w+)$");
+
+    bool isDesiredContext = false;
+    std::string line;
+    std::smatch match;
+    while (getline(ifs, line)) {
+        if (std::regex_search(line, match, kContextLine)) {
+            isDesiredContext = match.str(1) == contextName;
+            continue;
+        }
+        if (!isDesiredContext) {
+            continue;
+        }
+        eachLine(line);
+    }
+    return OK;
+}
+
+status_t getBinderPidInfo(BinderDebugContext context, pid_t pid, BinderPidInfo* pidInfo) {
+    std::smatch match;
+    static const std::regex kReferencePrefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
+    static const std::regex kThreadPrefix("^\\s*thread \\d+:\\s+l\\s+(\\d)(\\d)");
+    std::string contextStr = contextToString(context);
+    status_t ret = scanBinderContext(pid, contextStr, [&](const std::string& line) {
+        if (std::regex_search(line, match, kReferencePrefix)) {
+            const std::string& ptrString = "0x" + match.str(2); // use number after c
+            uint64_t ptr;
+            if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
+                // Should not reach here, but just be tolerant.
+                return;
+            }
+            const std::string proc = " proc ";
+            auto pos = line.rfind(proc);
+            if (pos != std::string::npos) {
+                for (const std::string& pidStr : base::Split(line.substr(pos + proc.size()), " ")) {
+                    int32_t pid;
+                    if (!::android::base::ParseInt(pidStr, &pid)) {
+                        return;
+                    }
+                    pidInfo->refPids[ptr].push_back(pid);
+                }
+            }
+
+            return;
+        }
+        if (std::regex_search(line, match, kThreadPrefix)) {
+            // "1" is waiting in binder driver
+            // "2" is poll. It's impossible to tell if these are in use.
+            //     and HIDL default code doesn't use it.
+            bool isInUse = match.str(1) != "1";
+            // "0" is a thread that has called into binder
+            // "1" is looper thread
+            // "2" is main looper thread
+            bool isBinderThread = match.str(2) != "0";
+            if (!isBinderThread) {
+                return;
+            }
+            if (isInUse) {
+                pidInfo->threadUsage++;
+            }
+
+            pidInfo->threadCount++;
+            return;
+        }
+        return;
+    });
+    return ret;
+}
+
+} // namespace  android
diff --git a/libs/binderdebug/TEST_MAPPING b/libs/binderdebug/TEST_MAPPING
new file mode 100644
index 0000000..2f3353e
--- /dev/null
+++ b/libs/binderdebug/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libbinderdebug_test"
+    }
+  ]
+}
diff --git a/libs/binderdebug/include/binderdebug/BinderDebug.h b/libs/binderdebug/include/binderdebug/BinderDebug.h
new file mode 100644
index 0000000..14a0ef3
--- /dev/null
+++ b/libs/binderdebug/include/binderdebug/BinderDebug.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <map>
+#include <vector>
+
+namespace android {
+
+struct BinderPidInfo {
+    std::map<uint64_t, std::vector<pid_t>> refPids; // cookie -> processes which hold binder
+    uint32_t threadUsage;                           // number of threads in use
+    uint32_t threadCount;                           // number of threads total
+};
+
+enum class BinderDebugContext {
+    BINDER,
+    HWBINDER,
+    VNDBINDER,
+};
+
+status_t getBinderPidInfo(BinderDebugContext context, pid_t pid, BinderPidInfo* pidInfo);
+
+} // namespace  android
diff --git a/libs/binderdebug/tests/Android.bp b/libs/binderdebug/tests/Android.bp
new file mode 100644
index 0000000..4c06b1d
--- /dev/null
+++ b/libs/binderdebug/tests/Android.bp
@@ -0,0 +1,30 @@
+// 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.
+
+cc_test {
+    name: "libbinderdebug_test",
+    test_suites: ["general-tests"],
+    srcs: [
+        "binderdebug_test.cpp",
+        "android/binderdebug/test/IControl.aidl",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libutils",
+    ],
+    static_libs: ["libbinderdebug"],
+    cflags: ["-Wall", "-Werror"],
+    require_root: true,
+}
diff --git a/libs/binderdebug/tests/android/binderdebug/test/IControl.aidl b/libs/binderdebug/tests/android/binderdebug/test/IControl.aidl
new file mode 100644
index 0000000..8efeb63
--- /dev/null
+++ b/libs/binderdebug/tests/android/binderdebug/test/IControl.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+package android.binderdebug.test;
+
+interface IControl {
+    // Notifies the service to continue execution
+    void Continue();
+}
diff --git a/libs/binderdebug/tests/binderdebug_test.cpp b/libs/binderdebug/tests/binderdebug_test.cpp
new file mode 100644
index 0000000..ea799c0
--- /dev/null
+++ b/libs/binderdebug/tests/binderdebug_test.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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/Binder.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/IPCThreadState.h>
+#include <binderdebug/BinderDebug.h>
+#include <gtest/gtest.h>
+#include <semaphore.h>
+#include <thread>
+
+#include <android/binderdebug/test/BnControl.h>
+#include <android/binderdebug/test/IControl.h>
+
+namespace android {
+namespace binderdebug {
+namespace test {
+
+class Control : public BnControl {
+public:
+    Control() {sem_init(&s, 1, 0);};
+    ::android::binder::Status Continue() override;
+    sem_t s;
+};
+
+::android::binder::Status Control::Continue() {
+    IPCThreadState::self()->flushCommands();
+    sem_post(&s);
+    return binder::Status::ok();
+}
+
+TEST(BinderDebugTests, BinderPid) {
+    BinderPidInfo pidInfo;
+    const auto& status = getBinderPidInfo(BinderDebugContext::BINDER, getpid(), &pidInfo);
+    ASSERT_EQ(status, OK);
+    // There should be one referenced PID for servicemanager
+    EXPECT_TRUE(!pidInfo.refPids.empty());
+}
+
+TEST(BinderDebugTests, BinderThreads) {
+    BinderPidInfo pidInfo;
+    const auto& status = getBinderPidInfo(BinderDebugContext::BINDER, getpid(), &pidInfo);
+    ASSERT_EQ(status, OK);
+    EXPECT_TRUE(pidInfo.threadUsage <= pidInfo.threadCount);
+    // The second looper thread can sometimes take longer to spawn.
+    EXPECT_GE(pidInfo.threadCount, 1);
+}
+
+extern "C" {
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    // Create a child/client process to call into the main process so we can ensure
+    // looper thread has been registered before attempting to get the BinderPidInfo
+    pid_t pid = fork();
+    if (pid == 0) {
+        sp<IBinder> binder = android::defaultServiceManager()->getService(String16("binderdebug"));
+        sp<IControl> service;
+        if (binder != nullptr) {
+            service = android::interface_cast<IControl>(binder);
+        }
+        service->Continue();
+        exit(0);
+    }
+    sp<Control> iface = new Control;
+    android::defaultServiceManager()->addService(String16("binderdebug"), iface);
+    android::ProcessState::self()->setThreadPoolMaxThreadCount(8);
+    ProcessState::self()->startThreadPool();
+    sem_wait(&iface->s);
+
+    return RUN_ALL_TESTS();
+}
+} // extern "C"
+} // namespace  test
+} // namespace  binderdebug
+} // namespace  android
diff --git a/libs/ftl/.clang-format b/libs/ftl/.clang-format
new file mode 120000
index 0000000..86b1593
--- /dev/null
+++ b/libs/ftl/.clang-format
@@ -0,0 +1 @@
+../../../../build/soong/scripts/system-clang-format-2
\ No newline at end of file
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index eb8e57a..5bccaca 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -5,9 +5,10 @@
         address: true,
     },
     srcs: [
-        "SmallMap_test.cpp",
-        "SmallVector_test.cpp",
-        "StaticVector_test.cpp",
+        "future_test.cpp",
+        "small_map_test.cpp",
+        "small_vector_test.cpp",
+        "static_vector_test.cpp",
     ],
     cflags: [
         "-Wall",
diff --git a/libs/ftl/README.md b/libs/ftl/README.md
new file mode 100644
index 0000000..bdd750f
--- /dev/null
+++ b/libs/ftl/README.md
@@ -0,0 +1,41 @@
+# FTL
+
+FTL is a template library shared by SurfaceFlinger and InputFlinger, inspired by
+and supplementing the C++ Standard Library. The intent is to fill gaps for areas
+not (yet) covered—like cache-efficient data structures and lock-free concurrency
+primitives—and implement proposals that are missing or experimental in Android's
+libc++ branch. The design takes some liberties with standard compliance, notably
+assuming that exceptions are disabled.
+
+## Tests
+
+    atest ftl_test
+
+## Style
+
+- Based on [Google C++ Style](https://google.github.io/styleguide/cppguide.html).
+- Informed by [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines).
+
+Naming conventions are as follows:
+
+- `PascalCase`
+    - Types and aliases, except standard interfaces.
+    - Template parameters, including non-type ones.
+- `snake_case`
+    - Variables, and data members with trailing underscore.
+    - Functions, free and member alike.
+    - Type traits, with standard `_t` and `_v` suffixes.
+- `kCamelCase`
+    - Enumerators and `constexpr` constants with static storage duration.
+- `MACRO_CASE`
+    - Macros, with `FTL_` prefix unless `#undef`ed.
+
+Template parameter packs are named with the following convention:
+
+    typename T, typename... Ts
+    typename Arg, typename... Args
+
+    std::size_t I, std::size_t... Is
+    std::size_t Size, std::size_t... Sizes
+
+The `details` namespace contains implementation details.
diff --git a/libs/ftl/SmallMap_test.cpp b/libs/ftl/SmallMap_test.cpp
deleted file mode 100644
index fa00c06..0000000
--- a/libs/ftl/SmallMap_test.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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 <ftl/SmallMap.h>
-#include <gtest/gtest.h>
-
-#include <cctype>
-
-namespace android::test {
-
-using ftl::SmallMap;
-
-// Keep in sync with example usage in header file.
-TEST(SmallMap, Example) {
-    ftl::SmallMap<int, std::string, 3> map;
-    EXPECT_TRUE(map.empty());
-    EXPECT_FALSE(map.dynamic());
-
-    map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
-    EXPECT_EQ(map.size(), 3u);
-    EXPECT_FALSE(map.dynamic());
-
-    EXPECT_TRUE(map.contains(123));
-
-    EXPECT_EQ(map.find(42, [](const std::string& s) { return s.size(); }), 3u);
-
-    const auto opt = map.find(-1);
-    ASSERT_TRUE(opt);
-
-    std::string& ref = *opt;
-    EXPECT_TRUE(ref.empty());
-    ref = "xyz";
-
-    EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
-}
-
-TEST(SmallMap, Construct) {
-    {
-        // Default constructor.
-        SmallMap<int, std::string, 2> map;
-
-        EXPECT_TRUE(map.empty());
-        EXPECT_FALSE(map.dynamic());
-    }
-    {
-        // In-place constructor with same types.
-        SmallMap<int, std::string, 5> map =
-                ftl::init::map<int, std::string>(123, "abc")(456, "def")(789, "ghi");
-
-        EXPECT_EQ(map.size(), 3u);
-        EXPECT_EQ(map.max_size(), 5u);
-        EXPECT_FALSE(map.dynamic());
-
-        EXPECT_EQ(map, SmallMap(ftl::init::map(123, "abc")(456, "def")(789, "ghi")));
-    }
-    {
-        // In-place constructor with different types.
-        SmallMap<int, std::string, 5> map =
-                ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
-
-        EXPECT_EQ(map.size(), 3u);
-        EXPECT_EQ(map.max_size(), 5u);
-        EXPECT_FALSE(map.dynamic());
-
-        EXPECT_EQ(map, SmallMap(ftl::init::map(42, "???")(123, "abc")(-1, "\0\0\0")));
-    }
-    {
-        // In-place constructor with implicit size.
-        SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
-
-        static_assert(std::is_same_v<decltype(map), SmallMap<int, std::string, 3>>);
-        EXPECT_EQ(map.size(), 3u);
-        EXPECT_EQ(map.max_size(), 3u);
-        EXPECT_FALSE(map.dynamic());
-
-        EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "\0\0\0")(42, "???")(123, "abc")));
-    }
-}
-
-TEST(SmallMap, Find) {
-    {
-        // Constant reference.
-        const ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
-
-        const auto opt = map.find('b');
-        EXPECT_EQ(opt, 'B');
-
-        const char d = 'D';
-        const auto ref = map.find('d').value_or(std::cref(d));
-        EXPECT_EQ(ref.get(), 'D');
-    }
-    {
-        // Mutable reference.
-        ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
-
-        const auto opt = map.find('c');
-        EXPECT_EQ(opt, 'C');
-
-        char d = 'd';
-        const auto ref = map.find('d').value_or(std::ref(d));
-        ref.get() = 'D';
-        EXPECT_EQ(d, 'D');
-    }
-    {
-        // Constant unary operation.
-        const ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
-        EXPECT_EQ(map.find('c', [](char c) { return std::toupper(c); }), 'Z');
-    }
-    {
-        // Mutable unary operation.
-        ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
-        EXPECT_TRUE(map.find('c', [](char& c) { c = std::toupper(c); }));
-
-        EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x')));
-    }
-}
-
-} // namespace android::test
diff --git a/libs/ftl/SmallVector_test.cpp b/libs/ftl/SmallVector_test.cpp
deleted file mode 100644
index d0c2858..0000000
--- a/libs/ftl/SmallVector_test.cpp
+++ /dev/null
@@ -1,465 +0,0 @@
-/*
- * 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 <ftl/SmallVector.h>
-#include <gtest/gtest.h>
-
-#include <algorithm>
-#include <iterator>
-#include <string>
-#include <utility>
-
-using namespace std::string_literals;
-
-namespace android::test {
-
-using ftl::SmallVector;
-
-// Keep in sync with example usage in header file.
-TEST(SmallVector, Example) {
-    ftl::SmallVector<char, 3> vector;
-    EXPECT_TRUE(vector.empty());
-    EXPECT_FALSE(vector.dynamic());
-
-    vector = {'a', 'b', 'c'};
-    EXPECT_EQ(vector.size(), 3u);
-    EXPECT_FALSE(vector.dynamic());
-
-    vector.push_back('d');
-    EXPECT_TRUE(vector.dynamic());
-
-    vector.unstable_erase(vector.begin());
-    EXPECT_EQ(vector, (ftl::SmallVector{'d', 'b', 'c'}));
-
-    vector.pop_back();
-    EXPECT_EQ(vector.back(), 'b');
-    EXPECT_TRUE(vector.dynamic());
-
-    const char array[] = "hi";
-    vector = ftl::SmallVector(array);
-    EXPECT_EQ(vector, (ftl::SmallVector{'h', 'i', '\0'}));
-    EXPECT_FALSE(vector.dynamic());
-
-    ftl::SmallVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?');
-    ASSERT_EQ(strings.size(), 3u);
-    EXPECT_FALSE(strings.dynamic());
-
-    EXPECT_EQ(strings[0], "abc");
-    EXPECT_EQ(strings[1], "123");
-    EXPECT_EQ(strings[2], "???");
-}
-
-TEST(SmallVector, Construct) {
-    {
-        // Default constructor.
-        SmallVector<std::string, 2> vector;
-
-        EXPECT_TRUE(vector.empty());
-        EXPECT_FALSE(vector.dynamic());
-    }
-    {
-        // Array constructor.
-        const float kFloats[] = {.1f, .2f, .3f};
-        SmallVector vector(kFloats);
-
-        EXPECT_EQ(vector, (SmallVector{.1f, .2f, .3f}));
-        EXPECT_FALSE(vector.dynamic());
-    }
-    {
-        // Iterator constructor.
-        const char chars[] = "abcdef";
-        std::string string(chars);
-        SmallVector<char, sizeof(chars)> vector(string.begin(), string.end());
-
-        EXPECT_STREQ(vector.begin(), chars);
-        EXPECT_FALSE(vector.dynamic());
-    }
-    {
-        // Variadic constructor with same types.
-        SmallVector vector = {1, 2, 3};
-
-        static_assert(std::is_same_v<decltype(vector), SmallVector<int, 3>>);
-        EXPECT_EQ(vector, (SmallVector{1, 2, 3}));
-        EXPECT_FALSE(vector.dynamic());
-    }
-    {
-        // Variadic constructor with different types.
-        const auto copy = "quince"s;
-        auto move = "tart"s;
-        SmallVector vector = {copy, std::move(move)};
-
-        static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 2>>);
-        EXPECT_EQ(vector, (SmallVector{"quince"s, "tart"s}));
-        EXPECT_FALSE(vector.dynamic());
-    }
-    {
-        // In-place constructor with same types.
-        SmallVector vector =
-                ftl::init::list<std::string>("redolent", 3u)("velveteen", 6u)("cakewalk", 4u);
-
-        static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
-        EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
-        EXPECT_FALSE(vector.dynamic());
-    }
-    {
-        // In-place constructor with different types.
-        const auto copy = "red"s;
-        auto move = "velvet"s;
-        std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
-        SmallVector vector = ftl::init::list<std::string>(copy.c_str())(std::move(move))(list);
-
-        static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
-        EXPECT_TRUE(move.empty());
-        EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
-        EXPECT_FALSE(vector.dynamic());
-    }
-    {
-        // Conversion from StaticVector.
-        ftl::StaticVector doubles = {.1, .2, .3};
-        SmallVector vector = std::move(doubles);
-        EXPECT_TRUE(doubles.empty());
-
-        static_assert(std::is_same_v<decltype(vector), SmallVector<double, 3>>);
-        EXPECT_EQ(vector, (SmallVector{.1, .2, .3}));
-        EXPECT_FALSE(vector.dynamic());
-    }
-}
-
-TEST(SmallVector, String) {
-    SmallVector<char, 10> chars;
-    char c = 'a';
-    std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; });
-    chars.push_back('\0');
-
-    EXPECT_TRUE(chars.dynamic());
-    EXPECT_EQ(chars.size(), 11u);
-    EXPECT_STREQ(chars.begin(), "abcdefghij");
-
-    // Constructor takes iterator range.
-    const char kString[] = "123456";
-    SmallVector<char, 10> string(std::begin(kString), std::end(kString));
-
-    EXPECT_FALSE(string.dynamic());
-    EXPECT_STREQ(string.begin(), "123456");
-    EXPECT_EQ(string.size(), 7u);
-
-    // Similar to emplace, but replaces rather than inserts.
-    string.replace(string.begin() + 5, '\0');
-    EXPECT_STREQ(string.begin(), "12345");
-
-    swap(chars, string);
-
-    EXPECT_STREQ(chars.begin(), "12345");
-    EXPECT_STREQ(string.begin(), "abcdefghij");
-
-    EXPECT_FALSE(chars.dynamic());
-    EXPECT_TRUE(string.dynamic());
-}
-
-TEST(SmallVector, CopyableElement) {
-    struct Pair {
-        // Needed because std::vector emplace does not use uniform initialization.
-        Pair(int a, int b) : a(a), b(b) {}
-
-        const int a, b;
-        bool operator==(Pair p) const { return p.a == a && p.b == b; }
-    };
-
-    SmallVector<Pair, 5> pairs;
-
-    EXPECT_TRUE(pairs.empty());
-    EXPECT_EQ(pairs.max_size(), 5u);
-
-    for (size_t i = 0; i < pairs.max_size(); ++i) {
-        EXPECT_EQ(pairs.size(), i);
-
-        const int a = static_cast<int>(i) * 2;
-        EXPECT_EQ(pairs.emplace_back(a, a + 1), Pair(a, a + 1));
-    }
-
-    EXPECT_EQ(pairs.size(), 5u);
-    EXPECT_FALSE(pairs.dynamic());
-
-    // The vector is promoted when full.
-    EXPECT_EQ(pairs.emplace_back(10, 11), Pair(10, 11));
-    EXPECT_TRUE(pairs.dynamic());
-
-    EXPECT_EQ(pairs,
-              (SmallVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9},
-                           Pair{10, 11}}));
-
-    // Constructor takes at most N elements.
-    SmallVector<int, 6> sums = {0, 0, 0, 0, 0, 0};
-    EXPECT_FALSE(sums.dynamic());
-
-    // Random-access iterators comply with standard.
-    std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; });
-    EXPECT_EQ(sums, (SmallVector{1, 5, 9, 13, 17, 21}));
-
-    sums.pop_back();
-    std::reverse(sums.begin(), sums.end());
-
-    EXPECT_EQ(sums, (SmallVector{17, 13, 9, 5, 1}));
-}
-
-TEST(SmallVector, MovableElement) {
-    // Construct std::string elements in place from per-element arguments.
-    SmallVector strings = ftl::init::list<std::string>()()()("cake")("velvet")("red")();
-    strings.pop_back();
-
-    EXPECT_EQ(strings.max_size(), 7u);
-    EXPECT_EQ(strings.size(), 6u);
-
-    // Erase "cake" and append a substring copy.
-    {
-        const auto it = std::find_if(strings.begin(), strings.end(),
-                                     [](const auto& s) { return !s.empty(); });
-        ASSERT_FALSE(it == strings.end());
-        EXPECT_EQ(*it, "cake");
-
-        // Construct std::string from first 4 characters of string literal.
-        strings.unstable_erase(it);
-        EXPECT_EQ(strings.emplace_back("cakewalk", 4u), "cake"s);
-    }
-
-    strings[1] = "quince"s;
-
-    // Replace last empty string with "tart".
-    {
-        const auto rit = std::find(strings.rbegin(), strings.rend(), std::string());
-        ASSERT_FALSE(rit == strings.rend());
-
-        std::initializer_list<char> list = {'t', 'a', 'r', 't'};
-        strings.replace(rit.base() - 1, list);
-    }
-
-    strings.front().assign("pie");
-
-    EXPECT_EQ(strings, (SmallVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s}));
-
-    strings.push_back("nougat");
-    strings.push_back("oreo");
-    EXPECT_TRUE(strings.dynamic());
-
-    std::rotate(strings.begin(), strings.end() - 2, strings.end());
-
-    EXPECT_EQ(strings,
-              (SmallVector{"nougat"s, "oreo"s, "pie"s, "quince"s, "tart"s, "red"s, "velvet"s,
-                           "cake"s}));
-}
-
-TEST(SmallVector, Replace) {
-    // Replacing does not require a copy/move assignment operator.
-    struct Word {
-        explicit Word(std::string str) : str(std::move(str)) {}
-        const std::string str;
-
-        bool operator==(const Word& other) const { return other.str == str; }
-    };
-
-    SmallVector words = ftl::init::list<Word>("colored")("velour");
-
-    // The replaced element can be referenced by the replacement.
-    {
-        const Word& word = words.replace(words.last(), words.back().str.substr(0, 3) + "vet");
-        EXPECT_EQ(word, Word("velvet"));
-    }
-
-    // The vector is not promoted if replacing while full.
-    EXPECT_FALSE(words.dynamic());
-
-    words.emplace_back("cake");
-    EXPECT_TRUE(words.dynamic());
-
-    {
-        const Word& word = words.replace(words.begin(), words.front().str.substr(4));
-        EXPECT_EQ(word, Word("red"));
-    }
-
-    EXPECT_EQ(words, (SmallVector{Word("red"), Word("velvet"), Word("cake")}));
-}
-
-TEST(SmallVector, ReverseAppend) {
-    SmallVector strings = {"red"s, "velvet"s, "cake"s};
-    EXPECT_FALSE(strings.dynamic());
-
-    auto rit = strings.rbegin();
-    while (rit != strings.rend()) {
-        // Iterator and reference are invalidated on insertion.
-        const auto i = std::distance(strings.begin(), rit.base());
-        std::string s = *rit;
-
-        strings.push_back(std::move(s));
-        rit = std::make_reverse_iterator(strings.begin() + i) + 1;
-    }
-
-    EXPECT_EQ(strings, (SmallVector{"red"s, "velvet"s, "cake"s, "cake"s, "velvet"s, "red"s}));
-    EXPECT_TRUE(strings.dynamic());
-}
-
-TEST(SmallVector, Sort) {
-    SmallVector strings = ftl::init::list<std::string>("pie")("quince")("tart")("red")("velvet");
-    strings.push_back("cake"s);
-
-    auto sorted = std::move(strings);
-    EXPECT_TRUE(strings.empty());
-
-    EXPECT_TRUE(sorted.dynamic());
-    EXPECT_TRUE(strings.dynamic());
-
-    std::sort(sorted.begin(), sorted.end());
-    EXPECT_EQ(sorted, (SmallVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s}));
-
-    // Constructor takes array reference.
-    {
-        const char* kStrings[] = {"cake", "lie"};
-        strings = SmallVector(kStrings);
-        EXPECT_FALSE(strings.dynamic());
-    }
-
-    EXPECT_GT(sorted, strings);
-    swap(sorted, strings);
-    EXPECT_LT(sorted, strings);
-
-    EXPECT_FALSE(sorted.dynamic());
-    EXPECT_TRUE(strings.dynamic());
-
-    // Append remaining elements, such that "pie" is the only difference.
-    for (const char* str : {"quince", "red", "tart", "velvet"}) {
-        sorted.emplace_back(str);
-    }
-    EXPECT_TRUE(sorted.dynamic());
-
-    EXPECT_NE(sorted, strings);
-
-    // Replace second element with "pie".
-    const auto it = sorted.begin() + 1;
-    EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie");
-
-    EXPECT_EQ(sorted, strings);
-}
-
-namespace {
-
-struct DestroyCounts {
-    DestroyCounts(int& live, int& dead) : counts{live, dead} {}
-    DestroyCounts(const DestroyCounts& other) : counts(other.counts) {}
-    DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; }
-    ~DestroyCounts() { ++(alive ? counts.live : counts.dead); }
-
-    struct {
-        int& live;
-        int& dead;
-    } counts;
-
-    bool alive = true;
-};
-
-void swap(DestroyCounts& lhs, DestroyCounts& rhs) {
-    std::swap(lhs.alive, rhs.alive);
-}
-
-} // namespace
-
-TEST(SmallVector, Destroy) {
-    int live = 0;
-    int dead = 0;
-
-    { SmallVector<DestroyCounts, 3> counts; }
-    EXPECT_EQ(0, live);
-    EXPECT_EQ(0, dead);
-
-    {
-        SmallVector<DestroyCounts, 3> counts;
-        counts.emplace_back(live, dead);
-        counts.emplace_back(live, dead);
-        counts.emplace_back(live, dead);
-
-        EXPECT_FALSE(counts.dynamic());
-    }
-    EXPECT_EQ(3, live);
-    EXPECT_EQ(0, dead);
-
-    live = 0;
-    {
-        SmallVector<DestroyCounts, 3> counts;
-        counts.emplace_back(live, dead);
-        counts.emplace_back(live, dead);
-        counts.emplace_back(live, dead);
-        counts.emplace_back(live, dead);
-
-        EXPECT_TRUE(counts.dynamic());
-    }
-    EXPECT_EQ(4, live);
-    EXPECT_EQ(3, dead);
-
-    live = dead = 0;
-    {
-        SmallVector<DestroyCounts, 2> counts;
-        counts.emplace_back(live, dead);
-        counts.emplace_back(live, dead);
-        counts.emplace_back(live, dead);
-
-        auto copy = counts;
-        EXPECT_TRUE(copy.dynamic());
-    }
-    EXPECT_EQ(6, live);
-    EXPECT_EQ(2, dead);
-
-    live = dead = 0;
-    {
-        SmallVector<DestroyCounts, 2> counts;
-        counts.emplace_back(live, dead);
-        counts.emplace_back(live, dead);
-        counts.emplace_back(live, dead);
-
-        auto move = std::move(counts);
-        EXPECT_TRUE(move.dynamic());
-    }
-    EXPECT_EQ(3, live);
-    EXPECT_EQ(2, dead);
-
-    live = dead = 0;
-    {
-        SmallVector<DestroyCounts, 2> counts1;
-        counts1.emplace_back(live, dead);
-        counts1.emplace_back(live, dead);
-        counts1.emplace_back(live, dead);
-
-        EXPECT_TRUE(counts1.dynamic());
-        EXPECT_EQ(2, dead);
-        dead = 0;
-
-        SmallVector<DestroyCounts, 2> counts2;
-        counts2.emplace_back(live, dead);
-
-        EXPECT_FALSE(counts2.dynamic());
-
-        swap(counts1, counts2);
-
-        EXPECT_FALSE(counts1.dynamic());
-        EXPECT_TRUE(counts2.dynamic());
-
-        EXPECT_EQ(0, live);
-        EXPECT_EQ(1, dead);
-
-        dead = 0;
-    }
-    EXPECT_EQ(4, live);
-    EXPECT_EQ(0, dead);
-}
-
-} // namespace android::test
diff --git a/libs/ftl/StaticVector_test.cpp b/libs/ftl/StaticVector_test.cpp
deleted file mode 100644
index db42d23..0000000
--- a/libs/ftl/StaticVector_test.cpp
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * 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 <ftl/StaticVector.h>
-#include <gtest/gtest.h>
-
-#include <algorithm>
-#include <iterator>
-#include <string>
-#include <utility>
-
-using namespace std::string_literals;
-
-namespace android::test {
-
-using ftl::StaticVector;
-
-// Keep in sync with example usage in header file.
-TEST(StaticVector, Example) {
-    ftl::StaticVector<char, 3> vector;
-    EXPECT_TRUE(vector.empty());
-
-    vector = {'a', 'b'};
-    EXPECT_EQ(vector.size(), 2u);
-
-    vector.push_back('c');
-    EXPECT_TRUE(vector.full());
-
-    EXPECT_FALSE(vector.push_back('d'));
-    EXPECT_EQ(vector.size(), 3u);
-
-    vector.unstable_erase(vector.begin());
-    EXPECT_EQ(vector, (ftl::StaticVector{'c', 'b'}));
-
-    vector.pop_back();
-    EXPECT_EQ(vector.back(), 'c');
-
-    const char array[] = "hi";
-    vector = ftl::StaticVector(array);
-    EXPECT_EQ(vector, (ftl::StaticVector{'h', 'i', '\0'}));
-
-    ftl::StaticVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?');
-    ASSERT_EQ(strings.size(), 3u);
-
-    EXPECT_EQ(strings[0], "abc");
-    EXPECT_EQ(strings[1], "123");
-    EXPECT_EQ(strings[2], "???");
-}
-
-TEST(StaticVector, Construct) {
-    {
-        // Default constructor.
-        StaticVector<std::string, 2> vector;
-        EXPECT_TRUE(vector.empty());
-    }
-    {
-        // Array constructor.
-        const float kFloats[] = {.1f, .2f, .3f};
-        StaticVector vector(kFloats);
-        EXPECT_EQ(vector, (StaticVector{.1f, .2f, .3f}));
-    }
-    {
-        // Iterator constructor.
-        const char chars[] = "abcdef";
-        std::string string(chars);
-        StaticVector<char, sizeof(chars)> vector(string.begin(), string.end());
-
-        EXPECT_STREQ(vector.begin(), chars);
-    }
-    {
-        // Variadic constructor with same types.
-        StaticVector vector = {1, 2, 3};
-
-        static_assert(std::is_same_v<decltype(vector), StaticVector<int, 3>>);
-        EXPECT_EQ(vector, (StaticVector{1, 2, 3}));
-    }
-    {
-        // Variadic constructor with different types.
-        const auto copy = "quince"s;
-        auto move = "tart"s;
-        StaticVector vector = {copy, std::move(move)};
-
-        static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 2>>);
-        EXPECT_EQ(vector, (StaticVector{"quince"s, "tart"s}));
-    }
-    {
-        // In-place constructor with same types.
-        StaticVector vector =
-                ftl::init::list<std::string>("redolent", 3u)("velveteen", 6u)("cakewalk", 4u);
-
-        static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
-        EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
-    }
-    {
-        // In-place constructor with different types.
-        const auto copy = "red"s;
-        auto move = "velvet"s;
-        std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
-        StaticVector vector = ftl::init::list<std::string>(copy.c_str())(std::move(move))(list);
-
-        static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
-        EXPECT_TRUE(move.empty());
-        EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
-    }
-    {
-        struct String {
-            explicit String(const char* str) : str(str) {}
-            explicit String(const char** ptr) : str(*ptr) {}
-            const char* str;
-        };
-
-        const char* kStrings[] = {"a", "b", "c", "d"};
-
-        {
-            // Two iterator-like elements.
-            StaticVector<String, 3> vector(kStrings, kStrings + 3);
-            ASSERT_EQ(vector.size(), 2u);
-
-            EXPECT_STREQ(vector[0].str, "a");
-            EXPECT_STREQ(vector[1].str, "d");
-        }
-        {
-            // Disambiguating iterator constructor.
-            StaticVector<String, 3> vector(ftl::IteratorRange, kStrings, kStrings + 3);
-            ASSERT_EQ(vector.size(), 3u);
-
-            EXPECT_STREQ(vector[0].str, "a");
-            EXPECT_STREQ(vector[1].str, "b");
-            EXPECT_STREQ(vector[2].str, "c");
-        }
-    }
-}
-
-TEST(StaticVector, String) {
-    StaticVector<char, 10> chars;
-    char c = 'a';
-    std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; });
-    chars.back() = '\0';
-
-    EXPECT_STREQ(chars.begin(), "abcdefghi");
-
-    // Constructor takes iterator range.
-    const char kString[] = "123456";
-    StaticVector<char, 10> string(std::begin(kString), std::end(kString));
-
-    EXPECT_STREQ(string.begin(), "123456");
-    EXPECT_EQ(string.size(), 7u);
-
-    // Similar to emplace, but replaces rather than inserts.
-    string.replace(string.begin() + 5, '\0');
-    EXPECT_STREQ(string.begin(), "12345");
-
-    swap(chars, string);
-
-    EXPECT_STREQ(chars.begin(), "12345");
-    EXPECT_STREQ(string.begin(), "abcdefghi");
-}
-
-TEST(StaticVector, CopyableElement) {
-    struct Pair {
-        const int a, b;
-        bool operator==(Pair p) const { return p.a == a && p.b == b; }
-    };
-
-    StaticVector<Pair, 5> pairs;
-
-    EXPECT_TRUE(pairs.empty());
-    EXPECT_EQ(pairs.max_size(), 5u);
-
-    for (size_t i = 0; i < pairs.max_size(); ++i) {
-        EXPECT_EQ(pairs.size(), i);
-
-        const int a = static_cast<int>(i) * 2;
-        const auto it = pairs.emplace_back(a, a + 1);
-        ASSERT_NE(it, pairs.end());
-        EXPECT_EQ(*it, (Pair{a, a + 1}));
-    }
-
-    EXPECT_TRUE(pairs.full());
-    EXPECT_EQ(pairs.size(), 5u);
-
-    // Insertion fails if the vector is full.
-    const auto it = pairs.emplace_back(10, 11);
-    EXPECT_EQ(it, pairs.end());
-
-    EXPECT_EQ(pairs, (StaticVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9}}));
-
-    // Constructor takes at most N elements.
-    StaticVector<int, 6> sums = {0, 0, 0, 0, 0, -1};
-    EXPECT_TRUE(sums.full());
-
-    // Random-access iterators comply with standard.
-    std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; });
-    EXPECT_EQ(sums, (StaticVector{1, 5, 9, 13, 17, -1}));
-
-    sums.pop_back();
-    std::reverse(sums.begin(), sums.end());
-
-    EXPECT_EQ(sums, (StaticVector{17, 13, 9, 5, 1}));
-}
-
-TEST(StaticVector, MovableElement) {
-    // Construct std::string elements in place from per-element arguments.
-    StaticVector strings = ftl::init::list<std::string>()()()("cake")("velvet")("red")();
-    strings.pop_back();
-
-    EXPECT_EQ(strings.max_size(), 7u);
-    EXPECT_EQ(strings.size(), 6u);
-
-    // Erase "cake" and append a substring copy.
-    {
-        auto it = std::find_if(strings.begin(), strings.end(),
-                               [](const auto& s) { return !s.empty(); });
-        ASSERT_FALSE(it == strings.end());
-        EXPECT_EQ(*it, "cake");
-
-        strings.unstable_erase(it);
-
-        // Construct std::string from first 4 characters of string literal.
-        it = strings.emplace_back("cakewalk", 4u);
-        ASSERT_NE(it, strings.end());
-        EXPECT_EQ(*it, "cake"s);
-    }
-
-    strings[1] = "quince"s;
-
-    // Replace last empty string with "tart".
-    {
-        const auto rit = std::find(strings.rbegin(), strings.rend(), std::string());
-        ASSERT_FALSE(rit == strings.rend());
-
-        std::initializer_list<char> list = {'t', 'a', 'r', 't'};
-        strings.replace(rit.base() - 1, list);
-    }
-
-    strings.front().assign("pie");
-
-    EXPECT_EQ(strings, (StaticVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s}));
-}
-
-TEST(StaticVector, Replace) {
-    // Replacing does not require a copy/move assignment operator.
-    struct Word {
-        explicit Word(std::string str) : str(std::move(str)) {}
-        const std::string str;
-    };
-
-    StaticVector words = ftl::init::list<Word>("red")("velour")("cake");
-
-    // The replaced element can be referenced by the replacement.
-    const auto it = words.begin() + 1;
-    const Word& word = words.replace(it, it->str.substr(0, 3) + "vet");
-    EXPECT_EQ(word.str, "velvet");
-}
-
-TEST(StaticVector, ReverseTruncate) {
-    StaticVector<std::string, 10> strings("pie", "quince", "tart", "red", "velvet", "cake");
-    EXPECT_FALSE(strings.full());
-
-    for (auto it = strings.begin(); it != strings.end(); ++it) {
-        strings.replace(it, strings.back());
-        strings.pop_back();
-    }
-
-    EXPECT_EQ(strings, (StaticVector{"cake"s, "velvet"s, "red"s}));
-}
-
-TEST(StaticVector, Sort) {
-    StaticVector<std::string, 7> strings("pie", "quince", "tart", "red", "velvet", "cake");
-    EXPECT_FALSE(strings.full());
-
-    auto sorted = std::move(strings);
-    EXPECT_TRUE(strings.empty());
-
-    std::sort(sorted.begin(), sorted.end());
-    EXPECT_EQ(sorted, (StaticVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s}));
-
-    // Constructor takes array reference.
-    {
-        const char* kStrings[] = {"cake", "lie"};
-        strings = StaticVector(kStrings);
-    }
-
-    EXPECT_GT(sorted, strings);
-    swap(sorted, strings);
-    EXPECT_LT(sorted, strings);
-
-    // Append remaining elements, such that "pie" is the only difference.
-    for (const char* str : {"quince", "red", "tart", "velvet"}) {
-        sorted.emplace_back(str);
-    }
-
-    EXPECT_NE(sorted, strings);
-
-    // Replace second element with "pie".
-    const auto it = sorted.begin() + 1;
-    EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie");
-
-    EXPECT_EQ(sorted, strings);
-}
-
-namespace {
-
-struct DestroyCounts {
-    DestroyCounts(int& live, int& dead) : counts{live, dead} {}
-    DestroyCounts(const DestroyCounts& other) : counts(other.counts) {}
-    DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; }
-    ~DestroyCounts() { ++(alive ? counts.live : counts.dead); }
-
-    struct {
-        int& live;
-        int& dead;
-    } counts;
-
-    bool alive = true;
-};
-
-void swap(DestroyCounts& lhs, DestroyCounts& rhs) {
-    std::swap(lhs.alive, rhs.alive);
-}
-
-} // namespace
-
-TEST(StaticVector, Destroy) {
-    int live = 0;
-    int dead = 0;
-
-    { StaticVector<DestroyCounts, 5> counts; }
-    EXPECT_EQ(0, live);
-    EXPECT_EQ(0, dead);
-
-    {
-        StaticVector<DestroyCounts, 5> counts;
-        counts.emplace_back(live, dead);
-        counts.emplace_back(live, dead);
-        counts.emplace_back(live, dead);
-    }
-    EXPECT_EQ(3, live);
-    EXPECT_EQ(0, dead);
-
-    live = 0;
-    {
-        StaticVector<DestroyCounts, 5> counts;
-        counts.emplace_back(live, dead);
-        counts.emplace_back(live, dead);
-        counts.emplace_back(live, dead);
-
-        auto copy = counts;
-    }
-    EXPECT_EQ(6, live);
-    EXPECT_EQ(0, dead);
-
-    live = 0;
-    {
-        StaticVector<DestroyCounts, 5> counts;
-        counts.emplace_back(live, dead);
-        counts.emplace_back(live, dead);
-        counts.emplace_back(live, dead);
-
-        auto move = std::move(counts);
-    }
-    EXPECT_EQ(3, live);
-    EXPECT_EQ(3, dead);
-
-    live = dead = 0;
-    {
-        StaticVector<DestroyCounts, 5> counts1;
-        counts1.emplace_back(live, dead);
-        counts1.emplace_back(live, dead);
-        counts1.emplace_back(live, dead);
-
-        StaticVector<DestroyCounts, 5> counts2;
-        counts2.emplace_back(live, dead);
-
-        swap(counts1, counts2);
-
-        EXPECT_EQ(0, live);
-        EXPECT_EQ(2, dead);
-
-        dead = 0;
-    }
-    EXPECT_EQ(4, live);
-    EXPECT_EQ(0, dead);
-}
-
-} // namespace android::test
diff --git a/libs/ftl/future_test.cpp b/libs/ftl/future_test.cpp
new file mode 100644
index 0000000..9b3e936
--- /dev/null
+++ b/libs/ftl/future_test.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 <ftl/future.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <future>
+#include <string>
+#include <thread>
+#include <vector>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+TEST(Future, Example) {
+  {
+    auto future = ftl::defer([](int x) { return x + 1; }, 99);
+    EXPECT_EQ(future.get(), 100);
+  }
+  {
+    auto future = ftl::yield(42);
+    EXPECT_EQ(future.get(), 42);
+  }
+  {
+    auto ptr = std::make_unique<char>('!');
+    auto future = ftl::yield(std::move(ptr));
+    EXPECT_EQ(*future.get(), '!');
+  }
+  {
+    auto future = ftl::yield(123);
+    std::future<char> futures[] = {ftl::yield('a'), ftl::yield('b')};
+
+    std::future<char> chain = ftl::chain(std::move(future))
+                                  .then([](int x) { return static_cast<size_t>(x % 2); })
+                                  .then([&futures](size_t i) { return std::move(futures[i]); });
+
+    EXPECT_EQ(chain.get(), 'b');
+  }
+}
+
+namespace {
+
+using ByteVector = std::vector<uint8_t>;
+
+ByteVector decrement(ByteVector bytes) {
+  std::transform(bytes.begin(), bytes.end(), bytes.begin(), [](auto b) { return b - 1; });
+  return bytes;
+}
+
+}  // namespace
+
+TEST(Future, Chain) {
+  std::packaged_task<const char*()> fetch_string([] { return "ifmmp-"; });
+
+  std::packaged_task<ByteVector(std::string)> append_string([](std::string str) {
+    str += "!xpsme";
+    return ByteVector{str.begin(), str.end()};
+  });
+
+  std::packaged_task<std::future<ByteVector>(ByteVector)> decrement_bytes(
+      [](ByteVector bytes) { return ftl::defer(decrement, std::move(bytes)); });
+
+  auto fetch = fetch_string.get_future();
+  std::thread fetch_thread(std::move(fetch_string));
+
+  std::thread append_thread, decrement_thread;
+
+  EXPECT_EQ(
+      "hello, world",
+      ftl::chain(std::move(fetch))
+          .then([](const char* str) { return std::string(str); })
+          .then([&](std::string str) {
+            auto append = append_string.get_future();
+            append_thread = std::thread(std::move(append_string), std::move(str));
+            return append;
+          })
+          .then([&](ByteVector bytes) {
+            auto decrement = decrement_bytes.get_future();
+            decrement_thread = std::thread(std::move(decrement_bytes), std::move(bytes));
+            return decrement;
+          })
+          .then([](std::future<ByteVector> bytes) { return bytes; })
+          .then([](const ByteVector& bytes) { return std::string(bytes.begin(), bytes.end()); })
+          .get());
+
+  fetch_thread.join();
+  append_thread.join();
+  decrement_thread.join();
+}
+
+}  // namespace android::test
diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp
new file mode 100644
index 0000000..323b9f9
--- /dev/null
+++ b/libs/ftl/small_map_test.cpp
@@ -0,0 +1,131 @@
+/*
+ * 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 <ftl/small_map.h>
+#include <gtest/gtest.h>
+
+#include <cctype>
+
+namespace android::test {
+
+using ftl::SmallMap;
+
+// Keep in sync with example usage in header file.
+TEST(SmallMap, Example) {
+  ftl::SmallMap<int, std::string, 3> map;
+  EXPECT_TRUE(map.empty());
+  EXPECT_FALSE(map.dynamic());
+
+  map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+  EXPECT_EQ(map.size(), 3u);
+  EXPECT_FALSE(map.dynamic());
+
+  EXPECT_TRUE(map.contains(123));
+
+  EXPECT_EQ(map.find(42, [](const std::string& s) { return s.size(); }), 3u);
+
+  const auto opt = map.find(-1);
+  ASSERT_TRUE(opt);
+
+  std::string& ref = *opt;
+  EXPECT_TRUE(ref.empty());
+  ref = "xyz";
+
+  EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
+}
+
+TEST(SmallMap, Construct) {
+  {
+    // Default constructor.
+    SmallMap<int, std::string, 2> map;
+
+    EXPECT_TRUE(map.empty());
+    EXPECT_FALSE(map.dynamic());
+  }
+  {
+    // In-place constructor with same types.
+    SmallMap<int, std::string, 5> map =
+        ftl::init::map<int, std::string>(123, "abc")(456, "def")(789, "ghi");
+
+    EXPECT_EQ(map.size(), 3u);
+    EXPECT_EQ(map.max_size(), 5u);
+    EXPECT_FALSE(map.dynamic());
+
+    EXPECT_EQ(map, SmallMap(ftl::init::map(123, "abc")(456, "def")(789, "ghi")));
+  }
+  {
+    // In-place constructor with different types.
+    SmallMap<int, std::string, 5> map =
+        ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+
+    EXPECT_EQ(map.size(), 3u);
+    EXPECT_EQ(map.max_size(), 5u);
+    EXPECT_FALSE(map.dynamic());
+
+    EXPECT_EQ(map, SmallMap(ftl::init::map(42, "???")(123, "abc")(-1, "\0\0\0")));
+  }
+  {
+    // In-place constructor with implicit size.
+    SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
+
+    static_assert(std::is_same_v<decltype(map), SmallMap<int, std::string, 3>>);
+    EXPECT_EQ(map.size(), 3u);
+    EXPECT_EQ(map.max_size(), 3u);
+    EXPECT_FALSE(map.dynamic());
+
+    EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "\0\0\0")(42, "???")(123, "abc")));
+  }
+}
+
+TEST(SmallMap, Find) {
+  {
+    // Constant reference.
+    const ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+
+    const auto opt = map.find('b');
+    EXPECT_EQ(opt, 'B');
+
+    const char d = 'D';
+    const auto ref = map.find('d').value_or(std::cref(d));
+    EXPECT_EQ(ref.get(), 'D');
+  }
+  {
+    // Mutable reference.
+    ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+
+    const auto opt = map.find('c');
+    EXPECT_EQ(opt, 'C');
+
+    char d = 'd';
+    const auto ref = map.find('d').value_or(std::ref(d));
+    ref.get() = 'D';
+    EXPECT_EQ(d, 'D');
+  }
+  {
+    // Constant unary operation.
+    const ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+    EXPECT_EQ(map.find('c', [](char c) { return std::toupper(c); }), 'Z');
+  }
+  {
+    // Mutable unary operation.
+    ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+    EXPECT_TRUE(map.find('c', [](char& c) { c = std::toupper(c); }));
+
+    EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x')));
+  }
+}
+
+}  // namespace android::test
diff --git a/libs/ftl/small_vector_test.cpp b/libs/ftl/small_vector_test.cpp
new file mode 100644
index 0000000..3a03e69
--- /dev/null
+++ b/libs/ftl/small_vector_test.cpp
@@ -0,0 +1,463 @@
+/*
+ * 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 <ftl/small_vector.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+
+using namespace std::string_literals;
+
+namespace android::test {
+
+using ftl::SmallVector;
+
+// Keep in sync with example usage in header file.
+TEST(SmallVector, Example) {
+  ftl::SmallVector<char, 3> vector;
+  EXPECT_TRUE(vector.empty());
+  EXPECT_FALSE(vector.dynamic());
+
+  vector = {'a', 'b', 'c'};
+  EXPECT_EQ(vector.size(), 3u);
+  EXPECT_FALSE(vector.dynamic());
+
+  vector.push_back('d');
+  EXPECT_TRUE(vector.dynamic());
+
+  vector.unstable_erase(vector.begin());
+  EXPECT_EQ(vector, (ftl::SmallVector{'d', 'b', 'c'}));
+
+  vector.pop_back();
+  EXPECT_EQ(vector.back(), 'b');
+  EXPECT_TRUE(vector.dynamic());
+
+  const char array[] = "hi";
+  vector = ftl::SmallVector(array);
+  EXPECT_EQ(vector, (ftl::SmallVector{'h', 'i', '\0'}));
+  EXPECT_FALSE(vector.dynamic());
+
+  ftl::SmallVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?');
+  ASSERT_EQ(strings.size(), 3u);
+  EXPECT_FALSE(strings.dynamic());
+
+  EXPECT_EQ(strings[0], "abc");
+  EXPECT_EQ(strings[1], "123");
+  EXPECT_EQ(strings[2], "???");
+}
+
+TEST(SmallVector, Construct) {
+  {
+    // Default constructor.
+    SmallVector<std::string, 2> vector;
+
+    EXPECT_TRUE(vector.empty());
+    EXPECT_FALSE(vector.dynamic());
+  }
+  {
+    // Array constructor.
+    const float floats[] = {.1f, .2f, .3f};
+    SmallVector vector(floats);
+
+    EXPECT_EQ(vector, (SmallVector{.1f, .2f, .3f}));
+    EXPECT_FALSE(vector.dynamic());
+  }
+  {
+    // Iterator constructor.
+    const char chars[] = "abcdef";
+    std::string string(chars);
+    SmallVector<char, sizeof(chars)> vector(string.begin(), string.end());
+
+    EXPECT_STREQ(vector.begin(), chars);
+    EXPECT_FALSE(vector.dynamic());
+  }
+  {
+    // Variadic constructor with same types.
+    SmallVector vector = {1, 2, 3};
+
+    static_assert(std::is_same_v<decltype(vector), SmallVector<int, 3>>);
+    EXPECT_EQ(vector, (SmallVector{1, 2, 3}));
+    EXPECT_FALSE(vector.dynamic());
+  }
+  {
+    // Variadic constructor with different types.
+    const auto copy = "quince"s;
+    auto move = "tart"s;
+    SmallVector vector = {copy, std::move(move)};
+
+    static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 2>>);
+    EXPECT_EQ(vector, (SmallVector{"quince"s, "tart"s}));
+    EXPECT_FALSE(vector.dynamic());
+  }
+  {
+    // In-place constructor with same types.
+    SmallVector vector =
+        ftl::init::list<std::string>("redolent", 3u)("velveteen", 6u)("cakewalk", 4u);
+
+    static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
+    EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
+    EXPECT_FALSE(vector.dynamic());
+  }
+  {
+    // In-place constructor with different types.
+    const auto copy = "red"s;
+    auto move = "velvet"s;
+    std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
+    SmallVector vector = ftl::init::list<std::string>(copy.c_str())(std::move(move))(list);
+
+    static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
+    EXPECT_TRUE(move.empty());
+    EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
+    EXPECT_FALSE(vector.dynamic());
+  }
+  {
+    // Conversion from StaticVector.
+    ftl::StaticVector doubles = {.1, .2, .3};
+    SmallVector vector = std::move(doubles);
+    EXPECT_TRUE(doubles.empty());
+
+    static_assert(std::is_same_v<decltype(vector), SmallVector<double, 3>>);
+    EXPECT_EQ(vector, (SmallVector{.1, .2, .3}));
+    EXPECT_FALSE(vector.dynamic());
+  }
+}
+
+TEST(SmallVector, String) {
+  SmallVector<char, 10> chars;
+  char c = 'a';
+  std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; });
+  chars.push_back('\0');
+
+  EXPECT_TRUE(chars.dynamic());
+  EXPECT_EQ(chars.size(), 11u);
+  EXPECT_STREQ(chars.begin(), "abcdefghij");
+
+  // Constructor takes iterator range.
+  const char numbers[] = "123456";
+  SmallVector<char, 10> string(std::begin(numbers), std::end(numbers));
+
+  EXPECT_FALSE(string.dynamic());
+  EXPECT_STREQ(string.begin(), "123456");
+  EXPECT_EQ(string.size(), 7u);
+
+  // Similar to emplace, but replaces rather than inserts.
+  string.replace(string.begin() + 5, '\0');
+  EXPECT_STREQ(string.begin(), "12345");
+
+  swap(chars, string);
+
+  EXPECT_STREQ(chars.begin(), "12345");
+  EXPECT_STREQ(string.begin(), "abcdefghij");
+
+  EXPECT_FALSE(chars.dynamic());
+  EXPECT_TRUE(string.dynamic());
+}
+
+TEST(SmallVector, CopyableElement) {
+  struct Pair {
+    // Needed because std::vector does not use list initialization to emplace.
+    Pair(int a, int b) : a(a), b(b) {}
+
+    const int a, b;
+    bool operator==(Pair p) const { return p.a == a && p.b == b; }
+  };
+
+  SmallVector<Pair, 5> pairs;
+
+  EXPECT_TRUE(pairs.empty());
+  EXPECT_EQ(pairs.max_size(), 5u);
+
+  for (size_t i = 0; i < pairs.max_size(); ++i) {
+    EXPECT_EQ(pairs.size(), i);
+
+    const int a = static_cast<int>(i) * 2;
+    EXPECT_EQ(pairs.emplace_back(a, a + 1), Pair(a, a + 1));
+  }
+
+  EXPECT_EQ(pairs.size(), 5u);
+  EXPECT_FALSE(pairs.dynamic());
+
+  // The vector is promoted when full.
+  EXPECT_EQ(pairs.emplace_back(10, 11), Pair(10, 11));
+  EXPECT_TRUE(pairs.dynamic());
+
+  EXPECT_EQ(pairs, (SmallVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9},
+                                Pair{10, 11}}));
+
+  // Constructor takes at most N elements.
+  SmallVector<int, 6> sums = {0, 0, 0, 0, 0, 0};
+  EXPECT_FALSE(sums.dynamic());
+
+  // Random-access iterators comply with standard.
+  std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; });
+  EXPECT_EQ(sums, (SmallVector{1, 5, 9, 13, 17, 21}));
+
+  sums.pop_back();
+  std::reverse(sums.begin(), sums.end());
+
+  EXPECT_EQ(sums, (SmallVector{17, 13, 9, 5, 1}));
+}
+
+TEST(SmallVector, MovableElement) {
+  // Construct std::string elements in place from per-element arguments.
+  SmallVector strings = ftl::init::list<std::string>()()()("cake")("velvet")("red")();
+  strings.pop_back();
+
+  EXPECT_EQ(strings.max_size(), 7u);
+  EXPECT_EQ(strings.size(), 6u);
+
+  // Erase "cake" and append a substring copy.
+  {
+    const auto it =
+        std::find_if(strings.begin(), strings.end(), [](const auto& s) { return !s.empty(); });
+    ASSERT_FALSE(it == strings.end());
+    EXPECT_EQ(*it, "cake");
+
+    // Construct std::string from first 4 characters of string literal.
+    strings.unstable_erase(it);
+    EXPECT_EQ(strings.emplace_back("cakewalk", 4u), "cake"s);
+  }
+
+  strings[1] = "quince"s;
+
+  // Replace last empty string with "tart".
+  {
+    const auto rit = std::find(strings.rbegin(), strings.rend(), std::string());
+    ASSERT_FALSE(rit == strings.rend());
+
+    std::initializer_list<char> list = {'t', 'a', 'r', 't'};
+    strings.replace(rit.base() - 1, list);
+  }
+
+  strings.front().assign("pie");
+
+  EXPECT_EQ(strings, (SmallVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s}));
+
+  strings.push_back("nougat");
+  strings.push_back("oreo");
+  EXPECT_TRUE(strings.dynamic());
+
+  std::rotate(strings.begin(), strings.end() - 2, strings.end());
+
+  EXPECT_EQ(strings, (SmallVector{"nougat"s, "oreo"s, "pie"s, "quince"s, "tart"s, "red"s, "velvet"s,
+                                  "cake"s}));
+}
+
+TEST(SmallVector, Replace) {
+  // Replacing does not require a copy/move assignment operator.
+  struct Word {
+    explicit Word(std::string str) : str(std::move(str)) {}
+    const std::string str;
+
+    bool operator==(const Word& other) const { return other.str == str; }
+  };
+
+  SmallVector words = ftl::init::list<Word>("colored")("velour");
+
+  // The replaced element can be referenced by the replacement.
+  {
+    const Word& word = words.replace(words.last(), words.back().str.substr(0, 3) + "vet");
+    EXPECT_EQ(word, Word("velvet"));
+  }
+
+  // The vector is not promoted if replacing while full.
+  EXPECT_FALSE(words.dynamic());
+
+  words.emplace_back("cake");
+  EXPECT_TRUE(words.dynamic());
+
+  {
+    const Word& word = words.replace(words.begin(), words.front().str.substr(4));
+    EXPECT_EQ(word, Word("red"));
+  }
+
+  EXPECT_EQ(words, (SmallVector{Word("red"), Word("velvet"), Word("cake")}));
+}
+
+TEST(SmallVector, ReverseAppend) {
+  SmallVector strings = {"red"s, "velvet"s, "cake"s};
+  EXPECT_FALSE(strings.dynamic());
+
+  auto rit = strings.rbegin();
+  while (rit != strings.rend()) {
+    // Iterator and reference are invalidated on insertion.
+    const auto i = std::distance(strings.begin(), rit.base());
+    std::string s = *rit;
+
+    strings.push_back(std::move(s));
+    rit = std::make_reverse_iterator(strings.begin() + i) + 1;
+  }
+
+  EXPECT_EQ(strings, (SmallVector{"red"s, "velvet"s, "cake"s, "cake"s, "velvet"s, "red"s}));
+  EXPECT_TRUE(strings.dynamic());
+}
+
+TEST(SmallVector, Sort) {
+  SmallVector strings = ftl::init::list<std::string>("pie")("quince")("tart")("red")("velvet");
+  strings.push_back("cake"s);
+
+  auto sorted = std::move(strings);
+  EXPECT_TRUE(strings.empty());
+
+  EXPECT_TRUE(sorted.dynamic());
+  EXPECT_TRUE(strings.dynamic());
+
+  std::sort(sorted.begin(), sorted.end());
+  EXPECT_EQ(sorted, (SmallVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s}));
+
+  // Constructor takes array reference.
+  {
+    const char* array[] = {"cake", "lie"};
+    strings = SmallVector(array);
+    EXPECT_FALSE(strings.dynamic());
+  }
+
+  EXPECT_GT(sorted, strings);
+  swap(sorted, strings);
+  EXPECT_LT(sorted, strings);
+
+  EXPECT_FALSE(sorted.dynamic());
+  EXPECT_TRUE(strings.dynamic());
+
+  // Append remaining elements, such that "pie" is the only difference.
+  for (const char* str : {"quince", "red", "tart", "velvet"}) {
+    sorted.emplace_back(str);
+  }
+  EXPECT_TRUE(sorted.dynamic());
+
+  EXPECT_NE(sorted, strings);
+
+  // Replace second element with "pie".
+  const auto it = sorted.begin() + 1;
+  EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie");
+
+  EXPECT_EQ(sorted, strings);
+}
+
+namespace {
+
+struct DestroyCounts {
+  DestroyCounts(int& live, int& dead) : counts{live, dead} {}
+  DestroyCounts(const DestroyCounts& other) : counts(other.counts) {}
+  DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; }
+  ~DestroyCounts() { ++(alive ? counts.live : counts.dead); }
+
+  struct {
+    int& live;
+    int& dead;
+  } counts;
+
+  bool alive = true;
+};
+
+void swap(DestroyCounts& lhs, DestroyCounts& rhs) {
+  std::swap(lhs.alive, rhs.alive);
+}
+
+}  // namespace
+
+TEST(SmallVector, Destroy) {
+  int live = 0;
+  int dead = 0;
+
+  { SmallVector<DestroyCounts, 3> counts; }
+  EXPECT_EQ(0, live);
+  EXPECT_EQ(0, dead);
+
+  {
+    SmallVector<DestroyCounts, 3> counts;
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+
+    EXPECT_FALSE(counts.dynamic());
+  }
+  EXPECT_EQ(3, live);
+  EXPECT_EQ(0, dead);
+
+  live = 0;
+  {
+    SmallVector<DestroyCounts, 3> counts;
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+
+    EXPECT_TRUE(counts.dynamic());
+  }
+  EXPECT_EQ(4, live);
+  EXPECT_EQ(3, dead);
+
+  live = dead = 0;
+  {
+    SmallVector<DestroyCounts, 2> counts;
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+
+    auto copy = counts;
+    EXPECT_TRUE(copy.dynamic());
+  }
+  EXPECT_EQ(6, live);
+  EXPECT_EQ(2, dead);
+
+  live = dead = 0;
+  {
+    SmallVector<DestroyCounts, 2> counts;
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+
+    auto move = std::move(counts);
+    EXPECT_TRUE(move.dynamic());
+  }
+  EXPECT_EQ(3, live);
+  EXPECT_EQ(2, dead);
+
+  live = dead = 0;
+  {
+    SmallVector<DestroyCounts, 2> counts1;
+    counts1.emplace_back(live, dead);
+    counts1.emplace_back(live, dead);
+    counts1.emplace_back(live, dead);
+
+    EXPECT_TRUE(counts1.dynamic());
+    EXPECT_EQ(2, dead);
+    dead = 0;
+
+    SmallVector<DestroyCounts, 2> counts2;
+    counts2.emplace_back(live, dead);
+
+    EXPECT_FALSE(counts2.dynamic());
+
+    swap(counts1, counts2);
+
+    EXPECT_FALSE(counts1.dynamic());
+    EXPECT_TRUE(counts2.dynamic());
+
+    EXPECT_EQ(0, live);
+    EXPECT_EQ(1, dead);
+
+    dead = 0;
+  }
+  EXPECT_EQ(4, live);
+  EXPECT_EQ(0, dead);
+}
+
+}  // namespace android::test
diff --git a/libs/ftl/static_vector_test.cpp b/libs/ftl/static_vector_test.cpp
new file mode 100644
index 0000000..cbe8dff
--- /dev/null
+++ b/libs/ftl/static_vector_test.cpp
@@ -0,0 +1,399 @@
+/*
+ * 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 <ftl/static_vector.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+
+using namespace std::string_literals;
+
+namespace android::test {
+
+using ftl::StaticVector;
+
+// Keep in sync with example usage in header file.
+TEST(StaticVector, Example) {
+  ftl::StaticVector<char, 3> vector;
+  EXPECT_TRUE(vector.empty());
+
+  vector = {'a', 'b'};
+  EXPECT_EQ(vector.size(), 2u);
+
+  vector.push_back('c');
+  EXPECT_TRUE(vector.full());
+
+  EXPECT_FALSE(vector.push_back('d'));
+  EXPECT_EQ(vector.size(), 3u);
+
+  vector.unstable_erase(vector.begin());
+  EXPECT_EQ(vector, (ftl::StaticVector{'c', 'b'}));
+
+  vector.pop_back();
+  EXPECT_EQ(vector.back(), 'c');
+
+  const char array[] = "hi";
+  vector = ftl::StaticVector(array);
+  EXPECT_EQ(vector, (ftl::StaticVector{'h', 'i', '\0'}));
+
+  ftl::StaticVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?');
+  ASSERT_EQ(strings.size(), 3u);
+
+  EXPECT_EQ(strings[0], "abc");
+  EXPECT_EQ(strings[1], "123");
+  EXPECT_EQ(strings[2], "???");
+}
+
+TEST(StaticVector, Construct) {
+  {
+    // Default constructor.
+    StaticVector<std::string, 2> vector;
+    EXPECT_TRUE(vector.empty());
+  }
+  {
+    // Array constructor.
+    const float floats[] = {.1f, .2f, .3f};
+    StaticVector vector(floats);
+    EXPECT_EQ(vector, (StaticVector{.1f, .2f, .3f}));
+  }
+  {
+    // Iterator constructor.
+    const char chars[] = "abcdef";
+    std::string string(chars);
+    StaticVector<char, sizeof(chars)> vector(string.begin(), string.end());
+
+    EXPECT_STREQ(vector.begin(), chars);
+  }
+  {
+    // Variadic constructor with same types.
+    StaticVector vector = {1, 2, 3};
+
+    static_assert(std::is_same_v<decltype(vector), StaticVector<int, 3>>);
+    EXPECT_EQ(vector, (StaticVector{1, 2, 3}));
+  }
+  {
+    // Variadic constructor with different types.
+    const auto copy = "quince"s;
+    auto move = "tart"s;
+    StaticVector vector = {copy, std::move(move)};
+
+    static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 2>>);
+    EXPECT_EQ(vector, (StaticVector{"quince"s, "tart"s}));
+  }
+  {
+    // In-place constructor with same types.
+    StaticVector vector =
+        ftl::init::list<std::string>("redolent", 3u)("velveteen", 6u)("cakewalk", 4u);
+
+    static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
+    EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
+  }
+  {
+    // In-place constructor with different types.
+    const auto copy = "red"s;
+    auto move = "velvet"s;
+    std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
+    StaticVector vector = ftl::init::list<std::string>(copy.c_str())(std::move(move))(list);
+
+    static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
+    EXPECT_TRUE(move.empty());
+    EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
+  }
+  {
+    struct String {
+      explicit String(const char* str) : str(str) {}
+      explicit String(const char** ptr) : str(*ptr) {}
+      const char* str;
+    };
+
+    const char* strings[] = {"a", "b", "c", "d"};
+
+    {
+      // Two iterator-like elements.
+      StaticVector<String, 3> vector(strings, strings + 3);
+      ASSERT_EQ(vector.size(), 2u);
+
+      EXPECT_STREQ(vector[0].str, "a");
+      EXPECT_STREQ(vector[1].str, "d");
+    }
+    {
+      // Disambiguating iterator constructor.
+      StaticVector<String, 3> vector(ftl::kIteratorRange, strings, strings + 3);
+      ASSERT_EQ(vector.size(), 3u);
+
+      EXPECT_STREQ(vector[0].str, "a");
+      EXPECT_STREQ(vector[1].str, "b");
+      EXPECT_STREQ(vector[2].str, "c");
+    }
+  }
+}
+
+TEST(StaticVector, String) {
+  StaticVector<char, 10> chars;
+  char c = 'a';
+  std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; });
+  chars.back() = '\0';
+
+  EXPECT_STREQ(chars.begin(), "abcdefghi");
+
+  // Constructor takes iterator range.
+  const char numbers[] = "123456";
+  StaticVector<char, 10> string(std::begin(numbers), std::end(numbers));
+
+  EXPECT_STREQ(string.begin(), "123456");
+  EXPECT_EQ(string.size(), 7u);
+
+  // Similar to emplace, but replaces rather than inserts.
+  string.replace(string.begin() + 5, '\0');
+  EXPECT_STREQ(string.begin(), "12345");
+
+  swap(chars, string);
+
+  EXPECT_STREQ(chars.begin(), "12345");
+  EXPECT_STREQ(string.begin(), "abcdefghi");
+}
+
+TEST(StaticVector, CopyableElement) {
+  struct Pair {
+    const int a, b;
+    bool operator==(Pair p) const { return p.a == a && p.b == b; }
+  };
+
+  StaticVector<Pair, 5> pairs;
+
+  EXPECT_TRUE(pairs.empty());
+  EXPECT_EQ(pairs.max_size(), 5u);
+
+  for (size_t i = 0; i < pairs.max_size(); ++i) {
+    EXPECT_EQ(pairs.size(), i);
+
+    const int a = static_cast<int>(i) * 2;
+    const auto it = pairs.emplace_back(a, a + 1);
+    ASSERT_NE(it, pairs.end());
+    EXPECT_EQ(*it, (Pair{a, a + 1}));
+  }
+
+  EXPECT_TRUE(pairs.full());
+  EXPECT_EQ(pairs.size(), 5u);
+
+  // Insertion fails if the vector is full.
+  const auto it = pairs.emplace_back(10, 11);
+  EXPECT_EQ(it, pairs.end());
+
+  EXPECT_EQ(pairs, (StaticVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9}}));
+
+  // Constructor takes at most N elements.
+  StaticVector<int, 6> sums = {0, 0, 0, 0, 0, -1};
+  EXPECT_TRUE(sums.full());
+
+  // Random-access iterators comply with standard.
+  std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; });
+  EXPECT_EQ(sums, (StaticVector{1, 5, 9, 13, 17, -1}));
+
+  sums.pop_back();
+  std::reverse(sums.begin(), sums.end());
+
+  EXPECT_EQ(sums, (StaticVector{17, 13, 9, 5, 1}));
+}
+
+TEST(StaticVector, MovableElement) {
+  // Construct std::string elements in place from per-element arguments.
+  StaticVector strings = ftl::init::list<std::string>()()()("cake")("velvet")("red")();
+  strings.pop_back();
+
+  EXPECT_EQ(strings.max_size(), 7u);
+  EXPECT_EQ(strings.size(), 6u);
+
+  // Erase "cake" and append a substring copy.
+  {
+    auto it =
+        std::find_if(strings.begin(), strings.end(), [](const auto& s) { return !s.empty(); });
+    ASSERT_FALSE(it == strings.end());
+    EXPECT_EQ(*it, "cake");
+
+    strings.unstable_erase(it);
+
+    // Construct std::string from first 4 characters of string literal.
+    it = strings.emplace_back("cakewalk", 4u);
+    ASSERT_NE(it, strings.end());
+    EXPECT_EQ(*it, "cake"s);
+  }
+
+  strings[1] = "quince"s;
+
+  // Replace last empty string with "tart".
+  {
+    const auto rit = std::find(strings.rbegin(), strings.rend(), std::string());
+    ASSERT_FALSE(rit == strings.rend());
+
+    std::initializer_list<char> list = {'t', 'a', 'r', 't'};
+    strings.replace(rit.base() - 1, list);
+  }
+
+  strings.front().assign("pie");
+
+  EXPECT_EQ(strings, (StaticVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s}));
+}
+
+TEST(StaticVector, Replace) {
+  // Replacing does not require a copy/move assignment operator.
+  struct Word {
+    explicit Word(std::string str) : str(std::move(str)) {}
+    const std::string str;
+  };
+
+  StaticVector words = ftl::init::list<Word>("red")("velour")("cake");
+
+  // The replaced element can be referenced by the replacement.
+  const auto it = words.begin() + 1;
+  const Word& word = words.replace(it, it->str.substr(0, 3) + "vet");
+  EXPECT_EQ(word.str, "velvet");
+}
+
+TEST(StaticVector, ReverseTruncate) {
+  StaticVector<std::string, 10> strings("pie", "quince", "tart", "red", "velvet", "cake");
+  EXPECT_FALSE(strings.full());
+
+  for (auto it = strings.begin(); it != strings.end(); ++it) {
+    strings.replace(it, strings.back());
+    strings.pop_back();
+  }
+
+  EXPECT_EQ(strings, (StaticVector{"cake"s, "velvet"s, "red"s}));
+}
+
+TEST(StaticVector, Sort) {
+  StaticVector<std::string, 7> strings("pie", "quince", "tart", "red", "velvet", "cake");
+  EXPECT_FALSE(strings.full());
+
+  auto sorted = std::move(strings);
+  EXPECT_TRUE(strings.empty());
+
+  std::sort(sorted.begin(), sorted.end());
+  EXPECT_EQ(sorted, (StaticVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s}));
+
+  // Constructor takes array reference.
+  {
+    const char* array[] = {"cake", "lie"};
+    strings = StaticVector(array);
+  }
+
+  EXPECT_GT(sorted, strings);
+  swap(sorted, strings);
+  EXPECT_LT(sorted, strings);
+
+  // Append remaining elements, such that "pie" is the only difference.
+  for (const char* str : {"quince", "red", "tart", "velvet"}) {
+    sorted.emplace_back(str);
+  }
+
+  EXPECT_NE(sorted, strings);
+
+  // Replace second element with "pie".
+  const auto it = sorted.begin() + 1;
+  EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie");
+
+  EXPECT_EQ(sorted, strings);
+}
+
+namespace {
+
+struct DestroyCounts {
+  DestroyCounts(int& live, int& dead) : counts{live, dead} {}
+  DestroyCounts(const DestroyCounts& other) : counts(other.counts) {}
+  DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; }
+  ~DestroyCounts() { ++(alive ? counts.live : counts.dead); }
+
+  struct {
+    int& live;
+    int& dead;
+  } counts;
+
+  bool alive = true;
+};
+
+void swap(DestroyCounts& lhs, DestroyCounts& rhs) {
+  std::swap(lhs.alive, rhs.alive);
+}
+
+}  // namespace
+
+TEST(StaticVector, Destroy) {
+  int live = 0;
+  int dead = 0;
+
+  { StaticVector<DestroyCounts, 5> counts; }
+  EXPECT_EQ(0, live);
+  EXPECT_EQ(0, dead);
+
+  {
+    StaticVector<DestroyCounts, 5> counts;
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+  }
+  EXPECT_EQ(3, live);
+  EXPECT_EQ(0, dead);
+
+  live = 0;
+  {
+    StaticVector<DestroyCounts, 5> counts;
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+
+    auto copy = counts;
+  }
+  EXPECT_EQ(6, live);
+  EXPECT_EQ(0, dead);
+
+  live = 0;
+  {
+    StaticVector<DestroyCounts, 5> counts;
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+    counts.emplace_back(live, dead);
+
+    auto move = std::move(counts);
+  }
+  EXPECT_EQ(3, live);
+  EXPECT_EQ(3, dead);
+
+  live = dead = 0;
+  {
+    StaticVector<DestroyCounts, 5> counts1;
+    counts1.emplace_back(live, dead);
+    counts1.emplace_back(live, dead);
+    counts1.emplace_back(live, dead);
+
+    StaticVector<DestroyCounts, 5> counts2;
+    counts2.emplace_back(live, dead);
+
+    swap(counts1, counts2);
+
+    EXPECT_EQ(0, live);
+    EXPECT_EQ(2, dead);
+
+    dead = 0;
+  }
+  EXPECT_EQ(4, live);
+  EXPECT_EQ(0, dead);
+}
+
+}  // namespace android::test
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index af9ef06..7dd584d 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -38,9 +38,10 @@
 
 cc_library_shared {
     name: "libgui",
-    vendor_available: false,
+    vendor_available: true,
     vndk: {
         enabled: true,
+        private: true,
     },
     double_loadable: true,
 
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index bf0386f..eaa47f9 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -22,8 +22,13 @@
 
 #include <gui/BLASTBufferQueue.h>
 #include <gui/BufferItemConsumer.h>
+#include <gui/BufferQueueConsumer.h>
+#include <gui/BufferQueueCore.h>
+#include <gui/BufferQueueProducer.h>
 #include <gui/GLConsumer.h>
+#include <gui/IProducerListener.h>
 #include <gui/Surface.h>
+#include <utils/Singleton.h>
 
 #include <utils/Trace.h>
 
@@ -118,7 +123,7 @@
         mSize(width, height),
         mRequestedSize(mSize),
         mNextTransaction(nullptr) {
-    BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+    createBufferQueue(&mProducer, &mConsumer);
     // since the adapter is in the client process, set dequeue timeout
     // explicitly so that dequeueBuffer will block
     mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());
@@ -146,6 +151,19 @@
     mPendingReleaseItem.releaseFence = nullptr;
 }
 
+BLASTBufferQueue::~BLASTBufferQueue() {
+    if (mPendingTransactions.empty()) {
+        return;
+    }
+    BQA_LOGE("Applying pending transactions on dtor %d",
+             static_cast<uint32_t>(mPendingTransactions.size()));
+    SurfaceComposerClient::Transaction t;
+    for (auto& [targetFrameNumber, transaction] : mPendingTransactions) {
+        t.merge(std::move(transaction));
+    }
+    t.apply();
+}
+
 void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height) {
     std::unique_lock _lock{mMutex};
     mSurfaceControl = surface;
@@ -179,49 +197,64 @@
 
 void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
                                            const std::vector<SurfaceControlStats>& stats) {
-    std::unique_lock _lock{mMutex};
-    ATRACE_CALL();
-    BQA_LOGV("transactionCallback");
-    mInitialCallbackReceived = true;
+    std::function<void(int64_t)> transactionCompleteCallback = nullptr;
+    uint64_t currFrameNumber = 0;
 
-    if (!stats.empty()) {
-        mTransformHint = stats[0].transformHint;
-        mBufferItemConsumer->setTransformHint(mTransformHint);
-        mBufferItemConsumer->updateFrameTimestamps(stats[0].frameEventStats.frameNumber,
-                                                   stats[0].frameEventStats.refreshStartTime,
-                                                   stats[0].frameEventStats.gpuCompositionDoneFence,
-                                                   stats[0].presentFence,
-                                                   stats[0].previousReleaseFence,
-                                                   stats[0].frameEventStats.compositorTiming,
-                                                   stats[0].latchTime,
-                                                   stats[0].frameEventStats.dequeueReadyTime);
-    }
-    if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) {
+    {
+        std::unique_lock _lock{mMutex};
+        ATRACE_CALL();
+        BQA_LOGV("transactionCallback");
+        mInitialCallbackReceived = true;
+
         if (!stats.empty()) {
-            mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
-        } else {
-            BQA_LOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
+            mTransformHint = stats[0].transformHint;
+            mBufferItemConsumer->setTransformHint(mTransformHint);
+            mBufferItemConsumer
+                    ->updateFrameTimestamps(stats[0].frameEventStats.frameNumber,
+                                            stats[0].frameEventStats.refreshStartTime,
+                                            stats[0].frameEventStats.gpuCompositionDoneFence,
+                                            stats[0].presentFence, stats[0].previousReleaseFence,
+                                            stats[0].frameEventStats.compositorTiming,
+                                            stats[0].latchTime,
+                                            stats[0].frameEventStats.dequeueReadyTime);
+        }
+        if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) {
+            if (!stats.empty()) {
+                mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
+            } else {
+                BQA_LOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
+                mPendingReleaseItem.releaseFence = nullptr;
+            }
+            mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item,
+                                               mPendingReleaseItem.releaseFence
+                                                       ? mPendingReleaseItem.releaseFence
+                                                       : Fence::NO_FENCE);
+            mNumAcquired--;
+            mPendingReleaseItem.item = BufferItem();
             mPendingReleaseItem.releaseFence = nullptr;
         }
-        mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item,
-                                           mPendingReleaseItem.releaseFence
-                                                   ? mPendingReleaseItem.releaseFence
-                                                   : Fence::NO_FENCE);
-        mNumAcquired--;
-        mPendingReleaseItem.item = BufferItem();
-        mPendingReleaseItem.releaseFence = nullptr;
+
+        if (mSubmitted.empty()) {
+            BQA_LOGE("ERROR: callback with no corresponding submitted buffer item");
+        }
+        mPendingReleaseItem.item = std::move(mSubmitted.front());
+        mSubmitted.pop();
+
+        processNextBufferLocked(false);
+
+        currFrameNumber = mPendingReleaseItem.item.mFrameNumber;
+        if (mTransactionCompleteCallback && mTransactionCompleteFrameNumber == currFrameNumber) {
+            transactionCompleteCallback = std::move(mTransactionCompleteCallback);
+            mTransactionCompleteFrameNumber = 0;
+        }
+
+        mCallbackCV.notify_all();
+        decStrong((void*)transactionCallbackThunk);
     }
 
-    if (mSubmitted.empty()) {
-        BQA_LOGE("ERROR: callback with no corresponding submitted buffer item");
+    if (transactionCompleteCallback) {
+        transactionCompleteCallback(currFrameNumber);
     }
-    mPendingReleaseItem.item = std::move(mSubmitted.front());
-    mSubmitted.pop();
-
-    processNextBufferLocked(false);
-
-    mCallbackCV.notify_all();
-    decStrong((void*)transactionCallbackThunk);
 }
 
 void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
@@ -292,6 +325,7 @@
     incStrong((void*)transactionCallbackThunk);
 
     mLastBufferScalingMode = bufferItem.mScalingMode;
+    mLastAcquiredFrameNumber = bufferItem.mFrameNumber;
 
     t->setBuffer(mSurfaceControl, buffer);
     t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
@@ -306,7 +340,9 @@
     t->setCrop(mSurfaceControl, computeCrop(bufferItem));
     t->setTransform(mSurfaceControl, bufferItem.mTransform);
     t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
-    t->setDesiredPresentTime(bufferItem.mTimestamp);
+    if (!bufferItem.mIsAutoTimestamp) {
+        t->setDesiredPresentTime(bufferItem.mTimestamp);
+    }
     t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber);
 
     if (!mNextFrameTimelineVsyncIdQueue.empty()) {
@@ -319,14 +355,29 @@
         mAutoRefresh = bufferItem.mAutoRefresh;
     }
 
+    auto mergeTransaction =
+            [&t, currentFrameNumber = bufferItem.mFrameNumber](
+                    std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) {
+                auto& [targetFrameNumber, transaction] = pendingTransaction;
+                if (currentFrameNumber < targetFrameNumber) {
+                    return false;
+                }
+                t->merge(std::move(transaction));
+                return true;
+            };
+
+    mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(),
+                                              mPendingTransactions.end(), mergeTransaction),
+                               mPendingTransactions.end());
+
     if (applyTransaction) {
-        t->apply();
+        t->setApplyToken(mApplyToken).apply();
     }
 
     BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64
-             " applyTransaction=%s mTimestamp=%" PRId64,
+             " applyTransaction=%s mTimestamp=%" PRId64 " mPendingTransactions.size=%d",
              mSize.width, mSize.height, bufferItem.mFrameNumber, toString(applyTransaction),
-             bufferItem.mTimestamp);
+             bufferItem.mTimestamp, static_cast<uint32_t>(mPendingTransactions.size()));
 }
 
 Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
@@ -389,6 +440,17 @@
     return mSize != bufferSize;
 }
 
+void BLASTBufferQueue::setTransactionCompleteCallback(
+        uint64_t frameNumber, std::function<void(int64_t)>&& transactionCompleteCallback) {
+    std::lock_guard _lock{mMutex};
+    if (transactionCompleteCallback == nullptr) {
+        mTransactionCompleteCallback = nullptr;
+    } else {
+        mTransactionCompleteCallback = std::move(transactionCompleteCallback);
+        mTransactionCompleteFrameNumber = frameNumber;
+    }
+}
+
 // Check if we have acquired the maximum number of buffers.
 // As a special case, we wait for the first callback before acquiring the second buffer so we
 // can ensure the first buffer is presented if multiple buffers are queued in succession.
@@ -454,4 +516,122 @@
     return new BBQSurface(mProducer, true, scHandle, this);
 }
 
+void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transaction* t,
+                                                uint64_t frameNumber) {
+    std::lock_guard _lock{mMutex};
+    if (mLastAcquiredFrameNumber >= frameNumber) {
+        // Apply the transaction since we have already acquired the desired frame.
+        t->apply();
+    } else {
+        mPendingTransactions.emplace_back(frameNumber, std::move(*t));
+    }
+}
+
+// Maintains a single worker thread per process that services a list of runnables.
+class AsyncWorker : public Singleton<AsyncWorker> {
+private:
+    std::thread mThread;
+    bool mDone = false;
+    std::deque<std::function<void()>> mRunnables;
+    std::mutex mMutex;
+    std::condition_variable mCv;
+    void run() {
+        std::unique_lock<std::mutex> lock(mMutex);
+        while (!mDone) {
+            mCv.wait(lock);
+            while (!mRunnables.empty()) {
+                std::function<void()> runnable = mRunnables.front();
+                mRunnables.pop_front();
+                runnable();
+            }
+        }
+    }
+
+public:
+    AsyncWorker() : Singleton<AsyncWorker>() { mThread = std::thread(&AsyncWorker::run, this); }
+
+    ~AsyncWorker() {
+        mDone = true;
+        mCv.notify_all();
+        if (mThread.joinable()) {
+            mThread.join();
+        }
+    }
+
+    void post(std::function<void()> runnable) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        mRunnables.emplace_back(std::move(runnable));
+        mCv.notify_one();
+    }
+};
+ANDROID_SINGLETON_STATIC_INSTANCE(AsyncWorker);
+
+// Asynchronously calls ProducerListener functions so we can emulate one way binder calls.
+class AsyncProducerListener : public BnProducerListener {
+private:
+    const sp<IProducerListener> mListener;
+
+public:
+    AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {}
+
+    void onBufferReleased() override {
+        AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferReleased(); });
+    }
+
+    void onBuffersDiscarded(const std::vector<int32_t>& slots) override {
+        AsyncWorker::getInstance().post(
+                [listener = mListener, slots = slots]() { listener->onBuffersDiscarded(slots); });
+    }
+};
+
+// Extends the BufferQueueProducer to create a wrapper around the listener so the listener calls
+// can be non-blocking when the producer is in the client process.
+class BBQBufferQueueProducer : public BufferQueueProducer {
+public:
+    BBQBufferQueueProducer(const sp<BufferQueueCore>& core)
+          : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/) {}
+
+    status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp,
+                     QueueBufferOutput* output) override {
+        if (!listener) {
+            return BufferQueueProducer::connect(listener, api, producerControlledByApp, output);
+        }
+
+        return BufferQueueProducer::connect(new AsyncProducerListener(listener), api,
+                                            producerControlledByApp, output);
+    }
+
+    int query(int what, int* value) override {
+        if (what == NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER) {
+            *value = 1;
+            return NO_ERROR;
+        }
+        return BufferQueueProducer::query(what, value);
+    }
+};
+
+// Similar to BufferQueue::createBufferQueue but creates an adapter specific bufferqueue producer.
+// This BQP allows invoking client specified ProducerListeners and invoke them asynchronously,
+// emulating one way binder call behavior. Without this, if the listener calls back into the queue,
+// we can deadlock.
+void BLASTBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+                                         sp<IGraphicBufferConsumer>* outConsumer) {
+    LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BLASTBufferQueue: outProducer must not be NULL");
+    LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BLASTBufferQueue: outConsumer must not be NULL");
+
+    sp<BufferQueueCore> core(new BufferQueueCore());
+    LOG_ALWAYS_FATAL_IF(core == nullptr, "BLASTBufferQueue: failed to create BufferQueueCore");
+
+    sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core));
+    LOG_ALWAYS_FATAL_IF(producer == nullptr,
+                        "BLASTBufferQueue: failed to create BBQBufferQueueProducer");
+
+    sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
+    LOG_ALWAYS_FATAL_IF(consumer == nullptr,
+                        "BLASTBufferQueue: failed to create BufferQueueConsumer");
+
+    *outProducer = producer;
+    *outConsumer = consumer;
+}
+
 } // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index e46a415..a8d6832 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -71,7 +71,7 @@
     virtual status_t setTransactionState(
             int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
             const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
-            const InputWindowCommands& commands, int64_t desiredPresentTime,
+            const InputWindowCommands& commands, int64_t desiredPresentTime, bool isAutoTimestamp,
             const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
             const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
         Parcel data, reply;
@@ -92,6 +92,7 @@
         SAFE_PARCEL(data.writeStrongBinder, applyToken);
         SAFE_PARCEL(commands.write, data);
         SAFE_PARCEL(data.writeInt64, desiredPresentTime);
+        SAFE_PARCEL(data.writeBool, isAutoTimestamp);
         SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
         SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
         SAFE_PARCEL(data.writeBool, hasListenerCallbacks);
@@ -1230,6 +1231,21 @@
 
         return remote()->transact(BnSurfaceComposer::ADD_TRANSACTION_TRACE_LISTENER, data, &reply);
     }
+
+    /**
+     * Get priority of the RenderEngine in surface flinger.
+     */
+    virtual int getGPUContextPriority() {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        status_t err =
+                remote()->transact(BnSurfaceComposer::GET_GPU_CONTEXT_PRIORITY, data, &reply);
+        if (err != NO_ERROR) {
+            ALOGE("getGPUContextPriority failed to read data:  %s (%d)", strerror(-err), err);
+            return 0;
+        }
+        return reply.readInt32();
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -1282,7 +1298,9 @@
             SAFE_PARCEL(inputWindowCommands.read, data);
 
             int64_t desiredPresentTime = 0;
+            bool isAutoTimestamp = true;
             SAFE_PARCEL(data.readInt64, &desiredPresentTime);
+            SAFE_PARCEL(data.readBool, &isAutoTimestamp);
 
             client_cache_t uncachedBuffer;
             sp<IBinder> tmpBinder;
@@ -1308,8 +1326,8 @@
 
             return setTransactionState(frameTimelineVsyncId, state, displays, stateFlags,
                                        applyToken, inputWindowCommands, desiredPresentTime,
-                                       uncachedBuffer, hasListenerCallbacks, listenerCallbacks,
-                                       transactionId);
+                                       isAutoTimestamp, uncachedBuffer, hasListenerCallbacks,
+                                       listenerCallbacks, transactionId);
         }
         case BOOT_FINISHED: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -2094,6 +2112,12 @@
 
             return addTransactionTraceListener(listener);
         }
+        case GET_GPU_CONTEXT_PRIORITY: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            int priority = getGPUContextPriority();
+            SAFE_PARCEL(reply->writeInt32, priority);
+            return NO_ERROR;
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 2dacae1..1808571 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -99,15 +99,13 @@
 
 status_t JankData::writeToParcel(Parcel* output) const {
     SAFE_PARCEL(output->writeInt64, frameVsyncId);
-    SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankType));
+    SAFE_PARCEL(output->writeInt32, jankType);
     return NO_ERROR;
 }
 
 status_t JankData::readFromParcel(const Parcel* input) {
     SAFE_PARCEL(input->readInt64, &frameVsyncId);
-    int32_t jankTypeInt;
-    SAFE_PARCEL(input->readInt32, &jankTypeInt);
-    jankType = static_cast<JankType>(jankTypeInt);
+    SAFE_PARCEL(input->readInt32, &jankType);
     return NO_ERROR;
 }
 
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 94390aa..1bba5e4 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -985,6 +985,10 @@
                 }
                 break;
             case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: {
+                status_t err = mGraphicBufferProducer->query(what, value);
+                if (err == NO_ERROR) {
+                    return NO_ERROR;
+                }
                 if (composerService()->authenticateSurfaceTexture(
                         mGraphicBufferProducer)) {
                     *value = 1;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 9ed7d1c..97c2693 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -395,7 +395,9 @@
         mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd),
         mContainsBuffer(other.mContainsBuffer),
         mDesiredPresentTime(other.mDesiredPresentTime),
-        mFrameTimelineVsyncId(other.mFrameTimelineVsyncId) {
+        mIsAutoTimestamp(other.mIsAutoTimestamp),
+        mFrameTimelineVsyncId(other.mFrameTimelineVsyncId),
+        mApplyToken(other.mApplyToken) {
     mDisplayStates = other.mDisplayStates;
     mComposerStates = other.mComposerStates;
     mInputWindowCommands = other.mInputWindowCommands;
@@ -424,8 +426,10 @@
     const bool explicitEarlyWakeupEnd = parcel->readBool();
     const bool containsBuffer = parcel->readBool();
     const int64_t desiredPresentTime = parcel->readInt64();
+    const bool isAutoTimestamp = parcel->readBool();
     const int64_t frameTimelineVsyncId = parcel->readInt64();
-
+    sp<IBinder> applyToken;
+    parcel->readNullableStrongBinder(&applyToken);
     size_t count = static_cast<size_t>(parcel->readUint32());
     if (count > parcel->dataSize()) {
         return BAD_VALUE;
@@ -497,11 +501,13 @@
     mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
     mContainsBuffer = containsBuffer;
     mDesiredPresentTime = desiredPresentTime;
+    mIsAutoTimestamp = isAutoTimestamp;
     mFrameTimelineVsyncId = frameTimelineVsyncId;
     mDisplayStates = displayStates;
     mListenerCallbacks = listenerCallbacks;
     mComposerStates = composerStates;
     mInputWindowCommands = inputWindowCommands;
+    mApplyToken = applyToken;
     return NO_ERROR;
 }
 
@@ -527,7 +533,9 @@
     parcel->writeBool(mExplicitEarlyWakeupEnd);
     parcel->writeBool(mContainsBuffer);
     parcel->writeInt64(mDesiredPresentTime);
+    parcel->writeBool(mIsAutoTimestamp);
     parcel->writeInt64(mFrameTimelineVsyncId);
+    parcel->writeStrongBinder(mApplyToken);
     parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
     for (auto const& displayState : mDisplayStates) {
         displayState.write(*parcel);
@@ -603,6 +611,7 @@
     mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
     mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart;
     mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd;
+    mApplyToken = other.mApplyToken;
 
     // When merging vsync Ids we take the oldest one
     if (mFrameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID &&
@@ -628,8 +637,10 @@
     mEarlyWakeup = false;
     mExplicitEarlyWakeupStart = false;
     mExplicitEarlyWakeupEnd = false;
-    mDesiredPresentTime = -1;
+    mDesiredPresentTime = 0;
+    mIsAutoTimestamp = true;
     mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
+    mApplyToken = nullptr;
 }
 
 void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -640,8 +651,9 @@
     uncacheBuffer.id = cacheId;
 
     sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    sf->setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, {}, {}, 0, applyToken, {}, -1,
-                            uncacheBuffer, false, {}, 0 /* Undefined transactionId */);
+    sf->setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, {}, {}, 0, applyToken, {},
+                            systemTime(), true, uncacheBuffer, false, {},
+                            0 /* Undefined transactionId */);
 }
 
 void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -757,9 +769,12 @@
         flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd;
     }
 
-    sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    sp<IBinder> applyToken = mApplyToken
+            ? mApplyToken
+            : IInterface::asBinder(TransactionCompletedListener::getIInstance());
+
     sf->setTransactionState(mFrameTimelineVsyncId, composerStates, displayStates, flags, applyToken,
-                            mInputWindowCommands, mDesiredPresentTime,
+                            mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
                             {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
                             hasListenerCallbacks, listenerCallbacks, mId);
     mId = generateId();
@@ -1201,6 +1216,9 @@
     }
     s->what |= layer_state_t::eBufferChanged;
     s->buffer = buffer;
+    if (mIsAutoTimestamp) {
+        mDesiredPresentTime = systemTime();
+    }
 
     registerSurfaceControlForCallback(sc);
 
@@ -1295,6 +1313,7 @@
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime(
         nsecs_t desiredPresentTime) {
     mDesiredPresentTime = desiredPresentTime;
+    mIsAutoTimestamp = false;
     return *this;
 }
 
@@ -1569,6 +1588,12 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApplyToken(
+        const sp<IBinder>& applyToken) {
+    mApplyToken = applyToken;
+    return *this;
+}
+
 // ---------------------------------------------------------------------------
 
 DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -1988,6 +2013,10 @@
                                                                           lightRadius);
 }
 
+int SurfaceComposerClient::getGPUContextPriority() {
+    return ComposerService::getComposerService()->getGPUContextPriority();
+}
+
 // ----------------------------------------------------------------------------
 
 status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 1139390..1198135 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -82,6 +82,9 @@
     void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
             const std::vector<SurfaceControlStats>& stats);
     void setNextTransaction(SurfaceComposerClient::Transaction *t);
+    void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
+    void setTransactionCompleteCallback(uint64_t frameNumber,
+                                        std::function<void(int64_t)>&& transactionCompleteCallback);
 
     void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height);
     void flushShadowQueue() { mFlushShadowQueue = true; }
@@ -89,7 +92,7 @@
     status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
     status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId);
 
-    virtual ~BLASTBufferQueue() = default;
+    virtual ~BLASTBufferQueue();
 
 private:
     friend class BLASTBufferQueueHelper;
@@ -97,6 +100,8 @@
     // can't be copied
     BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs);
     BLASTBufferQueue(const BLASTBufferQueue& rhs);
+    void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+                           sp<IGraphicBufferConsumer>* outConsumer);
 
     void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex);
     Rect computeCrop(const BufferItem& item) REQUIRES(mMutex);
@@ -137,6 +142,9 @@
     sp<BLASTBufferItemConsumer> mBufferItemConsumer;
 
     SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex);
+    std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
+            mPendingTransactions GUARDED_BY(mMutex);
+
     // If set to true, the next queue buffer will wait until the shadow queue has been processed by
     // the adapter.
     bool mFlushShadowQueue = false;
@@ -151,6 +159,16 @@
     // layer size immediately or wait until we get the next buffer. This will support scenarios
     // where the layer can change sizes and the buffer will scale to fit the new size.
     uint32_t mLastBufferScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+
+    // Tracks the last acquired frame number
+    uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0;
+
+    std::function<void(int64_t)> mTransactionCompleteCallback GUARDED_BY(mMutex) = nullptr;
+    uint64_t mTransactionCompleteFrameNumber GUARDED_BY(mMutex){0};
+
+    // Queues up transactions using this token in SurfaceFlinger. This prevents queued up
+    // transactions from other parts of the client from blocking this transaction.
+    const sp<IBinder> mApplyToken = new BBinder();
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 86e3a25..40316db 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -166,7 +166,7 @@
             int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
             const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
             const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
-            const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+            bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
             const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) = 0;
 
     /* signal that we're done booting.
@@ -504,6 +504,11 @@
      */
     virtual status_t addTransactionTraceListener(
             const sp<gui::ITransactionTraceListener>& listener) = 0;
+
+    /**
+     * Gets priority of the RenderEngine in SurfaceFlinger.
+     */
+    virtual int getGPUContextPriority() = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -565,6 +570,7 @@
         ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN,
         SET_FRAME_TIMELINE_VSYNC,
         ADD_TRANSACTION_TRACE_LISTENER,
+        GET_GPU_CONTEXT_PRIORITY,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 26b3840..cb17cee 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -69,15 +69,14 @@
     status_t readFromParcel(const Parcel* input) override;
 
     JankData();
-    JankData(int64_t frameVsyncId, JankType jankType)
-          : frameVsyncId(frameVsyncId),
-            jankType(jankType) {}
+    JankData(int64_t frameVsyncId, int32_t jankType)
+          : frameVsyncId(frameVsyncId), jankType(jankType) {}
 
     // Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId
     int64_t frameVsyncId;
 
-    // The type of jank occurred
-    JankType jankType;
+    // Bitmask of janks that occurred
+    int32_t jankType;
 };
 
 class SurfaceStats : public Parcelable {
diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h
index 47daf95..fc91714 100644
--- a/libs/gui/include/gui/JankInfo.h
+++ b/libs/gui/include/gui/JankInfo.h
@@ -18,24 +18,31 @@
 
 namespace android {
 
-// Jank information tracked by SurfaceFlinger for the purpose of funneling to telemetry.
+// Jank information tracked by SurfaceFlinger(SF) for perfetto tracing and telemetry.
+// TODO(b/175843808): Change JankType from enum to enum class
 enum JankType {
     // No Jank
     None = 0x0,
-    // Jank not related to SurfaceFlinger or the App
-    Display = 0x1,
+    // Jank that occurs in the layers below SurfaceFlinger
+    DisplayHAL = 0x1,
     // SF took too long on the CPU
-    SurfaceFlingerDeadlineMissed = 0x2,
+    SurfaceFlingerCpuDeadlineMissed = 0x2,
     // SF took too long on the GPU
     SurfaceFlingerGpuDeadlineMissed = 0x4,
     // Either App or GPU took too long on the frame
     AppDeadlineMissed = 0x8,
-    // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a
-    // jank
-    // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
-    PredictionExpired = 0x10,
-    // Latching a buffer early might cause an early present of the frame
-    SurfaceFlingerEarlyLatch = 0x20,
+    // Vsync predictions have drifted beyond the threshold from the actual HWVsync
+    PredictionError = 0x10,
+    // Janks caused due to the time SF was scheduled to work on the frame
+    // Example: SF woke up too early and latched a buffer resulting in an early present
+    SurfaceFlingerScheduling = 0x20,
+    // A buffer is said to be stuffed if it was expected to be presented on a vsync but was
+    // presented later because the previous buffer was presented in its expected vsync. This
+    // usually happens if there is an unexpectedly long frame causing the rest of the buffers
+    // to enter a stuffed state.
+    BufferStuffing = 0x40,
+    // Jank due to unknown reasons.
+    Unknown = 0x80,
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index f1845ee..48bc5d5 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -185,6 +185,11 @@
     static bool getProtectedContentSupport();
 
     /**
+     * Gets the context priority of surface flinger's render engine.
+     */
+    static int getGPUContextPriority();
+
+    /**
      * Uncaches a buffer in ISurfaceComposer. It must be uncached via a transaction so that it is
      * in order with other transactions that use buffers.
      */
@@ -368,17 +373,25 @@
         // to be presented. When it is not possible to present at exactly that time, it will be
         // presented after the time has passed.
         //
+        // If the client didn't pass a desired presentation time, mDesiredPresentTime will be
+        // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true.
+        //
         // Desired present times that are more than 1 second in the future may be ignored.
         // When a desired present time has already passed, the transaction will be presented as soon
         // as possible.
         //
         // Transactions from the same process are presented in the same order that they are applied.
         // The desired present time does not affect this ordering.
-        int64_t mDesiredPresentTime = -1;
+        int64_t mDesiredPresentTime = 0;
+        bool mIsAutoTimestamp = true;
 
         // The vsync Id provided by Choreographer.getVsyncId
         int64_t mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
 
+        // If not null, transactions will be queued up using this token otherwise a common token
+        // per process will be used.
+        sp<IBinder> mApplyToken = nullptr;
+
         InputWindowCommands mInputWindowCommands;
         int mStatus = NO_ERROR;
 
@@ -546,6 +559,11 @@
         // in shared buffer mode.
         Transaction& setAutoRefresh(const sp<SurfaceControl>& sc, bool autoRefresh);
 
+        // Queues up transactions using this token in SurfaceFlinger.  By default, all transactions
+        // from a client are placed on the same queue. This can be used to prevent multiple
+        // transactions from blocking each other.
+        Transaction& setApplyToken(const sp<IBinder>& token);
+
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
 
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 4282ef9..d69b7c3 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -24,6 +24,7 @@
 #include <gui/FrameTimestamps.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/IProducerListener.h>
+#include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <gui/SyncScreenCaptureListener.h>
 #include <private/gui/ComposerService.h>
@@ -220,6 +221,32 @@
         return captureResults.result;
     }
 
+    void queueBuffer(sp<IGraphicBufferProducer> igbp, uint8_t r, uint8_t g, uint8_t b,
+                     nsecs_t presentTimeDelay) {
+        int slot;
+        sp<Fence> fence;
+        sp<GraphicBuffer> buf;
+        auto ret = igbp->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                       PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                       nullptr, nullptr);
+        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+        ASSERT_EQ(OK, igbp->requestBuffer(slot, &buf));
+
+        uint32_t* bufData;
+        buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+                  reinterpret_cast<void**>(&bufData));
+        fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight() / 2), buf->getStride(), r, g, b);
+        buf->unlock();
+
+        IGraphicBufferProducer::QueueBufferOutput qbOutput;
+        nsecs_t timestampNanos = systemTime() + presentTimeDelay;
+        IGraphicBufferProducer::QueueBufferInput input(timestampNanos, false, HAL_DATASPACE_UNKNOWN,
+                                                       Rect(mDisplayWidth, mDisplayHeight / 2),
+                                                       NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                       Fence::NO_FENCE);
+        igbp->queueBuffer(slot, input, &qbOutput);
+    }
+
     sp<SurfaceComposerClient> mClient;
     sp<ISurfaceComposer> mComposer;
 
@@ -475,6 +502,93 @@
                                /*border*/ 0, /*outsideRegion*/ true));
 }
 
+class TestProducerListener : public BnProducerListener {
+public:
+    sp<IGraphicBufferProducer> mIgbp;
+    TestProducerListener(const sp<IGraphicBufferProducer>& igbp) : mIgbp(igbp) {}
+    void onBufferReleased() override {
+        sp<GraphicBuffer> buffer;
+        sp<Fence> fence;
+        mIgbp->detachNextBuffer(&buffer, &fence);
+    }
+};
+
+TEST_F(BLASTBufferQueueTest, CustomProducerListener) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> igbProducer = adapter.getIGraphicBufferProducer();
+    ASSERT_NE(nullptr, igbProducer.get());
+    ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2));
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    ASSERT_EQ(NO_ERROR,
+              igbProducer->connect(new TestProducerListener(igbProducer), NATIVE_WINDOW_API_CPU,
+                                   false, &qbOutput));
+    ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+    for (int i = 0; i < 3; i++) {
+        int slot;
+        sp<Fence> fence;
+        sp<GraphicBuffer> buf;
+        auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                              PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                              nullptr, nullptr);
+        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+        ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+        IGraphicBufferProducer::QueueBufferOutput qbOutput;
+        IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+                                                       Rect(mDisplayWidth, mDisplayHeight),
+                                                       NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                       Fence::NO_FENCE);
+        igbProducer->queueBuffer(slot, input, &qbOutput);
+    }
+    adapter.waitForCallbacks();
+}
+
+TEST_F(BLASTBufferQueueTest, QueryNativeWindowQueuesToWindowComposer) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+    sp<android::Surface> surface = new Surface(adapter.getIGraphicBufferProducer());
+    ANativeWindow* nativeWindow = (ANativeWindow*)(surface.get());
+    int queuesToNativeWindow = 0;
+    int err = nativeWindow->query(nativeWindow, NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
+                                  &queuesToNativeWindow);
+    ASSERT_EQ(NO_ERROR, err);
+    ASSERT_EQ(queuesToNativeWindow, 1);
+}
+
+TEST_F(BLASTBufferQueueTest, OutOfOrderTransactionTest) {
+    sp<SurfaceControl> bgSurface =
+            mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceBufferState);
+    ASSERT_NE(nullptr, bgSurface.get());
+    Transaction t;
+    t.setLayerStack(bgSurface, 0)
+            .show(bgSurface)
+            .setDataspace(bgSurface, ui::Dataspace::V0_SRGB)
+            .setFrame(bgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+            .setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1)
+            .apply();
+
+    BLASTBufferQueueHelper slowAdapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> slowIgbProducer;
+    setUpProducer(slowAdapter, slowIgbProducer);
+    nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
+    queueBuffer(slowIgbProducer, 0 /* r */, 0 /* g */, 0 /* b */, presentTimeDelay);
+
+    BLASTBufferQueueHelper fastAdapter(bgSurface, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> fastIgbProducer;
+    setUpProducer(fastAdapter, fastIgbProducer);
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+    queueBuffer(fastIgbProducer, r, g, b, 0 /* presentTimeDelay */);
+    fastAdapter.waitForCallbacks();
+
+    // capture screen and verify that it is red
+    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
 class BLASTBufferQueueTransformTest : public BLASTBufferQueueTest {
 public:
     void test(uint32_t tr) {
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index c75c46c..3965ea0 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -638,6 +638,9 @@
     std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
     nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
     nonTouchableSurface->mInputInfo.ownerUid = 22222;
+    // Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by
+    // the default obscured/untrusted touch filter introduced in S.
+    nonTouchableSurface->mInputInfo.touchOcclusionMode = TouchOcclusionMode::ALLOW;
     nonTouchableSurface->showAt(100, 100);
 
     injectTap(190, 199);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index ce3afa2..fa98cd4 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -700,7 +700,7 @@
                                  const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
                                  const sp<IBinder>& /*applyToken*/,
                                  const InputWindowCommands& /*inputWindowCommands*/,
-                                 int64_t /*desiredPresentTime*/,
+                                 int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
                                  const client_cache_t& /*cachedBuffer*/,
                                  bool /*hasListenerCallbacks*/,
                                  const std::vector<ListenerCallbacks>& /*listenerCallbacks*/,
@@ -887,6 +887,8 @@
         return NO_ERROR;
     }
 
+    int getGPUContextPriority() override { return 0; };
+
 protected:
     IBinder* onAsBinder() override { return nullptr; }
 
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 34eba5b..2ed441d 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -23,6 +23,7 @@
 #include <android-base/stringprintf.h>
 #include <input/InputDevice.h>
 #include <input/InputEventLabels.h>
+#include <input/NamedEnum.h>
 
 using android::base::StringPrintf;
 
@@ -166,7 +167,9 @@
         mKeyCharacterMap(other.mKeyCharacterMap),
         mHasVibrator(other.mHasVibrator),
         mHasButtonUnderPad(other.mHasButtonUnderPad),
-        mMotionRanges(other.mMotionRanges) {}
+        mHasSensor(other.mHasSensor),
+        mMotionRanges(other.mMotionRanges),
+        mSensors(other.mSensors) {}
 
 InputDeviceInfo::~InputDeviceInfo() {
 }
@@ -185,7 +188,9 @@
     mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
     mHasVibrator = false;
     mHasButtonUnderPad = false;
+    mHasSensor = false;
     mMotionRanges.clear();
+    mSensors.clear();
 }
 
 const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(
@@ -214,4 +219,28 @@
     mMotionRanges.push_back(range);
 }
 
+void InputDeviceInfo::addSensorInfo(const InputDeviceSensorInfo& info) {
+    if (mSensors.find(info.type) != mSensors.end()) {
+        ALOGW("Sensor type %s already exists, will be replaced by new sensor added.",
+              NamedEnum::string(info.type).c_str());
+    }
+    mSensors.insert_or_assign(info.type, info);
+}
+
+const std::vector<InputDeviceSensorType> InputDeviceInfo::getSensorTypes() {
+    std::vector<InputDeviceSensorType> types;
+    for (const auto& [type, info] : mSensors) {
+        types.push_back(type);
+    }
+    return types;
+}
+
+const InputDeviceSensorInfo* InputDeviceInfo::getSensorInfo(InputDeviceSensorType type) {
+    auto it = mSensors.find(type);
+    if (it == mSensors.end()) {
+        return nullptr;
+    }
+    return &it->second;
+}
+
 } // namespace android
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index f5432ad..44f3f34 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -103,6 +103,48 @@
     }
 }
 
+bool KeyCharacterMap::operator==(const KeyCharacterMap& other) const {
+    if (mType != other.mType) {
+        return false;
+    }
+    if (mKeys.size() != other.mKeys.size() ||
+        mKeysByScanCode.size() != other.mKeysByScanCode.size() ||
+        mKeysByUsageCode.size() != other.mKeysByUsageCode.size()) {
+        return false;
+    }
+
+    for (size_t i = 0; i < mKeys.size(); i++) {
+        if (mKeys.keyAt(i) != other.mKeys.keyAt(i)) {
+            return false;
+        }
+        const Key* key = mKeys.valueAt(i);
+        const Key* otherKey = other.mKeys.valueAt(i);
+        if (key->label != otherKey->label || key->number != otherKey->number) {
+            return false;
+        }
+    }
+
+    for (size_t i = 0; i < mKeysByScanCode.size(); i++) {
+        if (mKeysByScanCode.keyAt(i) != other.mKeysByScanCode.keyAt(i)) {
+            return false;
+        }
+        if (mKeysByScanCode.valueAt(i) != other.mKeysByScanCode.valueAt(i)) {
+            return false;
+        }
+    }
+
+    for (size_t i = 0; i < mKeysByUsageCode.size(); i++) {
+        if (mKeysByUsageCode.keyAt(i) != other.mKeysByUsageCode.keyAt(i)) {
+            return false;
+        }
+        if (mKeysByUsageCode.valueAt(i) != other.mKeysByUsageCode.valueAt(i)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
 base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename,
                                                                      Format format) {
     Tokenizer* tokenizer;
@@ -112,7 +154,7 @@
     }
     std::unique_ptr<Tokenizer> t(tokenizer);
     auto ret = load(t.get(), format);
-    if (ret) {
+    if (ret.ok()) {
         (*ret)->mLoadFileName = filename;
     }
     return ret;
@@ -128,7 +170,7 @@
     }
     std::unique_ptr<Tokenizer> t(tokenizer);
     auto ret = load(t.get(), format);
-    if (ret) {
+    if (ret.ok()) {
         (*ret)->mLoadFileName = filename;
     }
     return ret;
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index 16ce48a..fa5a541 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -20,12 +20,13 @@
 
 #include <android/keycodes.h>
 #include <input/InputEventLabels.h>
-#include <input/Keyboard.h>
 #include <input/KeyLayoutMap.h>
-#include <utils/Log.h>
+#include <input/Keyboard.h>
+#include <input/NamedEnum.h>
 #include <utils/Errors.h>
-#include <utils/Tokenizer.h>
+#include <utils/Log.h>
 #include <utils/Timers.h>
+#include <utils/Tokenizer.h>
 
 // Enables debug output for the parser.
 #define DEBUG_PARSER 0
@@ -41,6 +42,26 @@
 
 static const char* WHITESPACE = " \t\r";
 
+#define SENSOR_ENTRY(type) NamedEnum::string(type), type
+static const std::unordered_map<std::string, InputDeviceSensorType> SENSOR_LIST =
+        {{SENSOR_ENTRY(InputDeviceSensorType::ACCELEROMETER)},
+         {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD)},
+         {SENSOR_ENTRY(InputDeviceSensorType::ORIENTATION)},
+         {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE)},
+         {SENSOR_ENTRY(InputDeviceSensorType::LIGHT)},
+         {SENSOR_ENTRY(InputDeviceSensorType::PRESSURE)},
+         {SENSOR_ENTRY(InputDeviceSensorType::TEMPERATURE)},
+         {SENSOR_ENTRY(InputDeviceSensorType::PROXIMITY)},
+         {SENSOR_ENTRY(InputDeviceSensorType::GRAVITY)},
+         {SENSOR_ENTRY(InputDeviceSensorType::LINEAR_ACCELERATION)},
+         {SENSOR_ENTRY(InputDeviceSensorType::ROTATION_VECTOR)},
+         {SENSOR_ENTRY(InputDeviceSensorType::RELATIVE_HUMIDITY)},
+         {SENSOR_ENTRY(InputDeviceSensorType::AMBIENT_TEMPERATURE)},
+         {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED)},
+         {SENSOR_ENTRY(InputDeviceSensorType::GAME_ROTATION_VECTOR)},
+         {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE_UNCALIBRATED)},
+         {SENSOR_ENTRY(InputDeviceSensorType::SIGNIFICANT_MOTION)}};
+
 // --- KeyLayoutMap ---
 
 KeyLayoutMap::KeyLayoutMap() {
@@ -59,7 +80,7 @@
     }
     std::unique_ptr<Tokenizer> t(tokenizer);
     auto ret = load(t.get());
-    if (ret) {
+    if (ret.ok()) {
         (*ret)->mLoadFileName = filename;
     }
     return ret;
@@ -74,7 +95,7 @@
     }
     std::unique_ptr<Tokenizer> t(tokenizer);
     auto ret = load(t.get());
-    if (ret) {
+    if (ret.ok()) {
         (*ret)->mLoadFileName = filename;
     }
     return ret;
@@ -127,6 +148,24 @@
     return NO_ERROR;
 }
 
+// Return pair of sensor type and sensor data index, for the input device abs code
+base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor(int32_t absCode) {
+    auto it = mSensorsByAbsCode.find(absCode);
+    if (it == mSensorsByAbsCode.end()) {
+#if DEBUG_MAPPING
+        ALOGD("mapSensor: absCode=%d, ~ Failed.", absCode);
+#endif
+        return Errorf("Can't find abs code {}.", absCode);
+    }
+    const Sensor& sensor = it->second;
+
+#if DEBUG_MAPPING
+    ALOGD("mapSensor: absCode=%d, sensorType=0x%0x, sensorDataIndex=0x%x.", absCode,
+          NamedEnum::string(sensor.sensorType), sensor.sensorDataIndex);
+#endif
+    return std::make_pair(sensor.sensorType, sensor.sensorDataIndex);
+}
+
 const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const {
     if (usageCode) {
         ssize_t index = mKeysByUsageCode.indexOfKey(usageCode);
@@ -242,6 +281,10 @@
                 mTokenizer->skipDelimiters(WHITESPACE);
                 status_t status = parseLed();
                 if (status) return status;
+            } else if (keywordToken == "sensor") {
+                mTokenizer->skipDelimiters(WHITESPACE);
+                status_t status = parseSensor();
+                if (status) return status;
             } else {
                 ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
                         keywordToken.string());
@@ -468,4 +511,84 @@
     map.add(code, led);
     return NO_ERROR;
 }
+
+static std::optional<InputDeviceSensorType> getSensorType(const char* token) {
+    auto it = SENSOR_LIST.find(std::string(token));
+    if (it == SENSOR_LIST.end()) {
+        return std::nullopt;
+    }
+    return it->second;
+}
+
+static std::optional<int32_t> getSensorDataIndex(String8 token) {
+    std::string tokenStr(token.string());
+    if (tokenStr == "X") {
+        return 0;
+    } else if (tokenStr == "Y") {
+        return 1;
+    } else if (tokenStr == "Z") {
+        return 2;
+    }
+    return std::nullopt;
+}
+
+// Parse sensor type and data index mapping, as below format
+// sensor <raw abs> <sensor type> <sensor data index>
+// raw abs : the linux abs code of the axis
+// sensor type : string name of InputDeviceSensorType
+// sensor data index : the data index of sensor, out of [X, Y, Z]
+// Examples:
+// sensor 0x00 ACCELEROMETER X
+// sensor 0x01 ACCELEROMETER Y
+// sensor 0x02 ACCELEROMETER Z
+// sensor 0x03 GYROSCOPE X
+// sensor 0x04 GYROSCOPE Y
+// sensor 0x05 GYROSCOPE Z
+status_t KeyLayoutMap::Parser::parseSensor() {
+    String8 codeToken = mTokenizer->nextToken(WHITESPACE);
+    char* end;
+    int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
+    if (*end) {
+        ALOGE("%s: Expected sensor %s number, got '%s'.", mTokenizer->getLocation().string(),
+              "abs code", codeToken.string());
+        return BAD_VALUE;
+    }
+
+    std::unordered_map<int32_t, Sensor>& map = mMap->mSensorsByAbsCode;
+    if (map.find(code) != map.end()) {
+        ALOGE("%s: Duplicate entry for sensor %s '%s'.", mTokenizer->getLocation().string(),
+              "abs code", codeToken.string());
+        return BAD_VALUE;
+    }
+
+    mTokenizer->skipDelimiters(WHITESPACE);
+    String8 sensorTypeToken = mTokenizer->nextToken(WHITESPACE);
+    std::optional<InputDeviceSensorType> typeOpt = getSensorType(sensorTypeToken.string());
+    if (!typeOpt) {
+        ALOGE("%s: Expected sensor code label, got '%s'.", mTokenizer->getLocation().string(),
+              sensorTypeToken.string());
+        return BAD_VALUE;
+    }
+    InputDeviceSensorType sensorType = typeOpt.value();
+    mTokenizer->skipDelimiters(WHITESPACE);
+    String8 sensorDataIndexToken = mTokenizer->nextToken(WHITESPACE);
+    std::optional<int32_t> indexOpt = getSensorDataIndex(sensorDataIndexToken);
+    if (!indexOpt) {
+        ALOGE("%s: Expected sensor data index label, got '%s'.", mTokenizer->getLocation().string(),
+              sensorDataIndexToken.string());
+        return BAD_VALUE;
+    }
+    int32_t sensorDataIndex = indexOpt.value();
+
+#if DEBUG_PARSER
+    ALOGD("Parsed sensor: abs code=%d, sensorType=%d, sensorDataIndex=%d.", code,
+          NamedEnum::string(sensorType).c_str(), sensorDataIndex);
+#endif
+
+    Sensor sensor;
+    sensor.sensorType = sensorType;
+    sensor.sensorDataIndex = sensorDataIndex;
+    map.emplace(code, sensor);
+    return NO_ERROR;
+}
 };
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 14dc9e5..f0895b3 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -111,7 +111,7 @@
     }
 
     base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path);
-    if (!ret) {
+    if (!ret.ok()) {
         return ret.error().code();
     }
     keyLayoutMap = *ret;
@@ -129,7 +129,7 @@
 
     base::Result<std::shared_ptr<KeyCharacterMap>> ret =
             KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE);
-    if (!ret) {
+    if (!ret.ok()) {
         return ret.error().code();
     }
     keyCharacterMap = *ret;
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
index c174ae9..f8f2f4e 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -14,9 +14,12 @@
  * limitations under the License.
  */
 
-
+#include <binder/Binder.h>
+#include <binder/Parcel.h>
 #include <gtest/gtest.h>
 #include <input/InputDevice.h>
+#include <input/KeyLayoutMap.h>
+#include <input/Keyboard.h>
 
 namespace android {
 
@@ -31,4 +34,52 @@
     ASSERT_EQ(std::string("deviceName-123_version_C_"), identifier.getCanonicalName());
 }
 
-} // namespace android
\ No newline at end of file
+class InputDeviceKeyMapTest : public testing::Test {
+protected:
+    void loadKeyLayout(const char* name) {
+        std::string path =
+                getInputDeviceConfigurationFilePathByName(name,
+                                                          InputDeviceConfigurationFileType::
+                                                                  KEY_LAYOUT);
+        ASSERT_FALSE(path.empty());
+        base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path);
+        ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << path;
+        mKeyMap.keyLayoutMap = std::move(*ret);
+        mKeyMap.keyLayoutFile = path;
+    }
+
+    void loadKeyCharacterMap(const char* name) {
+        InputDeviceIdentifier identifier;
+        identifier.name = name;
+        std::string path =
+                getInputDeviceConfigurationFilePathByName(identifier.getCanonicalName(),
+                                                          InputDeviceConfigurationFileType::
+                                                                  KEY_CHARACTER_MAP);
+        ASSERT_FALSE(path.empty()) << "KeyCharacterMap for " << name << " not found";
+        base::Result<std::shared_ptr<KeyCharacterMap>> ret =
+                KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE);
+        ASSERT_TRUE(ret.ok()) << "Cannot load KeyCharacterMap at " << path;
+        mKeyMap.keyCharacterMap = *ret;
+        mKeyMap.keyCharacterMapFile = path;
+    }
+
+    virtual void SetUp() override {
+        loadKeyLayout("Generic");
+        loadKeyCharacterMap("Generic");
+    }
+
+    virtual void TearDown() override {}
+
+    KeyMap mKeyMap;
+};
+
+TEST_F(InputDeviceKeyMapTest, keyCharacterMapParcelingTest) {
+    Parcel parcel;
+    mKeyMap.keyCharacterMap->writeToParcel(&parcel);
+    parcel.setDataPosition(0);
+    std::shared_ptr<KeyCharacterMap> map = KeyCharacterMap::readFromParcel(&parcel);
+    // Verify the key character map is the same as original
+    ASSERT_EQ(*map, *mKeyMap.keyCharacterMap);
+}
+
+} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 279e648..c88e298 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -222,6 +222,29 @@
     return err;
 }
 
+std::optional<RenderEngine::ContextPriority> GLESRenderEngine::createContextPriority(
+        const RenderEngineCreationArgs& args) {
+    if (!GLExtensions::getInstance().hasContextPriority()) {
+        return std::nullopt;
+    }
+
+    switch (args.contextPriority) {
+        case RenderEngine::ContextPriority::REALTIME:
+            if (gl::GLExtensions::getInstance().hasRealtimePriority()) {
+                return RenderEngine::ContextPriority::REALTIME;
+            } else {
+                ALOGI("Realtime priority unsupported, degrading gracefully to high priority");
+                return RenderEngine::ContextPriority::HIGH;
+            }
+        case RenderEngine::ContextPriority::HIGH:
+        case RenderEngine::ContextPriority::MEDIUM:
+        case RenderEngine::ContextPriority::LOW:
+            return args.contextPriority;
+        default:
+            return std::nullopt;
+    }
+}
+
 std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCreationArgs& args) {
     // initialize EGL for the default display
     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
@@ -235,6 +258,7 @@
         LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed");
     }
 
+    // Use the Android impl to grab EGL_NV_context_priority_realtime
     const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS);
     if (!eglExtensions) {
         checkGlError(__FUNCTION__, __LINE__);
@@ -251,17 +275,16 @@
         config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
     }
 
-    bool useContextPriority =
-            extensions.hasContextPriority() && args.contextPriority == ContextPriority::HIGH;
+    const std::optional<RenderEngine::ContextPriority> priority = createContextPriority(args);
     EGLContext protectedContext = EGL_NO_CONTEXT;
     if (args.enableProtectedContext && extensions.hasProtectedContent()) {
-        protectedContext = createEglContext(display, config, nullptr, useContextPriority,
-                                            Protection::PROTECTED);
+        protectedContext =
+                createEglContext(display, config, nullptr, priority, Protection::PROTECTED);
         ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
     }
 
-    EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority,
-                                       Protection::UNPROTECTED);
+    EGLContext ctxt =
+            createEglContext(display, config, protectedContext, priority, Protection::UNPROTECTED);
 
     // if can't create a GL context, we can only abort.
     LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
@@ -311,7 +334,6 @@
     ALOGI("extensions: %s", extensions.getExtensions());
     ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
     ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
-
     return engine;
 }
 
@@ -802,6 +824,12 @@
     ALOGV("Failed to find image for buffer: %" PRIu64, bufferId);
 }
 
+int GLESRenderEngine::getContextPriority() {
+    int value;
+    eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value);
+    return value;
+}
+
 FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) {
     // Translate win by the rounded corners rect coordinates, to have all values in
     // layer coordinate space.
@@ -1617,7 +1645,8 @@
 }
 
 EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
-                                              EGLContext shareContext, bool useContextPriority,
+                                              EGLContext shareContext,
+                                              std::optional<ContextPriority> contextPriority,
                                               Protection protection) {
     EGLint renderableType = 0;
     if (config == EGL_NO_CONFIG) {
@@ -1640,9 +1669,23 @@
     contextAttributes.reserve(7);
     contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
     contextAttributes.push_back(contextClientVersion);
-    if (useContextPriority) {
+    if (contextPriority) {
         contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
-        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+        switch (*contextPriority) {
+            case ContextPriority::REALTIME:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_REALTIME_NV);
+                break;
+            case ContextPriority::MEDIUM:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_MEDIUM_IMG);
+                break;
+            case ContextPriority::LOW:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LOW_IMG);
+                break;
+            case ContextPriority::HIGH:
+            default:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+                break;
+        }
     }
     if (protection == Protection::PROTECTED) {
         contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 92e1529..64d6c6b 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -71,6 +71,7 @@
                         const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
                         base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
     bool cleanupPostRender(CleanupMode mode) override;
+    int getContextPriority() override;
 
     EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
     // Creates an output image for rendering to
@@ -116,8 +117,11 @@
     static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
     static GlesVersion parseGlesVersion(const char* str);
     static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
-                                       EGLContext shareContext, bool useContextPriority,
+                                       EGLContext shareContext,
+                                       std::optional<ContextPriority> contextPriority,
                                        Protection protection);
+    static std::optional<RenderEngine::ContextPriority> createContextPriority(
+            const RenderEngineCreationArgs& args);
     static EGLSurface createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
                                                   int hwcFormat, Protection protection);
     std::unique_ptr<Framebuffer> createFramebuffer();
diff --git a/libs/renderengine/gl/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp
index 2924b0e..3dd534e 100644
--- a/libs/renderengine/gl/GLExtensions.cpp
+++ b/libs/renderengine/gl/GLExtensions.cpp
@@ -120,6 +120,10 @@
     if (extensionSet.hasExtension("EGL_KHR_surfaceless_context")) {
         mHasSurfacelessContext = true;
     }
+
+    if (extensionSet.hasExtension("EGL_NV_context_priority_realtime")) {
+        mHasRealtimePriority = true;
+    }
 }
 
 char const* GLExtensions::getEGLVersion() const {
diff --git a/libs/renderengine/gl/GLExtensions.h b/libs/renderengine/gl/GLExtensions.h
index ef00009..e415ff3 100644
--- a/libs/renderengine/gl/GLExtensions.h
+++ b/libs/renderengine/gl/GLExtensions.h
@@ -41,6 +41,7 @@
     bool hasContextPriority() const { return mHasContextPriority; }
     bool hasSurfacelessContext() const { return mHasSurfacelessContext; }
     bool hasProtectedTexture() const { return mHasProtectedTexture; }
+    bool hasRealtimePriority() const { return mHasRealtimePriority; }
 
     void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version,
                            GLubyte const* extensions);
@@ -67,6 +68,7 @@
     bool mHasContextPriority = false;
     bool mHasSurfacelessContext = false;
     bool mHasProtectedTexture = false;
+    bool mHasRealtimePriority = false;
 
     String8 mVendor;
     String8 mRenderer;
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 7fc0499..5ff9240 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -740,15 +740,6 @@
         if (needs.isOpaque()) {
             fs << "gl_FragColor.a = 1.0;";
         }
-        if (needs.hasAlpha()) {
-            // modulate the current alpha value with alpha set
-            if (needs.isPremultiplied()) {
-                // ... and the color too if we're premultiplied
-                fs << "gl_FragColor *= color.a;";
-            } else {
-                fs << "gl_FragColor.a *= color.a;";
-            }
-        }
     }
 
     if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) ||
@@ -768,6 +759,23 @@
         }
     }
 
+    /*
+     * Whether applying layer alpha before or after color transform doesn't matter,
+     * as long as we can undo premultiplication. But we cannot un-premultiply
+     * for color transform if the layer alpha = 0, e.g. 0 / (0 + 0.0019) = 0.
+     */
+    if (!needs.drawShadows()) {
+        if (needs.hasAlpha()) {
+            // modulate the current alpha value with alpha set
+            if (needs.isPremultiplied()) {
+                // ... and the color too if we're premultiplied
+                fs << "gl_FragColor *= color.a;";
+            } else {
+                fs << "gl_FragColor.a *= color.a;";
+            }
+        }
+    }
+
     if (needs.hasRoundedCorners()) {
         if (needs.isPremultiplied()) {
             fs << "gl_FragColor *= vec4(applyCornerRadius(outCropCoords));";
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index d8d989e..3a727c9 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -154,6 +154,9 @@
     int backgroundBlurRadius = 0;
 
     std::vector<BlurRegion> blurRegions;
+
+    // Name associated with the layer for debugging purposes.
+    std::string name;
 };
 
 // Keep in sync with custom comparison function in
@@ -274,6 +277,10 @@
     *os << "\n    .colorTransform = " << settings.colorTransform;
     *os << "\n    .disableBlending = " << settings.disableBlending;
     *os << "\n    .backgroundBlurRadius = " << settings.backgroundBlurRadius;
+    for (auto blurRegion : settings.blurRegions) {
+        *os << "\n";
+        PrintTo(blurRegion, os);
+    }
     *os << "\n    .shadow = ";
     PrintTo(settings.shadow, os);
     *os << "\n}";
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index ef12fd2..9157066 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -75,6 +75,7 @@
         LOW = 1,
         MEDIUM = 2,
         HIGH = 3,
+        REALTIME = 4,
     };
 
     enum class RenderEngineType {
@@ -181,6 +182,10 @@
                                 const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
                                 base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0;
     virtual void cleanFramebufferCache() = 0;
+    // Returns the priority this context was actually created with. Note: this may not be
+    // the same as specified at context creation time, due to implementation limits on the
+    // number of contexts that can be created at a specific priority level in the system.
+    virtual int getContextPriority() = 0;
 
 protected:
     friend class threaded::RenderEngineThreaded;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 95ee925..2c34da4 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -53,6 +53,7 @@
                           const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
                           base::unique_fd*));
     MOCK_METHOD0(cleanFramebufferCache, void());
+    MOCK_METHOD0(getContextPriority, int());
 };
 
 } // namespace mock
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index ee0a70a..46ae18c 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -169,17 +169,16 @@
         config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
     }
 
-    bool useContextPriority =
-            extensions.hasContextPriority() && args.contextPriority == ContextPriority::HIGH;
     EGLContext protectedContext = EGL_NO_CONTEXT;
+    const std::optional<RenderEngine::ContextPriority> priority = createContextPriority(args);
     if (args.enableProtectedContext && extensions.hasProtectedContent()) {
-        protectedContext = createEglContext(display, config, nullptr, useContextPriority,
-                                            Protection::PROTECTED);
+        protectedContext =
+                createEglContext(display, config, nullptr, priority, Protection::PROTECTED);
         ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
     }
 
-    EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority,
-                                       Protection::UNPROTECTED);
+    EGLContext ctxt =
+            createEglContext(display, config, protectedContext, priority, Protection::UNPROTECTED);
 
     // if can't create a GL context, we can only abort.
     LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
@@ -477,10 +476,23 @@
                                                                 mGrContext.get());
 
     SkCanvas* canvas = mCapture.tryCapture(surface.get());
+    if (canvas == nullptr) {
+        ALOGE("Cannot acquire canvas from Skia.");
+        return BAD_VALUE;
+    }
     // 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.
@@ -511,6 +523,15 @@
     for (const auto& layer : layers) {
         canvas->save();
 
+        if (mCapture.isCaptureRunning()) {
+            // Record the name of the layer if the capture is running.
+            std::stringstream layerSettings;
+            PrintTo(*layer, &layerSettings);
+            // Store the LayerSettings in additional information.
+            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());
 
@@ -651,13 +672,17 @@
                     ? getSkRect(layer->geometry.roundedCornersCrop)
                     : dest;
             drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
+        } else {
+            // Shadows are assumed to live only on their own layer - it's not valid
+            // to draw the boundary retangles when there is already a caster shadow
+            // TODO(b/175915334): consider relaxing this restriction to enable more flexible
+            // composition - using a well-defined invalid color is long-term less error-prone.
+            // Push the clipRRect onto the clip stack. Draw the image. Pop the clip.
+            if (layer->geometry.roundedCornersRadius > 0) {
+                canvas->clipRRect(getRoundedRect(layer), true);
+            }
+            canvas->drawRect(dest, paint);
         }
-
-        // Push the clipRRect onto the clip stack. Draw the image. Pop the clip.
-        if (layer->geometry.roundedCornersRadius > 0) {
-            canvas->clipRRect(getRoundedRect(layer), true);
-        }
-        canvas->drawRect(dest, paint);
         canvas->restore();
     }
     canvas->restore();
@@ -810,7 +835,8 @@
 }
 
 EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
-                                                EGLContext shareContext, bool useContextPriority,
+                                                EGLContext shareContext,
+                                                std::optional<ContextPriority> contextPriority,
                                                 Protection protection) {
     EGLint renderableType = 0;
     if (config == EGL_NO_CONFIG_KHR) {
@@ -833,9 +859,23 @@
     contextAttributes.reserve(7);
     contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
     contextAttributes.push_back(contextClientVersion);
-    if (useContextPriority) {
+    if (contextPriority) {
         contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
-        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+        switch (*contextPriority) {
+            case ContextPriority::REALTIME:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_REALTIME_NV);
+                break;
+            case ContextPriority::MEDIUM:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_MEDIUM_IMG);
+                break;
+            case ContextPriority::LOW:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LOW_IMG);
+                break;
+            case ContextPriority::HIGH:
+            default:
+                contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+                break;
+        }
     }
     if (protection == Protection::PROTECTED) {
         contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
@@ -860,6 +900,29 @@
     return context;
 }
 
+std::optional<RenderEngine::ContextPriority> SkiaGLRenderEngine::createContextPriority(
+        const RenderEngineCreationArgs& args) {
+    if (!gl::GLExtensions::getInstance().hasContextPriority()) {
+        return std::nullopt;
+    }
+
+    switch (args.contextPriority) {
+        case RenderEngine::ContextPriority::REALTIME:
+            if (gl::GLExtensions::getInstance().hasRealtimePriority()) {
+                return RenderEngine::ContextPriority::REALTIME;
+            } else {
+                ALOGI("Realtime priority unsupported, degrading gracefully to high priority");
+                return RenderEngine::ContextPriority::HIGH;
+            }
+        case RenderEngine::ContextPriority::HIGH:
+        case RenderEngine::ContextPriority::MEDIUM:
+        case RenderEngine::ContextPriority::LOW:
+            return args.contextPriority;
+        default:
+            return std::nullopt;
+    }
+}
+
 EGLSurface SkiaGLRenderEngine::createPlaceholderEglPbufferSurface(EGLDisplay display,
                                                                   EGLConfig config, int hwcFormat,
                                                                   Protection protection) {
@@ -884,6 +947,12 @@
 
 void SkiaGLRenderEngine::cleanFramebufferCache() {}
 
+int SkiaGLRenderEngine::getContextPriority() {
+    int value;
+    eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value);
+    return value;
+}
+
 } // namespace skia
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index b53250e..3d8c693 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -56,6 +56,7 @@
                         const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
                         base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
     void cleanFramebufferCache() override;
+    int getContextPriority() override;
     bool isProtected() const override { return mInProtectedContext; }
     bool supportsProtectedContent() const override;
     bool useProtectedContext(bool useProtectedContext) override;
@@ -68,8 +69,11 @@
 private:
     static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
     static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
-                                       EGLContext shareContext, bool useContextPriority,
+                                       EGLContext shareContext,
+                                       std::optional<ContextPriority> contextPriority,
                                        Protection protection);
+    static std::optional<RenderEngine::ContextPriority> createContextPriority(
+            const RenderEngineCreationArgs& args);
     static EGLSurface createPlaceholderEglPbufferSurface(EGLDisplay display, EGLConfig config,
                                                          int hwcFormat, Protection protection);
     inline SkRect getSkRect(const FloatRect& layer);
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 2352c7e..12b8586 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -56,6 +56,7 @@
         return 0;
     };
     virtual bool cleanupPostRender(CleanupMode) override { return true; };
+    virtual int getContextPriority() override { return 0; }
 };
 
 } // namespace skia
diff --git a/libs/renderengine/skia/debug/SkiaCapture.h b/libs/renderengine/skia/debug/SkiaCapture.h
index 52717a7..eaaf598 100644
--- a/libs/renderengine/skia/debug/SkiaCapture.h
+++ b/libs/renderengine/skia/debug/SkiaCapture.h
@@ -45,6 +45,8 @@
     SkCanvas* tryCapture(SkSurface* surface);
     // Called at the end of every frame.
     void endCapture();
+    // Returns whether the capture is running.
+    bool isCaptureRunning() { return mCaptureRunning; }
 
 private:
     // Performs the first-frame work of a multi frame SKP capture. Returns true if successful.
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 712e5e2..15fb1b8 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -20,6 +20,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include <chrono>
 #include <condition_variable>
@@ -305,6 +306,26 @@
                           backgroundColor.a);
     }
 
+    void expectShadowColorWithoutCaster(const FloatRect& casterBounds,
+                                        const renderengine::ShadowSettings& shadow,
+                                        const ubyte4& backgroundColor) {
+        const float shadowInset = shadow.length * -1.0f;
+        const Rect casterRect(casterBounds);
+        const Rect shadowRect =
+                Rect(casterRect).inset(shadowInset, shadowInset, shadowInset, shadowInset);
+
+        const Region backgroundRegion =
+                Region(fullscreenRect()).subtractSelf(casterRect).subtractSelf(shadowRect);
+
+        expectAlpha(shadowRect, 255);
+        // (0, 0, 0) fill on the bounds of the layer should be ignored.
+        expectBufferColor(casterRect, 255, 255, 255, 255, 254);
+
+        // verify background
+        expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b,
+                          backgroundColor.a);
+    }
+
     static renderengine::ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength,
                                                           bool casterIsTranslucent) {
         renderengine::ShadowSettings shadow;
@@ -406,6 +427,12 @@
     void fillBufferColorTransform();
 
     template <typename SourceVariant>
+    void fillBufferWithColorTransformZeroLayerAlpha();
+
+    template <typename SourceVariant>
+    void fillBufferColorTransformZeroLayerAlpha();
+
+    template <typename SourceVariant>
     void fillRedBufferWithRoundedCorners();
 
     template <typename SourceVariant>
@@ -440,6 +467,10 @@
                     const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
                     const ubyte4& backgroundColor);
 
+    void drawShadowWithoutCaster(const FloatRect& castingBounds,
+                                 const renderengine::ShadowSettings& shadow,
+                                 const ubyte4& backgroundColor);
+
     std::unique_ptr<renderengine::gl::GLESRenderEngine> mRE;
 
     // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
@@ -765,6 +796,36 @@
 }
 
 template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithColorTransformZeroLayerAlpha() {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = Rect(1, 1);
+
+    std::vector<const renderengine::LayerSettings*> layers;
+
+    renderengine::LayerSettings layer;
+    layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+    SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+    layer.alpha = 0;
+
+    // construct a fake color matrix
+    // simple inverse color
+    settings.colorTransform = mat4(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 1, 1, 1, 1);
+
+    layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+    layers.push_back(&layer);
+
+    invokeDraw(settings, layers, mBuffer);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransformZeroLayerAlpha() {
+    fillBufferWithColorTransformZeroLayerAlpha<SourceVariant>();
+    expectBufferColor(fullscreenRect(), 0, 0, 0, 0);
+}
+
+template <typename SourceVariant>
 void RenderEngineTest::fillRedBufferWithRoundedCorners() {
     renderengine::DisplaySettings settings;
     settings.physicalDisplay = fullscreenRect();
@@ -1082,6 +1143,37 @@
     invokeDraw(settings, layers, mBuffer);
 }
 
+void RenderEngineTest::drawShadowWithoutCaster(const FloatRect& castingBounds,
+                                               const renderengine::ShadowSettings& shadow,
+                                               const ubyte4& backgroundColor) {
+    renderengine::DisplaySettings settings;
+    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    std::vector<const renderengine::LayerSettings*> layers;
+
+    // add background layer
+    renderengine::LayerSettings bgLayer;
+    bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    bgLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+    ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
+                                  backgroundColor.b / 255.0f, this);
+    bgLayer.alpha = backgroundColor.a / 255.0f;
+    layers.push_back(&bgLayer);
+
+    // add shadow layer
+    renderengine::LayerSettings shadowLayer;
+    shadowLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+    shadowLayer.geometry.boundaries = castingBounds;
+    shadowLayer.alpha = 1.0f;
+    ColorSourceVariant::fillColor(shadowLayer, 0, 0, 0, this);
+    shadowLayer.shadow = shadow;
+    layers.push_back(&shadowLayer);
+
+    invokeDraw(settings, layers, mBuffer);
+}
+
 INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
                          testing::Values(std::make_shared<GLESRenderEngineFactory>(),
                                          std::make_shared<GLESCMRenderEngineFactory>(),
@@ -1240,6 +1332,13 @@
     fillBufferWithRoundedCorners<ColorSourceVariant>();
 }
 
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_colorSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
+    fillBufferColorTransformZeroLayerAlpha<ColorSourceVariant>();
+}
+
 TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) {
     const auto& renderEngineFactory = GetParam();
     mRE = renderEngineFactory->createRenderEngine();
@@ -1337,6 +1436,12 @@
 
     fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqueBufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
+    fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
 
 TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
     const auto& renderEngineFactory = GetParam();
@@ -1436,6 +1541,13 @@
     fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_bufferSource) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
+    fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
 TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) {
     const auto& renderEngineFactory = GetParam();
     mRE = renderEngineFactory->createRenderEngine();
@@ -1552,6 +1664,22 @@
     EXPECT_FALSE(mRE->isImageCachedForTesting(bufferId));
 }
 
+TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {
+    const auto& renderEngineFactory = GetParam();
+    mRE = renderEngineFactory->createRenderEngine();
+
+    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const float shadowLength = 5.0f;
+    Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+    casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+    renderengine::ShadowSettings settings =
+            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+                              false /* casterIsTranslucent */);
+
+    drawShadowWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor);
+    expectShadowColorWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor);
+}
+
 TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
     const auto& renderEngineFactory = GetParam();
     mRE = renderEngineFactory->createRenderEngine();
@@ -1788,4 +1916,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 5453302..08f2949 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -304,6 +304,21 @@
     resultFuture.wait();
 }
 
+int RenderEngineThreaded::getContextPriority() {
+    std::promise<int> resultPromise;
+    std::future<int> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::getContextPriority");
+            int priority = instance.getContextPriority();
+            resultPromise.set_value(priority);
+        });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
 } // namespace threaded
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index cdfbd04..8b1e2de 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -63,6 +63,7 @@
                         base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
 
     void cleanFramebufferCache() override;
+    int getContextPriority() override;
 
 private:
     void threadMain(CreateInstanceFactory factory);
diff --git a/libs/ui/include/ui/BlurRegion.h b/libs/ui/include/ui/BlurRegion.h
index c5a5d47..69a586e 100644
--- a/libs/ui/include/ui/BlurRegion.h
+++ b/libs/ui/include/ui/BlurRegion.h
@@ -17,6 +17,8 @@
 #pragma once
 
 #include <inttypes.h>
+#include <iosfwd>
+#include <iostream>
 
 namespace android {
 
@@ -33,4 +35,19 @@
     int bottom;
 };
 
+static inline void PrintTo(const BlurRegion& blurRegion, ::std::ostream* os) {
+    *os << "BlurRegion {";
+    *os << "\n    .blurRadius = " << blurRegion.blurRadius;
+    *os << "\n    .cornerRadiusTL = " << blurRegion.cornerRadiusTL;
+    *os << "\n    .cornerRadiusTR = " << blurRegion.cornerRadiusTR;
+    *os << "\n    .cornerRadiusBL = " << blurRegion.cornerRadiusBL;
+    *os << "\n    .cornerRadiusBR = " << blurRegion.cornerRadiusBR;
+    *os << "\n    .alpha = " << blurRegion.alpha;
+    *os << "\n    .left = " << blurRegion.left;
+    *os << "\n    .top = " << blurRegion.top;
+    *os << "\n    .right = " << blurRegion.right;
+    *os << "\n    .bottom = " << blurRegion.bottom;
+    *os << "\n}";
+}
+
 } // namespace android
\ No newline at end of file
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 393ced7..8b94f61 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -52,36 +52,18 @@
     license: "include/KHR/NOTICE",
 }
 
-llndk_library {
-    name: "libEGL.llndk",
-    symbol_file: "libs/libEGL.map.txt",
-    export_include_dirs: ["include"],
-}
-
-llndk_library {
-    name: "libGLESv1_CM.llndk",
-    symbol_file: "libs/libGLESv1_CM.map.txt",
-    export_include_dirs: ["include"],
-}
-
-llndk_library {
-    name: "libGLESv2.llndk",
-    symbol_file: "libs/libGLESv2.map.txt",
-    export_include_dirs: ["include"],
-}
-
-llndk_library {
-    name: "libGLESv3.llndk",
-    symbol_file: "libs/libGLESv3.map.txt",
-    export_include_dirs: ["include"],
-}
-
 cc_library_headers {
     name: "gl_headers",
+    host_supported: true,
     vendor_available: true,
     export_include_dirs: ["include"],
 }
 
+llndk_headers {
+    name: "gl_llndk_headers",
+    export_include_dirs: ["include"],
+}
+
 subdirs = [
     "*",
 ]
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 77d887c..5d17561 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -225,3 +225,27 @@
     srcs: ["GLES2/gl2.cpp"],
     cflags: ["-DLOG_TAG=\"libGLESv3\""],
 }
+
+llndk_library {
+    name: "libEGL.llndk",
+    symbol_file: "libEGL.map.txt",
+    export_llndk_headers: ["gl_llndk_headers"],
+}
+
+llndk_library {
+    name: "libGLESv1_CM.llndk",
+    symbol_file: "libGLESv1_CM.map.txt",
+    export_llndk_headers: ["gl_llndk_headers"],
+}
+
+llndk_library {
+    name: "libGLESv2.llndk",
+    symbol_file: "libGLESv2.map.txt",
+    export_llndk_headers: ["gl_llndk_headers"],
+}
+
+llndk_library {
+    name: "libGLESv3.llndk",
+    symbol_file: "libGLESv3.map.txt",
+    export_llndk_headers: ["gl_llndk_headers"],
+}
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 940a26b..9606daa 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -20,6 +20,7 @@
     },
     srcs: [
         "GpuMemTest.cpp",
+        "GpuMemTracerTest.cpp",
         "GpuStatsTest.cpp",
     ],
     shared_libs: [
@@ -29,14 +30,19 @@
         "libcutils",
         "libgfxstats",
         "libgpumem",
+        "libgpumemtracer",
         "libgraphicsenv",
         "liblog",
+        "libprotobuf-cpp-lite",
+        "libprotoutil",
         "libstatslog",
         "libstatspull",
         "libutils",
     ],
     static_libs: [
         "libgmock",
+        "libperfetto_client_experimental",
+        "perfetto_trace_protos",
     ],
     require_root: true,
 }
diff --git a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
new file mode 100644
index 0000000..cd8e19c
--- /dev/null
+++ b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
@@ -0,0 +1,198 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "gpuservice_unittest"
+
+#include <bpf/BpfMap.h>
+#include <gpumem/GpuMem.h>
+#include <gtest/gtest.h>
+#include <perfetto/trace/trace.pb.h>
+#include <tracing/GpuMemTracer.h>
+
+#include "TestableGpuMem.h"
+
+namespace android {
+
+constexpr uint32_t TEST_MAP_SIZE = 10;
+constexpr uint64_t TEST_GLOBAL_KEY = 0;
+constexpr uint32_t TEST_GLOBAL_PID = 0;
+constexpr uint64_t TEST_GLOBAL_VAL = 123;
+constexpr uint32_t TEST_GLOBAL_GPU_ID = 0;
+constexpr uint64_t TEST_PROC_KEY_1 = 1;
+constexpr uint32_t TEST_PROC_PID_1 = 1;
+constexpr uint64_t TEST_PROC_VAL_1 = 234;
+constexpr uint32_t TEST_PROC_1_GPU_ID = 0;
+constexpr uint64_t TEST_PROC_KEY_2 = 4294967298; // (1 << 32) + 2
+constexpr uint32_t TEST_PROC_PID_2 = 2;
+constexpr uint64_t TEST_PROC_VAL_2 = 345;
+constexpr uint32_t TEST_PROC_2_GPU_ID = 1;
+
+class GpuMemTracerTest : public testing::Test {
+public:
+    GpuMemTracerTest() {
+        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());
+    }
+
+    ~GpuMemTracerTest() {
+        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());
+    }
+
+    void SetUp() override {
+        SKIP_IF_BPF_NOT_SUPPORTED;
+        bpf::setrlimitForTest();
+
+        mGpuMem = std::make_shared<GpuMem>();
+        mGpuMemTracer = std::make_unique<GpuMemTracer>();
+        mGpuMemTracer->initializeForTest(mGpuMem);
+        mTestableGpuMem = TestableGpuMem(mGpuMem.get());
+
+        errno = 0;
+        mTestMap = bpf::BpfMap<uint64_t, uint64_t>(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE,
+                                                   BPF_F_NO_PREALLOC);
+
+        EXPECT_EQ(0, errno);
+        EXPECT_LE(0, mTestMap.getMap().get());
+        EXPECT_TRUE(mTestMap.isValid());
+    }
+
+    int getTracerThreadCount() { return mGpuMemTracer->tracerThreadCount; }
+
+    std::vector<perfetto::protos::TracePacket> readGpuMemTotalPacketsBlocking(
+            perfetto::TracingSession* tracingSession) {
+        std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
+        perfetto::protos::Trace trace;
+        trace.ParseFromArray(raw_trace.data(), int(raw_trace.size()));
+
+        std::vector<perfetto::protos::TracePacket> packets;
+        for (const auto& packet : trace.packet()) {
+            if (!packet.has_gpu_mem_total_event()) {
+                continue;
+            }
+            packets.emplace_back(packet);
+        }
+        return packets;
+    }
+
+    std::shared_ptr<GpuMem> mGpuMem;
+    TestableGpuMem mTestableGpuMem;
+    std::unique_ptr<GpuMemTracer> mGpuMemTracer;
+    bpf::BpfMap<uint64_t, uint64_t> mTestMap;
+};
+
+static constexpr uint64_t getSizeForPid(uint32_t pid) {
+    switch (pid) {
+        case TEST_GLOBAL_PID:
+            return TEST_GLOBAL_VAL;
+        case TEST_PROC_PID_1:
+            return TEST_PROC_VAL_1;
+        case TEST_PROC_PID_2:
+            return TEST_PROC_VAL_2;
+    }
+    return 0;
+}
+
+static constexpr uint32_t getGpuIdForPid(uint32_t pid) {
+    switch (pid) {
+        case TEST_GLOBAL_PID:
+            return TEST_GLOBAL_GPU_ID;
+        case TEST_PROC_PID_1:
+            return TEST_PROC_1_GPU_ID;
+        case TEST_PROC_PID_2:
+            return TEST_PROC_2_GPU_ID;
+    }
+    return 0;
+}
+
+TEST_F(GpuMemTracerTest, traceInitialCountersAfterGpuMemInitialize) {
+    SKIP_IF_BPF_NOT_SUPPORTED;
+    ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY));
+    ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY));
+    ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY));
+    mTestableGpuMem.setGpuMemTotalMap(mTestMap);
+    mTestableGpuMem.setInitialized();
+
+    // Only 1 tracer thread should be existing for test.
+    EXPECT_EQ(getTracerThreadCount(), 1);
+    auto tracingSession = mGpuMemTracer->getTracingSessionForTest();
+
+    tracingSession->StartBlocking();
+    // Sleep for a short time to let the tracer thread finish its work
+    sleep(1);
+    tracingSession->StopBlocking();
+
+    // The test tracer thread should have finished its execution by now.
+    EXPECT_EQ(getTracerThreadCount(), 0);
+
+    auto packets = readGpuMemTotalPacketsBlocking(tracingSession.get());
+    EXPECT_EQ(packets.size(), 3);
+
+    const auto& packet0 = packets[0];
+    ASSERT_TRUE(packet0.has_timestamp());
+    ASSERT_TRUE(packet0.has_gpu_mem_total_event());
+    const auto& gpuMemEvent0 = packet0.gpu_mem_total_event();
+    ASSERT_TRUE(gpuMemEvent0.has_pid());
+    const auto& pid0 = gpuMemEvent0.pid();
+    ASSERT_TRUE(gpuMemEvent0.has_size());
+    EXPECT_EQ(gpuMemEvent0.size(), getSizeForPid(pid0));
+    ASSERT_TRUE(gpuMemEvent0.has_gpu_id());
+    EXPECT_EQ(gpuMemEvent0.gpu_id(), getGpuIdForPid(pid0));
+
+    const auto& packet1 = packets[1];
+    ASSERT_TRUE(packet1.has_timestamp());
+    ASSERT_TRUE(packet1.has_gpu_mem_total_event());
+    const auto& gpuMemEvent1 = packet1.gpu_mem_total_event();
+    ASSERT_TRUE(gpuMemEvent1.has_pid());
+    const auto& pid1 = gpuMemEvent1.pid();
+    ASSERT_TRUE(gpuMemEvent1.has_size());
+    EXPECT_EQ(gpuMemEvent1.size(), getSizeForPid(pid1));
+    ASSERT_TRUE(gpuMemEvent1.has_gpu_id());
+    EXPECT_EQ(gpuMemEvent1.gpu_id(), getGpuIdForPid(pid1));
+
+    const auto& packet2 = packets[2];
+    ASSERT_TRUE(packet2.has_timestamp());
+    ASSERT_TRUE(packet2.has_gpu_mem_total_event());
+    const auto& gpuMemEvent2 = packet2.gpu_mem_total_event();
+    ASSERT_TRUE(gpuMemEvent2.has_pid());
+    const auto& pid2 = gpuMemEvent2.pid();
+    ASSERT_TRUE(gpuMemEvent2.has_size());
+    EXPECT_EQ(gpuMemEvent2.size(), getSizeForPid(pid2));
+    ASSERT_TRUE(gpuMemEvent2.has_gpu_id());
+    EXPECT_EQ(gpuMemEvent2.gpu_id(), getGpuIdForPid(pid2));
+}
+
+TEST_F(GpuMemTracerTest, noTracingWithoutGpuMemInitialize) {
+    // Only 1 tracer thread should be existing for test.
+    EXPECT_EQ(getTracerThreadCount(), 1);
+
+    auto tracingSession = mGpuMemTracer->getTracingSessionForTest();
+
+    tracingSession->StartBlocking();
+    // Sleep for a short time to let the tracer thread finish its work
+    sleep(1);
+    tracingSession->StopBlocking();
+
+    // The test tracer thread should have finished its execution by now.
+    EXPECT_EQ(getTracerThreadCount(), 0);
+
+    auto packets = readGpuMemTotalPacketsBlocking(tracingSession.get());
+    EXPECT_EQ(packets.size(), 0);
+}
+} // namespace android
diff --git a/services/gpuservice/tracing/GpuMemTracer.cpp b/services/gpuservice/tracing/GpuMemTracer.cpp
index 000cf27..6975151 100644
--- a/services/gpuservice/tracing/GpuMemTracer.cpp
+++ b/services/gpuservice/tracing/GpuMemTracer.cpp
@@ -44,9 +44,35 @@
     args.backends = perfetto::kSystemBackend;
     perfetto::Tracing::Initialize(args);
     registerDataSource();
-    std::thread tracerThread(&GpuMemTracer::threadLoop, this);
+    std::thread tracerThread(&GpuMemTracer::threadLoop, this, true);
     pthread_setname_np(tracerThread.native_handle(), "GpuMemTracerThread");
     tracerThread.detach();
+    tracerThreadCount++;
+}
+
+void GpuMemTracer::initializeForTest(std::shared_ptr<GpuMem> gpuMem) {
+    mGpuMem = gpuMem;
+    perfetto::TracingInitArgs args;
+    args.backends = perfetto::kInProcessBackend;
+    perfetto::Tracing::Initialize(args);
+    registerDataSource();
+    std::thread tracerThread(&GpuMemTracer::threadLoop, this, false);
+    pthread_setname_np(tracerThread.native_handle(), "GpuMemTracerThreadForTest");
+    tracerThread.detach();
+    tracerThreadCount++;
+}
+
+// Each tracing session can be used for a single block of Start -> Stop.
+std::unique_ptr<perfetto::TracingSession> GpuMemTracer::getTracingSessionForTest() {
+    perfetto::TraceConfig cfg;
+    cfg.set_duration_ms(500);
+    cfg.add_buffers()->set_size_kb(1024);
+    auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+    ds_cfg->set_name(GpuMemTracer::kGpuMemDataSource);
+
+    auto tracingSession = perfetto::Tracing::NewTrace(perfetto::kInProcessBackend);
+    tracingSession->Setup(cfg);
+    return tracingSession;
 }
 
 void GpuMemTracer::registerDataSource() {
@@ -55,8 +81,8 @@
     GpuMemDataSource::Register(dsd);
 }
 
-void GpuMemTracer::threadLoop() {
-    while (true) {
+void GpuMemTracer::threadLoop(bool infiniteLoop) {
+    do {
         {
             std::unique_lock<std::mutex> lock(GpuMemTracer::sTraceMutex);
             while (!sTraceStarted) {
@@ -68,7 +94,11 @@
             std::lock_guard<std::mutex> lock(GpuMemTracer::sTraceMutex);
             sTraceStarted = false;
         }
-    }
+    } while (infiniteLoop);
+
+    // Thread loop is exiting. Reduce the tracerThreadCount to reflect the number of active threads
+    // in the wait loop.
+    tracerThreadCount--;
 }
 
 void GpuMemTracer::traceInitialCounters() {
diff --git a/services/gpuservice/tracing/include/tracing/GpuMemTracer.h b/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
index 40deb4c..ae871f1 100644
--- a/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
+++ b/services/gpuservice/tracing/include/tracing/GpuMemTracer.h
@@ -20,6 +20,10 @@
 
 #include <mutex>
 
+namespace perfetto::protos {
+class TracePacket;
+}
+
 namespace android {
 
 class GpuMem;
@@ -45,16 +49,37 @@
     // perfetto::kInProcessBackend in tests.
     void registerDataSource();
 
+    // TODO(b/175904796): Refactor gpuservice lib to include perfetto lib and move the test
+    // functions into the unittests.
+    // Functions only used for testing with in-process backend. These functions require the static
+    // perfetto lib to be linked. If the tests have a perfetto linked, while libgpumemtracer.so also
+    // has one linked, they will both use different static states maintained in perfetto. Since the
+    // static perfetto states are not shared, tracing sessions created in the unit test are not
+    // recognized by GpuMemTracer. As a result, we cannot use any of the perfetto functions from
+    // this class, which defeats the purpose of the unit test. To solve this, we restrict all
+    // tracing functionality to this class, while the unit test validates the data.
+    // Sets up the perfetto in-process backend and calls into registerDataSource.
+    void initializeForTest(std::shared_ptr<GpuMem>);
+    // Creates a tracing session with in process backend, for testing.
+    std::unique_ptr<perfetto::TracingSession> getTracingSessionForTest();
+    // Read and filter the gpu memory packets from the created trace.
+    std::vector<perfetto::protos::TracePacket> readGpuMemTotalPacketsForTestBlocking(
+            perfetto::TracingSession* tracingSession);
+
     static constexpr char kGpuMemDataSource[] = "android.gpu.memory";
     static std::condition_variable sCondition;
     static std::mutex sTraceMutex;
     static bool sTraceStarted;
 
 private:
-    void traceInitialCounters();
-    void threadLoop();
+    // Friend class for testing
+    friend class GpuMemTracerTest;
 
+    void threadLoop(bool infiniteLoop);
+    void traceInitialCounters();
     std::shared_ptr<GpuMem> mGpuMem;
+    // Count of how many tracer threads are currently active. Useful for testing.
+    std::atomic<int32_t> tracerThreadCount = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index eafb5ab..f5f0400 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -391,6 +391,11 @@
     mListener->notifyMotion(&newArgs);
 }
 
+void InputClassifier::notifySensor(const NotifySensorArgs* args) {
+    // pass through
+    mListener->notifySensor(args);
+}
+
 void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
     // pass through
     mListener->notifySwitch(args);
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 6965940..bf10920 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -229,6 +229,7 @@
     virtual void notifyKey(const NotifyKeyArgs* args) override;
     virtual void notifyMotion(const NotifyMotionArgs* args) override;
     virtual void notifySwitch(const NotifySwitchArgs* args) override;
+    virtual void notifySensor(const NotifySensorArgs* args) override;
     virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
     void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
 
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 49a813e..4be49b1 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -211,6 +211,41 @@
     listener->notifySwitch(this);
 }
 
+// --- NotifySensorArgs ---
+
+NotifySensorArgs::NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                                   InputDeviceSensorType sensorType,
+                                   InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
+                                   nsecs_t hwTimestamp, std::vector<float> values)
+      : NotifyArgs(id, eventTime),
+        deviceId(deviceId),
+        source(source),
+        sensorType(sensorType),
+        accuracy(accuracy),
+        accuracyChanged(accuracyChanged),
+        hwTimestamp(hwTimestamp),
+        values(std::move(values)) {}
+
+NotifySensorArgs::NotifySensorArgs(const NotifySensorArgs& other)
+      : NotifyArgs(other.id, other.eventTime),
+        deviceId(other.deviceId),
+        source(other.source),
+        sensorType(other.sensorType),
+        accuracy(other.accuracy),
+        accuracyChanged(other.accuracyChanged),
+        hwTimestamp(other.hwTimestamp),
+        values(other.values) {}
+
+bool NotifySensorArgs::operator==(const NotifySensorArgs rhs) const {
+    return id == rhs.id && eventTime == rhs.eventTime && sensorType == rhs.sensorType &&
+            accuracy == rhs.accuracy && accuracyChanged == rhs.accuracyChanged &&
+            hwTimestamp == rhs.hwTimestamp && values == rhs.values;
+}
+
+void NotifySensorArgs::notify(const sp<InputListenerInterface>& listener) const {
+    listener->notifySensor(this);
+}
+
 // --- NotifyDeviceResetArgs ---
 
 NotifyDeviceResetArgs::NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId)
@@ -286,6 +321,11 @@
     mArgsQueue.push_back(new NotifySwitchArgs(*args));
 }
 
+void QueuedInputListener::notifySensor(const NotifySensorArgs* args) {
+    traceEvent(__func__, args->id);
+    mArgsQueue.push_back(new NotifySensorArgs(*args));
+}
+
 void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
     traceEvent(__func__, args->id);
     mArgsQueue.push_back(new NotifyDeviceResetArgs(*args));
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 3d99589..a50e5c7 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -148,7 +148,7 @@
     }
 
     base::Result<std::unique_ptr<InputChannel>> channel = mDispatcher->createInputChannel(name);
-    if (!channel) {
+    if (!channel.ok()) {
         return binder::Status::fromExceptionCode(exceptionCodeFromStatusT(channel.error().code()),
                                                  channel.error().message().c_str());
     }
diff --git a/services/inputflinger/OWNERS b/services/inputflinger/OWNERS
index 0313a40..82c6ee1 100644
--- a/services/inputflinger/OWNERS
+++ b/services/inputflinger/OWNERS
@@ -1,2 +1,3 @@
+lzye@google.com
 michaelwr@google.com
 svv@google.com
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 9fea298..4e55872 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -66,6 +66,13 @@
 
     void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
 
+    void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType,
+                           InputDeviceSensorAccuracy accuracy, nsecs_t timestamp,
+                           const std::vector<float>& values) override {}
+
+    void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
+                              InputDeviceSensorAccuracy accuracy) override {}
+
     void notifyUntrustedTouch(const std::string& obscuringPackage) override {}
 
     void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
@@ -96,6 +103,8 @@
 
     void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
 
+    void setPointerCapture(bool enabled) override {}
+
     InputDispatcherConfiguration mConfig;
 };
 
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 29df00b..6953d04 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -114,6 +114,22 @@
     return StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false");
 }
 
+// --- PointerCaptureChangedEntry ---
+
+// PointerCaptureChanged notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER
+// for all entries.
+PointerCaptureChangedEntry::PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime,
+                                                       bool hasPointerCapture)
+      : EventEntry(id, Type::POINTER_CAPTURE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
+        pointerCaptureEnabled(hasPointerCapture) {}
+
+PointerCaptureChangedEntry::~PointerCaptureChangedEntry() {}
+
+std::string PointerCaptureChangedEntry::getDescription() const {
+    return StringPrintf("PointerCaptureChangedEvent(pointerCaptureEnabled=%s)",
+                        pointerCaptureEnabled ? "true" : "false");
+}
+
 // --- KeyEntry ---
 
 KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
@@ -223,6 +239,41 @@
     return msg;
 }
 
+// --- SensorEntry ---
+
+SensorEntry::SensorEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                         uint32_t policyFlags, nsecs_t hwTimestamp,
+                         InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy,
+                         bool accuracyChanged, std::vector<float> values)
+      : EventEntry(id, Type::SENSOR, eventTime, policyFlags),
+        deviceId(deviceId),
+        source(source),
+        sensorType(sensorType),
+        accuracy(accuracy),
+        accuracyChanged(accuracyChanged),
+        hwTimestamp(hwTimestamp),
+        values(std::move(values)) {}
+
+SensorEntry::~SensorEntry() {}
+
+std::string SensorEntry::getDescription() const {
+    std::string msg;
+    msg += StringPrintf("SensorEntry(deviceId=%d, source=0x%08x, sensorType=0x%08x, "
+                        "accuracy=0x%08x, hwTimestamp=%" PRId64,
+                        deviceId, source, sensorType, accuracy, hwTimestamp);
+
+    if (!GetBoolProperty("ro.debuggable", false)) {
+        for (size_t i = 0; i < values.size(); i++) {
+            if (i > 0) {
+                msg += ", ";
+            }
+            msg += StringPrintf("(%.3f)", values[i]);
+        }
+    }
+    msg += StringPrintf(", policyFlags=0x%08x", policyFlags);
+    return msg;
+}
+
 // --- DispatchEntry ---
 
 volatile int32_t DispatchEntry::sNextSeqAtomic;
@@ -255,7 +306,8 @@
         keyEntry(nullptr),
         userActivityEventType(0),
         seq(0),
-        handled(false) {}
+        handled(false),
+        enabled(false) {}
 
 CommandEntry::~CommandEntry() {}
 
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 0661709..3a860f0 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -36,23 +36,10 @@
         FOCUS,
         KEY,
         MOTION,
+        SENSOR,
+        POINTER_CAPTURE_CHANGED,
     };
 
-    static const char* typeToString(Type type) {
-        switch (type) {
-            case Type::CONFIGURATION_CHANGED:
-                return "CONFIGURATION_CHANGED";
-            case Type::DEVICE_RESET:
-                return "DEVICE_RESET";
-            case Type::FOCUS:
-                return "FOCUS";
-            case Type::KEY:
-                return "KEY";
-            case Type::MOTION:
-                return "MOTION";
-        }
-    }
-
     int32_t id;
     Type type;
     nsecs_t eventTime;
@@ -115,6 +102,15 @@
     virtual ~FocusEntry();
 };
 
+struct PointerCaptureChangedEntry : EventEntry {
+    bool pointerCaptureEnabled;
+
+    PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, bool hasPointerCapture);
+    std::string getDescription() const override;
+
+    virtual ~PointerCaptureChangedEntry();
+};
+
 struct KeyEntry : EventEntry {
     int32_t deviceId;
     uint32_t source;
@@ -179,6 +175,25 @@
     virtual ~MotionEntry();
 };
 
+struct SensorEntry : EventEntry {
+    int32_t deviceId;
+    uint32_t source;
+    InputDeviceSensorType sensorType;
+    InputDeviceSensorAccuracy accuracy;
+    bool accuracyChanged;
+    nsecs_t hwTimestamp;
+
+    std::vector<float> values;
+
+    SensorEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                uint32_t policyFlags, nsecs_t hwTimestamp, InputDeviceSensorType sensorType,
+                InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
+                std::vector<float> values);
+    std::string getDescription() const override;
+
+    virtual ~SensorEntry();
+};
+
 // Tracks the progress of dispatching a particular event to a particular connection.
 struct DispatchEntry {
     const uint32_t seq; // unique sequence number, never 0
@@ -245,6 +260,7 @@
     sp<Connection> connection;
     nsecs_t eventTime;
     std::shared_ptr<KeyEntry> keyEntry;
+    std::shared_ptr<SensorEntry> sensorEntry;
     std::shared_ptr<InputApplicationHandle> inputApplicationHandle;
     std::string reason;
     int32_t userActivityEventType;
@@ -254,6 +270,7 @@
     sp<IBinder> oldToken;
     sp<IBinder> newToken;
     std::string obscuringPackage;
+    bool enabled;
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index ed4f05a..f3d969e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -47,8 +47,6 @@
 // Log debug messages about hover events.
 #define DEBUG_HOVER 0
 
-#include "InputDispatcher.h"
-
 #include <android-base/chrono_utils.h>
 #include <android-base/stringprintf.h>
 #include <android/os/IInputConstants.h>
@@ -73,6 +71,7 @@
 #include <sstream>
 
 #include "Connection.h"
+#include "InputDispatcher.h"
 
 #define INDENT "  "
 #define INDENT2 "    "
@@ -322,6 +321,17 @@
 static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
                                                           std::shared_ptr<EventEntry> eventEntry,
                                                           int32_t inputTargetFlags) {
+    if (eventEntry->type == EventEntry::Type::MOTION) {
+        const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
+        if (motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) {
+            const ui::Transform identityTransform;
+            // Use identity transform for joystick events events because they don't depend on
+            // the window info
+            return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform,
+                                                   1.0f /*globalScaleFactor*/);
+        }
+    }
+
     if (inputTarget.useDefaultPointerTransform()) {
         const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
         return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
@@ -435,6 +445,14 @@
     return interface_cast<IPlatformCompatNative>(service);
 }
 
+static KeyEvent createKeyEvent(const KeyEntry& entry) {
+    KeyEvent event;
+    event.initialize(entry.id, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC,
+                     entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState,
+                     entry.repeatCount, entry.downTime, entry.eventTime);
+    return event;
+}
+
 // --- InputDispatcher ---
 
 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
@@ -454,6 +472,8 @@
         mInTouchMode(true),
         mMaximumObscuringOpacityForTouch(1.0f),
         mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
+        mFocusedWindowRequestedPointerCapture(false),
+        mWindowTokenWithPointerCapture(nullptr),
         mCompatService(getCompatService()) {
     mLooper = new Looper(false);
     mReporter = createInputReporter();
@@ -713,6 +733,14 @@
             break;
         }
 
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+            const auto typedEntry =
+                    std::static_pointer_cast<PointerCaptureChangedEntry>(mPendingEvent);
+            dispatchPointerCaptureChangedLocked(currentTime, typedEntry, dropReason);
+            done = true;
+            break;
+        }
+
         case EventEntry::Type::KEY: {
             std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
             if (isAppSwitchDue) {
@@ -748,6 +776,23 @@
             done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
             break;
         }
+
+        case EventEntry::Type::SENSOR: {
+            std::shared_ptr<SensorEntry> sensorEntry =
+                    std::static_pointer_cast<SensorEntry>(mPendingEvent);
+            if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
+                dropReason = DropReason::APP_SWITCH;
+            }
+            //  Sensor timestamps use SYSTEM_TIME_BOOTTIME time base, so we can't use
+            // 'currentTime' here, get SYSTEM_TIME_BOOTTIME instead.
+            nsecs_t bootTime = systemTime(SYSTEM_TIME_BOOTTIME);
+            if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(bootTime, *sensorEntry)) {
+                dropReason = DropReason::STALE;
+            }
+            dispatchSensorLocked(currentTime, sensorEntry, &dropReason, nextWakeupTime);
+            done = true;
+            break;
+        }
     }
 
     if (done) {
@@ -862,7 +907,9 @@
             break;
         }
         case EventEntry::Type::CONFIGURATION_CHANGED:
-        case EventEntry::Type::DEVICE_RESET: {
+        case EventEntry::Type::DEVICE_RESET:
+        case EventEntry::Type::SENSOR:
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
             // nothing to do
             break;
         }
@@ -872,7 +919,10 @@
 }
 
 void InputDispatcher::addRecentEventLocked(std::shared_ptr<EventEntry> entry) {
-    mRecentQueue.push_back(entry);
+    // Do not store sensor event in recent queue to avoid flooding the queue.
+    if (entry->type != EventEntry::Type::SENSOR) {
+        mRecentQueue.push_back(entry);
+    }
     if (mRecentQueue.size() > RECENT_QUEUE_MAX_SIZE) {
         mRecentQueue.pop_front();
     }
@@ -968,6 +1018,10 @@
             ALOGI("Dropped event because it is stale.");
             reason = "inbound event was dropped because it is stale";
             break;
+        case DropReason::NO_POINTER_CAPTURE:
+            ALOGI("Dropped event because there is no window with Pointer Capture.");
+            reason = "inbound event was dropped because there is no window with Pointer Capture";
+            break;
         case DropReason::NOT_DROPPED: {
             LOG_ALWAYS_FATAL("Should not be dropping a NOT_DROPPED event");
             return;
@@ -991,10 +1045,16 @@
             }
             break;
         }
+        case EventEntry::Type::SENSOR: {
+            break;
+        }
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+            break;
+        }
         case EventEntry::Type::FOCUS:
         case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET: {
-            LOG_ALWAYS_FATAL("Should not drop %s events", EventEntry::typeToString(entry.type));
+            LOG_ALWAYS_FATAL("Should not drop %s events", NamedEnum::string(entry.type).c_str());
             break;
         }
     }
@@ -1176,6 +1236,55 @@
     dispatchEventLocked(currentTime, entry, {target});
 }
 
+void InputDispatcher::dispatchPointerCaptureChangedLocked(
+        nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
+        DropReason& dropReason) {
+    const bool haveWindowWithPointerCapture = mWindowTokenWithPointerCapture != nullptr;
+    if (entry->pointerCaptureEnabled == haveWindowWithPointerCapture) {
+        LOG_ALWAYS_FATAL_IF(mFocusedWindowRequestedPointerCapture,
+                            "The Pointer Capture state has already been dispatched to the window.");
+        // Pointer capture was already forcefully disabled because of focus change.
+        dropReason = DropReason::NOT_DROPPED;
+        return;
+    }
+
+    // Set drop reason for early returns
+    dropReason = DropReason::NO_POINTER_CAPTURE;
+
+    sp<IBinder> token;
+    if (entry->pointerCaptureEnabled) {
+        // Enable Pointer Capture
+        if (!mFocusedWindowRequestedPointerCapture) {
+            // This can happen if a window requests capture and immediately releases capture.
+            ALOGW("No window requested Pointer Capture.");
+            return;
+        }
+        token = getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+        LOG_ALWAYS_FATAL_IF(!token, "Cannot find focused window for Pointer Capture.");
+        mWindowTokenWithPointerCapture = token;
+    } else {
+        // Disable Pointer Capture
+        token = mWindowTokenWithPointerCapture;
+        mWindowTokenWithPointerCapture = nullptr;
+        mFocusedWindowRequestedPointerCapture = false;
+    }
+
+    auto channel = getInputChannelLocked(token);
+    if (channel == nullptr) {
+        // Window has gone away, clean up Pointer Capture state.
+        mWindowTokenWithPointerCapture = nullptr;
+        mFocusedWindowRequestedPointerCapture = false;
+        return;
+    }
+    InputTarget target;
+    target.inputChannel = channel;
+    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+    entry->dispatchInProgress = true;
+    dispatchEventLocked(currentTime, entry, {target});
+
+    dropReason = DropReason::NOT_DROPPED;
+}
+
 bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
                                         DropReason* dropReason, nsecs_t* nextWakeupTime) {
     // Preprocessing.
@@ -1298,6 +1407,51 @@
 #endif
 }
 
+void InputDispatcher::doNotifySensorLockedInterruptible(CommandEntry* commandEntry) {
+    mLock.unlock();
+
+    const std::shared_ptr<SensorEntry>& entry = commandEntry->sensorEntry;
+    if (entry->accuracyChanged) {
+        mPolicy->notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy);
+    }
+    mPolicy->notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy,
+                               entry->hwTimestamp, entry->values);
+    mLock.lock();
+}
+
+void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
+                                           DropReason* dropReason, nsecs_t* nextWakeupTime) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+    ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
+          "source=0x%x, sensorType=%s",
+          entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source,
+          NamedEnum::string(sensorType).c_str());
+#endif
+    std::unique_ptr<CommandEntry> commandEntry =
+            std::make_unique<CommandEntry>(&InputDispatcher::doNotifySensorLockedInterruptible);
+    commandEntry->sensorEntry = entry;
+    postCommandLocked(std::move(commandEntry));
+}
+
+bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+    ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId,
+          NamedEnum::string(sensorType).c_str());
+#endif
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+
+        for (auto it = mInboundQueue.begin(); it != mInboundQueue.end(); it++) {
+            std::shared_ptr<EventEntry> entry = *it;
+            if (entry->type == EventEntry::Type::SENSOR) {
+                it = mInboundQueue.erase(it);
+                releaseInboundEventLocked(entry);
+            }
+        }
+    }
+    return true;
+}
+
 bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
                                            DropReason* dropReason, nsecs_t* nextWakeupTime) {
     ATRACE_CALL();
@@ -1483,10 +1637,12 @@
             displayId = motionEntry.displayId;
             break;
         }
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
         case EventEntry::Type::FOCUS:
         case EventEntry::Type::CONFIGURATION_CHANGED:
-        case EventEntry::Type::DEVICE_RESET: {
-            ALOGE("%s events do not have a target display", EventEntry::typeToString(entry.type));
+        case EventEntry::Type::DEVICE_RESET:
+        case EventEntry::Type::SENSOR: {
+            ALOGE("%s events do not have a target display", NamedEnum::string(entry.type).c_str());
             return ADISPLAY_ID_NONE;
         }
     }
@@ -1540,7 +1696,7 @@
     if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
         ALOGI("Dropping %s event because there is no focused window or focused application in "
               "display %" PRId32 ".",
-              EventEntry::typeToString(entry.type), displayId);
+              NamedEnum::string(entry.type).c_str(), displayId);
         return InputEventInjectionResult::FAILED;
     }
 
@@ -1565,7 +1721,7 @@
         } else if (currentTime > *mNoFocusedWindowTimeoutTime) {
             // Already raised ANR. Drop the event
             ALOGE("Dropping %s event because there is no focused window",
-                  EventEntry::typeToString(entry.type));
+                  NamedEnum::string(entry.type).c_str());
             return InputEventInjectionResult::FAILED;
         } else {
             // Still waiting for the focused window
@@ -1877,6 +2033,8 @@
                 }
                 if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
                     targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+                } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
+                    targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
                 }
 
                 BitSet32 pointerIds;
@@ -2366,8 +2524,10 @@
 }
 
 void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
-    if (eventEntry.type == EventEntry::Type::FOCUS) {
-        // Focus events are passed to apps, but do not represent user activity.
+    if (eventEntry.type == EventEntry::Type::FOCUS ||
+        eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED) {
+        // Focus or pointer capture changed events are passed to apps, but do not represent user
+        // activity.
         return;
     }
     int32_t displayId = getTargetDisplayId(eventEntry);
@@ -2405,9 +2565,11 @@
         }
         case EventEntry::Type::FOCUS:
         case EventEntry::Type::CONFIGURATION_CHANGED:
-        case EventEntry::Type::DEVICE_RESET: {
+        case EventEntry::Type::DEVICE_RESET:
+        case EventEntry::Type::SENSOR:
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
             LOG_ALWAYS_FATAL("%s events are not user activity",
-                             EventEntry::typeToString(eventEntry.type));
+                             NamedEnum::string(eventEntry.type).c_str());
             break;
         }
     }
@@ -2451,7 +2613,7 @@
     if (inputTarget.flags & InputTarget::FLAG_SPLIT) {
         LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION,
                             "Entry type %s should not have FLAG_SPLIT",
-                            EventEntry::typeToString(eventEntry->type));
+                            NamedEnum::string(eventEntry->type).c_str());
 
         const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
         if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
@@ -2617,13 +2779,18 @@
 
             break;
         }
-        case EventEntry::Type::FOCUS: {
+        case EventEntry::Type::FOCUS:
+        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+            break;
+        }
+        case EventEntry::Type::SENSOR: {
+            LOG_ALWAYS_FATAL("SENSOR events should not go to apps via input channel");
             break;
         }
         case EventEntry::Type::CONFIGURATION_CHANGED:
         case EventEntry::Type::DEVICE_RESET: {
             LOG_ALWAYS_FATAL("%s events should not go to apps",
-                             EventEntry::typeToString(newEntry.type));
+                             NamedEnum::string(newEntry.type).c_str());
             break;
         }
     }
@@ -2821,6 +2988,7 @@
                 reportTouchEventForStatistics(motionEntry);
                 break;
             }
+
             case EventEntry::Type::FOCUS: {
                 const FocusEntry& focusEntry = static_cast<const FocusEntry&>(eventEntry);
                 status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq,
@@ -2830,10 +2998,20 @@
                 break;
             }
 
+            case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+                const auto& captureEntry =
+                        static_cast<const PointerCaptureChangedEntry&>(eventEntry);
+                status = connection->inputPublisher
+                                 .publishCaptureEvent(dispatchEntry->seq, captureEntry.id,
+                                                      captureEntry.pointerCaptureEnabled);
+                break;
+            }
+
             case EventEntry::Type::CONFIGURATION_CHANGED:
-            case EventEntry::Type::DEVICE_RESET: {
+            case EventEntry::Type::DEVICE_RESET:
+            case EventEntry::Type::SENSOR: {
                 LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
-                                 EventEntry::typeToString(eventEntry.type));
+                                 NamedEnum::string(eventEntry.type).c_str());
                 return;
             }
         }
@@ -3047,8 +3225,8 @@
 
 void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
         const CancelationOptions& options) {
-    for (const auto& pair : mConnectionsByFd) {
-        synthesizeCancelationEventsForConnectionLocked(pair.second, options);
+    for (const auto& [fd, connection] : mConnectionsByFd) {
+        synthesizeCancelationEventsForConnectionLocked(connection, options);
     }
 }
 
@@ -3124,14 +3302,17 @@
                                          static_cast<const MotionEntry&>(*cancelationEventEntry));
                 break;
             }
-            case EventEntry::Type::FOCUS: {
-                LOG_ALWAYS_FATAL("Canceling focus events is not supported");
+            case EventEntry::Type::FOCUS:
+            case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+                LOG_ALWAYS_FATAL("Canceling %s events is not supported",
+                                 NamedEnum::string(cancelationEventEntry->type).c_str());
                 break;
             }
             case EventEntry::Type::CONFIGURATION_CHANGED:
-            case EventEntry::Type::DEVICE_RESET: {
+            case EventEntry::Type::DEVICE_RESET:
+            case EventEntry::Type::SENSOR: {
                 LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
-                                 EventEntry::typeToString(cancelationEventEntry->type));
+                                 NamedEnum::string(cancelationEventEntry->type).c_str());
                 break;
             }
         }
@@ -3185,9 +3366,11 @@
             case EventEntry::Type::KEY:
             case EventEntry::Type::FOCUS:
             case EventEntry::Type::CONFIGURATION_CHANGED:
-            case EventEntry::Type::DEVICE_RESET: {
+            case EventEntry::Type::DEVICE_RESET:
+            case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+            case EventEntry::Type::SENSOR: {
                 LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
-                                     EventEntry::typeToString(downEventEntry->type));
+                                 NamedEnum::string(downEventEntry->type).c_str());
                 break;
             }
         }
@@ -3250,7 +3433,9 @@
                 // The first/last pointer went down/up.
                 action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
                         ? AMOTION_EVENT_ACTION_DOWN
-                        : AMOTION_EVENT_ACTION_UP;
+                        : (originalMotionEntry.flags & AMOTION_EVENT_FLAG_CANCELED) != 0
+                                ? AMOTION_EVENT_ACTION_CANCEL
+                                : AMOTION_EVENT_ACTION_UP;
             } else {
                 // A secondary pointer went down/up.
                 uint32_t splitPointerIndex = 0;
@@ -3520,6 +3705,34 @@
     }
 }
 
+void InputDispatcher::notifySensor(const NotifySensorArgs* args) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+    ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
+          " sensorType=%s",
+          args->id, args->eventTime, args->deviceId, args->source,
+          NamedEnum::string(args->sensorType).c_str());
+#endif
+
+    bool needWake;
+    { // acquire lock
+        mLock.lock();
+
+        // Just enqueue a new sensor event.
+        std::unique_ptr<SensorEntry> newEntry =
+                std::make_unique<SensorEntry>(args->id, args->eventTime, args->deviceId,
+                                              args->source, 0 /* policyFlags*/, args->hwTimestamp,
+                                              args->sensorType, args->accuracy,
+                                              args->accuracyChanged, args->values);
+
+        needWake = enqueueInboundEventLocked(std::move(newEntry));
+        mLock.unlock();
+    } // release lock
+
+    if (needWake) {
+        mLooper->wake();
+    }
+}
+
 bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) {
     return mInputFilterEnabled;
 }
@@ -3562,7 +3775,17 @@
           args->enabled ? "true" : "false");
 #endif
 
-    // TODO(prabirmsp): Implement.
+    bool needWake;
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+        auto entry = std::make_unique<PointerCaptureChangedEntry>(args->id, args->eventTime,
+                                                                  args->enabled);
+        needWake = enqueueInboundEventLocked(std::move(entry));
+    } // release lock
+
+    if (needWake) {
+        mLooper->wake();
+    }
 }
 
 InputEventInjectionResult InputDispatcher::injectInputEvent(
@@ -4456,6 +4679,24 @@
     return dump;
 }
 
+std::string InputDispatcher::dumpPointerCaptureStateLocked() {
+    std::string dump;
+
+    dump += StringPrintf(INDENT "FocusedWindowRequestedPointerCapture: %s\n",
+                         toString(mFocusedWindowRequestedPointerCapture));
+
+    std::string windowName = "None";
+    if (mWindowTokenWithPointerCapture) {
+        const sp<InputWindowHandle> captureWindowHandle =
+                getWindowHandleLocked(mWindowTokenWithPointerCapture);
+        windowName = captureWindowHandle ? captureWindowHandle->getName().c_str()
+                                         : "token has capture without window";
+    }
+    dump += StringPrintf(INDENT "CurrentWindowWithPointerCapture: %s\n", windowName.c_str());
+
+    return dump;
+}
+
 void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
     dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled));
     dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen));
@@ -4479,6 +4720,7 @@
 
     dump += dumpFocusedWindowsLocked();
     dump += dumpPendingFocusRequestsLocked();
+    dump += dumpPointerCaptureStateLocked();
 
     if (!mTouchStatesByDisplay.empty()) {
         dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
@@ -4711,7 +4953,7 @@
 }
 
 base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(
-        int32_t displayId, bool isGestureMonitor, const std::string& name) {
+        int32_t displayId, bool isGestureMonitor, const std::string& name, int32_t pid) {
     std::shared_ptr<InputChannel> serverChannel;
     std::unique_ptr<InputChannel> clientChannel;
     status_t result = openInputChannelPair(name, serverChannel, clientChannel);
@@ -4735,7 +4977,7 @@
 
         auto& monitorsByDisplay =
                 isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
-        monitorsByDisplay[displayId].emplace_back(serverChannel);
+        monitorsByDisplay[displayId].emplace_back(serverChannel, pid);
 
         mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
     }
@@ -4860,6 +5102,39 @@
     return OK;
 }
 
+void InputDispatcher::requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) {
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+        if (DEBUG_FOCUS) {
+            const sp<InputWindowHandle> windowHandle = getWindowHandleLocked(windowToken);
+            ALOGI("Request to %s Pointer Capture from: %s.", enabled ? "enable" : "disable",
+                  windowHandle != nullptr ? windowHandle->getName().c_str()
+                                          : "token without window");
+        }
+
+        const sp<IBinder> focusedToken =
+                getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+        if (focusedToken != windowToken) {
+            ALOGW("Ignoring request to %s Pointer Capture: window does not have focus.",
+                  enabled ? "enable" : "disable");
+            return;
+        }
+
+        if (enabled == mFocusedWindowRequestedPointerCapture) {
+            ALOGW("Ignoring request to %s Pointer Capture: "
+                  "window has %s requested pointer capture.",
+                  enabled ? "enable" : "disable", enabled ? "already" : "not");
+            return;
+        }
+
+        mFocusedWindowRequestedPointerCapture = enabled;
+        setPointerCaptureLocked(enabled);
+    } // release lock
+
+    // Wake the thread to process command entries.
+    mLooper->wake();
+}
+
 std::optional<int32_t> InputDispatcher::findGestureMonitorDisplayByTokenLocked(
         const sp<IBinder>& token) {
     for (const auto& it : mGestureMonitorsByDisplay) {
@@ -5384,14 +5659,6 @@
     mLock.lock();
 }
 
-KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) {
-    KeyEvent event;
-    event.initialize(entry.id, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC,
-                     entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState,
-                     entry.repeatCount, entry.downTime, entry.eventTime);
-    return event;
-}
-
 void InputDispatcher::reportDispatchStatistics(std::chrono::nanoseconds eventDuration,
                                                const Connection& connection, bool handled) {
     // TODO Write some statistics about how long we spend waiting.
@@ -5578,11 +5845,50 @@
         enqueueFocusEventLocked(newFocusedToken, true /*hasFocus*/, reason);
     }
 
+    // If a window has pointer capture, then it must have focus. We need to ensure that this
+    // contract is upheld when pointer capture is being disabled due to a loss of window focus.
+    // If the window loses focus before it loses pointer capture, then the window can be in a state
+    // where it has pointer capture but not focus, violating the contract. Therefore we must
+    // dispatch the pointer capture event before the focus event. Since focus events are added to
+    // the front of the queue (above), we add the pointer capture event to the front of the queue
+    // after the focus events are added. This ensures the pointer capture event ends up at the
+    // front.
+    disablePointerCaptureForcedLocked();
+
     if (mFocusedDisplayId == displayId) {
         notifyFocusChangedLocked(oldFocusedToken, newFocusedToken);
     }
 }
 
+void InputDispatcher::disablePointerCaptureForcedLocked() {
+    if (!mFocusedWindowRequestedPointerCapture && !mWindowTokenWithPointerCapture) {
+        return;
+    }
+
+    ALOGD_IF(DEBUG_FOCUS, "Disabling Pointer Capture because the window lost focus.");
+
+    if (mFocusedWindowRequestedPointerCapture) {
+        mFocusedWindowRequestedPointerCapture = false;
+        setPointerCaptureLocked(false);
+    }
+
+    if (!mWindowTokenWithPointerCapture) {
+        // No need to send capture changes because no window has capture.
+        return;
+    }
+
+    if (mPendingEvent != nullptr) {
+        // Move the pending event to the front of the queue. This will give the chance
+        // for the pending event to be dropped if it is a captured event.
+        mInboundQueue.push_front(mPendingEvent);
+        mPendingEvent = nullptr;
+    }
+
+    auto entry = std::make_unique<PointerCaptureChangedEntry>(mIdGenerator.nextId(), now(),
+                                                              false /* hasCapture */);
+    mInboundQueue.push_front(std::move(entry));
+}
+
 /**
  * Checks if the window token can be focused on a display. The token can be focused if there is
  * at least one window handle that is visible with the same token and all window handles with the
@@ -5626,4 +5932,21 @@
 
     return FocusResult::OK;
 }
+
+void InputDispatcher::setPointerCaptureLocked(bool enabled) {
+    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+            &InputDispatcher::doSetPointerCaptureLockedInterruptible);
+    commandEntry->enabled = enabled;
+    postCommandLocked(std::move(commandEntry));
+}
+
+void InputDispatcher::doSetPointerCaptureLockedInterruptible(
+        android::inputdispatcher::CommandEntry* commandEntry) {
+    mLock.unlock();
+
+    mPolicy->setPointerCapture(commandEntry->enabled);
+
+    mLock.lock();
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 8f58785..df0be99 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -94,6 +94,7 @@
     virtual void notifyKey(const NotifyKeyArgs* args) override;
     virtual void notifyMotion(const NotifyMotionArgs* args) override;
     virtual void notifySwitch(const NotifySwitchArgs* args) override;
+    virtual void notifySensor(const NotifySensorArgs* args) override;
     virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
     virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
 
@@ -123,10 +124,14 @@
     virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
             const std::string& name) override;
     virtual void setFocusedWindow(const FocusRequest&) override;
-    virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(
-            int32_t displayId, bool isGestureMonitor, const std::string& name) override;
+    virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
+                                                                           bool isGestureMonitor,
+                                                                           const std::string& name,
+                                                                           int32_t pid) override;
     virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
     virtual status_t pilferPointers(const sp<IBinder>& token) override;
+    virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
+    virtual bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override;
 
     std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
 
@@ -138,6 +143,7 @@
         DISABLED,
         BLOCKED,
         STALE,
+        NO_POINTER_CAPTURE,
     };
 
     enum class FocusResult {
@@ -351,6 +357,21 @@
     // Top focused display.
     int32_t mFocusedDisplayId GUARDED_BY(mLock);
 
+    // Whether the focused window on the focused display has requested Pointer Capture.
+    // The state of this variable should always be in sync with the state of Pointer Capture in the
+    // policy, which is updated through setPointerCaptureLocked(enabled).
+    bool mFocusedWindowRequestedPointerCapture GUARDED_BY(mLock);
+
+    // The window token that has Pointer Capture.
+    // This should be in sync with PointerCaptureChangedEvents dispatched to the input channel.
+    sp<IBinder> mWindowTokenWithPointerCapture GUARDED_BY(mLock);
+
+    // Disable Pointer Capture as a result of loss of window focus.
+    void disablePointerCaptureForcedLocked() REQUIRES(mLock);
+
+    // Set the Pointer Capture state in the Policy.
+    void setPointerCaptureLocked(bool enabled) REQUIRES(mLock);
+
     // Dispatcher state at time of last ANR.
     std::string mLastAnrState GUARDED_BY(mLock);
 
@@ -370,9 +391,13 @@
                               DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
     void dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry)
             REQUIRES(mLock);
+    void dispatchPointerCaptureChangedLocked(
+            nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
+            DropReason& dropReason) REQUIRES(mLock);
     void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> entry,
                              const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
-
+    void dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
+                              DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
     void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
     void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
 
@@ -533,6 +558,7 @@
     void logDispatchStateLocked() REQUIRES(mLock);
     std::string dumpFocusedWindowsLocked() REQUIRES(mLock);
     std::string dumpPendingFocusRequestsLocked() REQUIRES(mLock);
+    std::string dumpPointerCaptureStateLocked() REQUIRES(mLock);
 
     // Registration.
     void removeMonitorChannelLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
@@ -571,10 +597,12 @@
             REQUIRES(mLock);
     void doNotifyConnectionResponsiveLockedInterruptible(CommandEntry* commandEntry)
             REQUIRES(mLock);
+    void doNotifySensorLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
             REQUIRES(mLock);
     void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    void doSetPointerCaptureLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
                                           DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
                                           bool handled) REQUIRES(mLock);
@@ -582,7 +610,6 @@
                                              DispatchEntry* dispatchEntry, MotionEntry& motionEntry,
                                              bool handled) REQUIRES(mLock);
     void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    KeyEvent createKeyEvent(const KeyEntry& entry);
     void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
 
     // Statistics gathering.
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 1656a21..3bb0bc9 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "input/InputDevice.h"
+
 #include "InputState.h"
 
 #include "InputDispatcher.h"
diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp
index b347674..bbce759 100644
--- a/services/inputflinger/dispatcher/Monitor.cpp
+++ b/services/inputflinger/dispatcher/Monitor.cpp
@@ -19,7 +19,8 @@
 namespace android::inputdispatcher {
 
 // --- Monitor ---
-Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel) : inputChannel(inputChannel) {}
+Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid)
+      : inputChannel(inputChannel), pid(pid) {}
 
 // --- TouchedMonitor ---
 TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset)
diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h
index fc0b020..7be0760 100644
--- a/services/inputflinger/dispatcher/Monitor.h
+++ b/services/inputflinger/dispatcher/Monitor.h
@@ -24,7 +24,9 @@
 struct Monitor {
     std::shared_ptr<InputChannel> inputChannel; // never null
 
-    explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel);
+    int32_t pid;
+
+    explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid);
 };
 
 // For tracking the offsets we need to apply when adding gesture monitor targets.
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 9154d48..3491893 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -25,6 +25,7 @@
 #include <android/os/InputEventInjectionResult.h>
 #include <android/os/InputEventInjectionSync.h>
 #include <input/InputApplication.h>
+#include <input/InputDevice.h>
 #include <input/InputTransport.h>
 #include <input/InputWindow.h>
 #include <unordered_map>
@@ -172,8 +173,10 @@
      *
      * This method may be called on any thread (usually by the input manager).
      */
-    virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(
-            int32_t displayId, bool gestureMonitor, const std::string& name) = 0;
+    virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
+                                                                           bool gestureMonitor,
+                                                                           const std::string& name,
+                                                                           int32_t pid) = 0;
 
     /* Removes input channels that will no longer receive input events.
      *
@@ -186,6 +189,18 @@
      * This method may be called on any thread (usually by the input manager).
      */
     virtual status_t pilferPointers(const sp<IBinder>& token) = 0;
+
+    /**
+     * Enables Pointer Capture on the specified window if the window has focus.
+     *
+     * InputDispatcher is the source of truth of Pointer Capture.
+     */
+    virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) = 0;
+    /* Flush input device motion sensor.
+     *
+     * Returns true on success.
+     */
+    virtual bool flushSensor(int deviceId, InputDeviceSensorType sensorType) = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 1125257..d9ec020 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -68,6 +68,11 @@
     /* Notifies the system that an input channel is unrecoverably broken. */
     virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
     virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) = 0;
+    virtual void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType,
+                                   InputDeviceSensorAccuracy accuracy, nsecs_t timestamp,
+                                   const std::vector<float>& values) = 0;
+    virtual void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
+                                      InputDeviceSensorAccuracy accuracy) = 0;
 
     /* Notifies the system that an untrusted touch occurred. */
     virtual void notifyUntrustedTouch(const std::string& obscuringPackage) = 0;
@@ -134,6 +139,12 @@
      * The touchedToken passed as an argument is the window that received the input event.
      */
     virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) = 0;
+
+    /* Change the Pointer Capture state in InputReader.
+     *
+     * InputDispatcher is solely responsible for updating the Pointer Capture state.
+     */
+    virtual void setPointerCapture(bool enabled) = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/docs/pointer_capture.md b/services/inputflinger/docs/pointer_capture.md
new file mode 100644
index 0000000..8da699d
--- /dev/null
+++ b/services/inputflinger/docs/pointer_capture.md
@@ -0,0 +1,44 @@
+# Pointer Capture in InputFlinger
+
+## Introduction
+
+[Pointer Capture](https://developer.android.com/training/gestures/movement#pointer-capture) is a feature that was introduced to the Android input pipeline in Android 8.0 (Oreo). Pointer Capture can be enabled or disabled for an `InputWindow` through requests to `InputManagerService`. Enabling Pointer Capture performs the following changes related to the mouse cursor and the devices that control it:
+
+- The position of the mouse cursor is fixed to its location before Pointer Capture was enabled.
+- The mouse cursor is hidden.
+- Events from a mouse will be delivered with the source `SOURCE_MOUSE_RELATIVE`, and their `AXIS_X` and `AXIS_Y` will report relative position changes.
+- Events from a touchpad will be delivered with the source `SOURCE_TOUCHPAD`, and their `AXIS_X` and `AXIS_Y` will report the absolute position of each of the pointers on the touchpad.
+- Events from mouse and touchpad devices are dispatched to the focused `InputWindow`.
+- Events from devices that do not normally control the mouse cursor are not affected.
+
+`InputWindow`s can only gain Pointer Capture if they have window focus. If a window with Pointer Capture loses focus, Pointer Capture is disabled.
+
+## Pointer Capture pipeline in InputFlinger
+
+`InputDispatcher` is responsible for controlling the state of Pointer Capture. Since the feature requires changes to how events are generated, Pointer Capture is configured in `InputReader`.
+
+### Enabling Pointer Capture
+
+There are four key steps that take place when Pointer Capture is enabled:
+
+1. Requests to enable Pointer Capture are forwarded from `InputManagerService` to `InputDispatcher`.
+2. If the window that makes the request has focus, `InputDispatcher` enables the Pointer Capture state in `InputReader` through the `InputDispatcherPolicy`.
+3. When `InputReader` is successfully configured, it notifies `InputDispatcher` through the `InputListener` interface.
+4. `InputDispatcher` then notifies the `InputWindow` that Pointer Capture has been enabled by sending a special `CAPTURE` event through the `InputChannel`.
+
+### Disabling Pointer Capture
+
+Pointer Capture can be disabled in two ways: by a request through `InputManagerService`, and as a result of the `InputWindow` losing focus.
+
+When Pointer Capture is disabled by a request from the application, it follows the same pipeline as when Pointer Capture is enabled.
+
+#### Window loses Pointer Capture when it loses focus
+
+When an `InputWindow` with Pointer Capture loses focus, Pointer Capture is disabled immediately. The `InputWindow` receives a `CAPTURE` event through the `InputChannel`, followed by a `FOCUS` event to notify loss of focus.
+
+## Pointer Capture in `InputDispatcher`
+
+`InputDispatcher` tracks two pieces of state information regarding Pointer Capture:
+
+- `mFocusedWindowRequestedPointerCapture`: Whether or not the focused window has requested Pointer Capture. This is updated whenever the Dispatcher receives requests from `InputManagerService`.
+- `mWindowTokenWithPointerCapture`: The Binder token of the `InputWindow` that currently has Pointer Capture. This is only updated during the dispatch cycle. If it is not `nullptr`, it signifies that the window was notified that it has Pointer Capture.
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 58eb915..11f726d 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -20,6 +20,7 @@
 #include <vector>
 
 #include <input/Input.h>
+#include <input/InputDevice.h>
 #include <input/TouchVideoFrame.h>
 #include <utils/RefBase.h>
 
@@ -141,6 +142,30 @@
     virtual void notify(const sp<InputListenerInterface>& listener) const;
 };
 
+/* Describes a sensor event. */
+struct NotifySensorArgs : public NotifyArgs {
+    int32_t deviceId;
+    uint32_t source;
+    InputDeviceSensorType sensorType;
+    InputDeviceSensorAccuracy accuracy;
+    bool accuracyChanged;
+    nsecs_t hwTimestamp;
+    std::vector<float> values;
+
+    inline NotifySensorArgs() {}
+
+    NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                     InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy,
+                     bool accuracyChanged, nsecs_t hwTimestamp, std::vector<float> values);
+
+    NotifySensorArgs(const NotifySensorArgs& other);
+
+    bool operator==(const NotifySensorArgs rhs) const;
+
+    ~NotifySensorArgs() override {}
+
+    void notify(const sp<InputListenerInterface>& listener) const override;
+};
 
 /* Describes a switch event. */
 struct NotifySwitchArgs : public NotifyArgs {
@@ -211,6 +236,7 @@
     virtual void notifyKey(const NotifyKeyArgs* args) = 0;
     virtual void notifyMotion(const NotifyMotionArgs* args) = 0;
     virtual void notifySwitch(const NotifySwitchArgs* args) = 0;
+    virtual void notifySensor(const NotifySensorArgs* args) = 0;
     virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) = 0;
     virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) = 0;
 };
@@ -231,6 +257,7 @@
     virtual void notifyKey(const NotifyKeyArgs* args) override;
     virtual void notifyMotion(const NotifyMotionArgs* args) override;
     virtual void notifySwitch(const NotifySwitchArgs* args) override;
+    virtual void notifySensor(const NotifySensorArgs* args) override;
     virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
     void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
 
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 6cce8ec..ea9b483 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -111,6 +111,17 @@
 
     /* Return true if the device can send input events to the specified display. */
     virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
+
+    /* Enable sensor in input reader mapper. */
+    virtual bool enableSensor(int32_t deviceId, InputDeviceSensorType sensorType,
+                              std::chrono::microseconds samplingPeriod,
+                              std::chrono::microseconds maxBatchReportLatency) = 0;
+
+    /* Disable sensor in input reader mapper. */
+    virtual void disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0;
+
+    /* Flush sensor data in input reader mapper. */
+    virtual void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0;
 };
 
 // --- InputReaderConfiguration ---
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 0ccada9..abda4ef 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -37,6 +37,7 @@
         "mapper/KeyboardInputMapper.cpp",
         "mapper/MultiTouchInputMapper.cpp",
         "mapper/RotaryEncoderInputMapper.cpp",
+        "mapper/SensorInputMapper.cpp",
         "mapper/SingleTouchInputMapper.cpp",
         "mapper/SwitchInputMapper.cpp",
         "mapper/TouchInputMapper.cpp",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index f864c0e..b97ff90 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -157,6 +157,18 @@
         }
     }
 
+    if (deviceClasses.test(InputDeviceClass::SENSOR)) {
+        switch (axis) {
+            case ABS_X:
+            case ABS_Y:
+            case ABS_Z:
+            case ABS_RX:
+            case ABS_RY:
+            case ABS_RZ:
+                return InputDeviceClass::SENSOR;
+        }
+    }
+
     // External stylus gets the pressure axis
     if (deviceClasses.test(InputDeviceClass::EXTERNAL_STYLUS)) {
         if (axis == ABS_PRESSURE) {
@@ -250,6 +262,11 @@
     // uses the timestamps extensively and assumes they were recorded using the monotonic
     // clock.
     int clockId = CLOCK_MONOTONIC;
+    if (classes.test(InputDeviceClass::SENSOR)) {
+        // Each new sensor event should use the same time base as
+        // SystemClock.elapsedRealtimeNanos().
+        clockId = CLOCK_BOOTTIME;
+    }
     bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId);
     ALOGI("usingClockIoctl=%s", toString(usingClockIoctl));
 }
@@ -550,6 +567,15 @@
             : false;
 }
 
+bool EventHub::hasMscEvent(int32_t deviceId, int mscEvent) const {
+    std::scoped_lock _l(mLock);
+
+    Device* device = getDeviceLocked(deviceId);
+    return mscEvent >= 0 && mscEvent <= MSC_MAX && device != nullptr
+            ? device->mscBitmask.test(mscEvent)
+            : false;
+}
+
 int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
     if (scanCode >= 0 && scanCode <= KEY_MAX) {
         std::scoped_lock _l(mLock);
@@ -705,6 +731,17 @@
     return NAME_NOT_FOUND;
 }
 
+base::Result<std::pair<InputDeviceSensorType, int32_t>> EventHub::mapSensor(int32_t deviceId,
+                                                                            int32_t absCode) {
+    std::scoped_lock _l(mLock);
+    Device* device = getDeviceLocked(deviceId);
+
+    if (device != nullptr && device->keyMap.haveKeyLayout()) {
+        return device->keyMap.keyLayoutMap->mapSensor(absCode);
+    }
+    return Errorf("Device not found or device has no key layout.");
+}
+
 void EventHub::setExcludedDevices(const std::vector<std::string>& devices) {
     std::scoped_lock _l(mLock);
 
@@ -1405,6 +1442,7 @@
     device->readDeviceBitMask(EVIOCGBIT(EV_SW, 0), device->swBitmask);
     device->readDeviceBitMask(EVIOCGBIT(EV_LED, 0), device->ledBitmask);
     device->readDeviceBitMask(EVIOCGBIT(EV_FF, 0), device->ffBitmask);
+    device->readDeviceBitMask(EVIOCGBIT(EV_MSC, 0), device->mscBitmask);
     device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask);
 
     // See if this is a keyboard.  Ignore everything in the button range except for
@@ -1469,6 +1507,11 @@
         }
     }
 
+    // Check whether this device is an accelerometer.
+    if (device->propBitmask.test(INPUT_PROP_ACCELEROMETER)) {
+        device->classes |= InputDeviceClass::SENSOR;
+    }
+
     // Check whether this device has switches.
     for (int i = 0; i <= SW_MAX; i++) {
         if (device->swBitmask.test(i)) {
@@ -1493,9 +1536,11 @@
     }
 
     // Load the key map.
-    // We need to do this for joysticks too because the key layout may specify axes.
+    // We need to do this for joysticks too because the key layout may specify axes, and for
+    // sensor as well because the key layout may specify the axes to sensor data mapping.
     status_t keyMapStatus = NAME_NOT_FOUND;
-    if (device->classes.any(InputDeviceClass::KEYBOARD | InputDeviceClass::JOYSTICK)) {
+    if (device->classes.any(InputDeviceClass::KEYBOARD | InputDeviceClass::JOYSTICK |
+                            InputDeviceClass::SENSOR)) {
         // Load the keymap for the device.
         keyMapStatus = device->loadKeyMapLocked();
     }
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index d25d64a..c7c8e7c 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -28,6 +28,7 @@
 #include "KeyboardInputMapper.h"
 #include "MultiTouchInputMapper.h"
 #include "RotaryEncoderInputMapper.h"
+#include "SensorInputMapper.h"
 #include "SingleTouchInputMapper.h"
 #include "SwitchInputMapper.h"
 #include "VibratorInputMapper.h"
@@ -53,10 +54,11 @@
     if (!hasEventHubDevices()) {
         return false;
     }
-    // devices are either all enabled or all disabled, so we only need to check the first
-    auto& devicePair = mDevices.begin()->second;
-    auto& contextPtr = devicePair.first;
-    return contextPtr->isDeviceEnabled();
+    // An input device composed of sub devices can be individually enabled or disabled.
+    // If any of the sub device is enabled then the input device is considered as enabled.
+    bool enabled = false;
+    for_each_subdevice([&enabled](auto& context) { enabled |= context.isDeviceEnabled(); });
+    return enabled;
 }
 
 void InputDevice::setEnabled(bool enabled, nsecs_t when) {
@@ -196,6 +198,11 @@
         mappers.push_back(std::make_unique<JoystickInputMapper>(*contextPtr));
     }
 
+    // Motion sensor enabled devices.
+    if (classes.test(InputDeviceClass::SENSOR)) {
+        mappers.push_back(std::make_unique<SensorInputMapper>(*contextPtr));
+    }
+
     // External stylus-like devices.
     if (classes.test(InputDeviceClass::EXTERNAL_STYLUS)) {
         mappers.push_back(std::make_unique<ExternalStylusInputMapper>(*contextPtr));
@@ -460,6 +467,25 @@
     return vibrators;
 }
 
+bool InputDevice::enableSensor(InputDeviceSensorType sensorType,
+                               std::chrono::microseconds samplingPeriod,
+                               std::chrono::microseconds maxBatchReportLatency) {
+    bool success = true;
+    for_each_mapper(
+            [&success, sensorType, samplingPeriod, maxBatchReportLatency](InputMapper& mapper) {
+                success &= mapper.enableSensor(sensorType, samplingPeriod, maxBatchReportLatency);
+            });
+    return success;
+}
+
+void InputDevice::disableSensor(InputDeviceSensorType sensorType) {
+    for_each_mapper([sensorType](InputMapper& mapper) { mapper.disableSensor(sensorType); });
+}
+
+void InputDevice::flushSensor(InputDeviceSensorType sensorType) {
+    for_each_mapper([sensorType](InputMapper& mapper) { mapper.flushSensor(sensorType); });
+}
+
 void InputDevice::cancelTouch(nsecs_t when) {
     for_each_mapper([when](InputMapper& mapper) { mapper.cancelTouch(when); });
 }
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index a6b5e2d..6c493ff 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -220,6 +220,13 @@
     if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS)) {
         notifyExternalStylusPresenceChangedLocked();
     }
+
+    // Sensor input device is noisy, to save power disable it by default.
+    // Input device is classified as SENSOR when any sub device is a SENSOR device, check Eventhub
+    // device class to disable SENSOR sub device only.
+    if (mEventHub->getDeviceClasses(eventHubId).test(InputDeviceClass::SENSOR)) {
+        mEventHub->disableDevice(eventHubId);
+    }
 }
 
 void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) {
@@ -639,6 +646,36 @@
     return {};
 }
 
+void InputReader::disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) {
+    std::scoped_lock _l(mLock);
+
+    InputDevice* device = findInputDeviceLocked(deviceId);
+    if (device) {
+        device->disableSensor(sensorType);
+    }
+}
+
+bool InputReader::enableSensor(int32_t deviceId, InputDeviceSensorType sensorType,
+                               std::chrono::microseconds samplingPeriod,
+                               std::chrono::microseconds maxBatchReportLatency) {
+    std::scoped_lock _l(mLock);
+
+    InputDevice* device = findInputDeviceLocked(deviceId);
+    if (device) {
+        return device->enableSensor(sensorType, samplingPeriod, maxBatchReportLatency);
+    }
+    return false;
+}
+
+void InputReader::flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) {
+    std::scoped_lock _l(mLock);
+
+    InputDevice* device = findInputDeviceLocked(deviceId);
+    if (device) {
+        device->flushSensor(sensorType);
+    }
+}
+
 bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
     std::scoped_lock _l(mLock);
 
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 9e38d0a..2cea017 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -118,6 +118,9 @@
     /* The input device has a rotary encoder */
     ROTARY_ENCODER = 0x00001000,
 
+    /* The input device has a sensor like accelerometer, gyro, etc */
+    SENSOR = 0x00002000,
+
     /* The input device is virtual (not a real device, not part of UI configuration). */
     VIRTUAL = 0x40000000,
 
@@ -177,6 +180,8 @@
 
     virtual bool hasInputProperty(int32_t deviceId, int property) const = 0;
 
+    virtual bool hasMscEvent(int32_t deviceId, int mscEvent) const = 0;
+
     virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
                             int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
                             uint32_t* outFlags) const = 0;
@@ -201,6 +206,8 @@
      */
     virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0;
     virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) = 0;
+    virtual base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t deviceId,
+                                                                              int32_t absCode) = 0;
 
     /*
      * Query current input state.
@@ -346,6 +353,8 @@
 
     bool hasInputProperty(int32_t deviceId, int property) const override final;
 
+    bool hasMscEvent(int32_t deviceId, int mscEvent) const override final;
+
     status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
                     int32_t* outKeycode, int32_t* outMetaState,
                     uint32_t* outFlags) const override final;
@@ -353,6 +362,9 @@
     status_t mapAxis(int32_t deviceId, int32_t scanCode,
                      AxisInfo* outAxisInfo) const override final;
 
+    base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
+            int32_t deviceId, int32_t absCode) override final;
+
     void setExcludedDevices(const std::vector<std::string>& devices) override final;
 
     int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override final;
@@ -420,6 +432,7 @@
         BitArray<LED_MAX> ledBitmask;
         BitArray<FF_MAX> ffBitmask;
         BitArray<INPUT_PROP_MAX> propBitmask;
+        BitArray<MSC_MAX> mscBitmask;
 
         std::string configurationFile;
         std::unique_ptr<PropertyMap> configuration;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 8b14b06..5af76b7 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -87,6 +87,10 @@
     bool isVibrating();
     std::vector<int32_t> getVibratorIds();
     void cancelTouch(nsecs_t when);
+    bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
+                      std::chrono::microseconds maxBatchReportLatency);
+    void disableSensor(InputDeviceSensorType sensorType);
+    void flushSensor(InputDeviceSensorType sensorType);
 
     int32_t getMetaState();
     void updateMetaState(int32_t keyCode);
@@ -229,9 +233,12 @@
     inline bool hasRelativeAxis(int32_t code) const {
         return mEventHub->hasRelativeAxis(mId, code);
     }
-    inline bool hasInputProperty(int property) const {
+    inline bool hasInputProperty(int32_t property) const {
         return mEventHub->hasInputProperty(mId, property);
     }
+
+    inline bool hasMscEvent(int mscEvent) const { return mEventHub->hasMscEvent(mId, mscEvent); }
+
     inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState,
                            int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
         return mEventHub->mapKey(mId, scanCode, usageCode, metaState, outKeycode, outMetaState,
@@ -240,6 +247,10 @@
     inline status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const {
         return mEventHub->mapAxis(mId, scanCode, outAxisInfo);
     }
+    inline base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t absCode) {
+        return mEventHub->mapSensor(mId, absCode);
+    }
+
     inline std::vector<TouchVideoFrame> getVideoFrames() { return mEventHub->getVideoFrames(mId); }
     inline int32_t getScanCodeState(int32_t scanCode) const {
         return mEventHub->getScanCodeState(mId, scanCode);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index b16b86c..48d4596 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -87,6 +87,14 @@
 
     bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override;
 
+    bool enableSensor(int32_t deviceId, InputDeviceSensorType sensorType,
+                      std::chrono::microseconds samplingPeriod,
+                      std::chrono::microseconds maxBatchReportLatency) override;
+
+    void disableSensor(int32_t deviceId, InputDeviceSensorType sensorType) override;
+
+    void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) override;
+
 protected:
     // These members are protected so they can be instrumented by test cases.
     virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t deviceId,
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index 913cef7..1ce54ae 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -70,6 +70,16 @@
 
 void InputMapper::cancelTouch(nsecs_t when) {}
 
+bool InputMapper::enableSensor(InputDeviceSensorType sensorType,
+                               std::chrono::microseconds samplingPeriod,
+                               std::chrono::microseconds maxBatchReportLatency) {
+    return true;
+}
+
+void InputMapper::disableSensor(InputDeviceSensorType sensorType) {}
+
+void InputMapper::flushSensor(InputDeviceSensorType sensorType) {}
+
 int32_t InputMapper::getMetaState() {
     return 0;
 }
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 088dbd8..bd64d8d 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -68,6 +68,11 @@
     virtual bool isVibrating();
     virtual std::vector<int32_t> getVibratorIds();
     virtual void cancelTouch(nsecs_t when);
+    virtual bool enableSensor(InputDeviceSensorType sensorType,
+                              std::chrono::microseconds samplingPeriod,
+                              std::chrono::microseconds maxBatchReportLatency);
+    virtual void disableSensor(InputDeviceSensorType sensorType);
+    virtual void flushSensor(InputDeviceSensorType sensorType);
 
     virtual int32_t getMetaState();
     virtual void updateMetaState(int32_t keyCode);
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
new file mode 100644
index 0000000..7ac2dec
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -0,0 +1,421 @@
+/*
+ * 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 <locale>
+
+#include "../Macros.h"
+
+#include "SensorInputMapper.h"
+
+// Log detailed debug messages about each sensor event notification to the dispatcher.
+constexpr bool DEBUG_SENSOR_EVENT_DETAILS = false;
+
+namespace android {
+
+// Mask for the LSB 2nd, 3rd and fourth bits.
+constexpr int REPORTING_MODE_MASK = 0xE;
+constexpr int REPORTING_MODE_SHIFT = 1;
+constexpr float GRAVITY_MS2_UNIT = 9.80665f;
+constexpr float DEGREE_RADIAN_UNIT = 0.0174533f;
+
+/* Convert the sensor data from Linux to Android
+ * Linux accelerometer unit is per g,  Android unit is m/s^2
+ * Linux gyroscope unit is degree/second, Android unit is radians/second
+ */
+static void convertFromLinuxToAndroid(std::vector<float>& values,
+                                      InputDeviceSensorType sensorType) {
+    for (size_t i = 0; i < values.size(); i++) {
+        switch (sensorType) {
+            case InputDeviceSensorType::ACCELEROMETER:
+                values[i] *= GRAVITY_MS2_UNIT;
+                break;
+            case InputDeviceSensorType::GYROSCOPE:
+                values[i] *= DEGREE_RADIAN_UNIT;
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+SensorInputMapper::SensorInputMapper(InputDeviceContext& deviceContext)
+      : InputMapper(deviceContext) {}
+
+SensorInputMapper::~SensorInputMapper() {}
+
+uint32_t SensorInputMapper::getSources() {
+    return AINPUT_SOURCE_SENSOR;
+}
+
+template <typename T>
+bool SensorInputMapper::tryGetProperty(std::string keyName, T& outValue) {
+    const auto& config = getDeviceContext().getConfiguration();
+    return config.tryGetProperty(String8(keyName.c_str()), outValue);
+}
+
+void SensorInputMapper::parseSensorConfiguration(InputDeviceSensorType sensorType, int32_t absCode,
+                                                 int32_t sensorDataIndex, const Axis& axis) {
+    auto it = mSensors.find(sensorType);
+    if (it == mSensors.end()) {
+        Sensor sensor = createSensor(sensorType, axis);
+        sensor.dataVec[sensorDataIndex] = absCode;
+        mSensors.emplace(sensorType, sensor);
+    } else {
+        it->second.dataVec[sensorDataIndex] = absCode;
+    }
+}
+
+void SensorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+    InputMapper::populateDeviceInfo(info);
+
+    for (const auto& [sensorType, sensor] : mSensors) {
+        info->addSensorInfo(sensor.sensorInfo);
+        info->setHasSensor(true);
+    }
+}
+
+void SensorInputMapper::dump(std::string& dump) {
+    dump += INDENT2 "Sensor Input Mapper:\n";
+    dump += StringPrintf(INDENT3 " isDeviceEnabled %d\n", getDeviceContext().isDeviceEnabled());
+    dump += StringPrintf(INDENT3 " mHasHardwareTimestamp %d\n", mHasHardwareTimestamp);
+    dump += INDENT3 "Sensors:\n";
+    for (const auto& [sensorType, sensor] : mSensors) {
+        dump += StringPrintf(INDENT4 "%s\n", NamedEnum::string(sensorType).c_str());
+        dump += StringPrintf(INDENT5 "enabled: %d\n", sensor.enabled);
+        dump += StringPrintf(INDENT5 "samplingPeriod: %lld\n", sensor.samplingPeriod.count());
+        dump += StringPrintf(INDENT5 "maxBatchReportLatency: %lld\n",
+                             sensor.maxBatchReportLatency.count());
+        dump += StringPrintf(INDENT5 "maxRange: %f\n", sensor.sensorInfo.maxRange);
+        dump += StringPrintf(INDENT5 "power: %f\n", sensor.sensorInfo.power);
+        for (ssize_t i = 0; i < SENSOR_VEC_LEN; i++) {
+            int32_t rawAxis = sensor.dataVec[i];
+            dump += StringPrintf(INDENT5 "[%zd]: rawAxis: %d \n", i, rawAxis);
+            const auto it = mAxes.find(rawAxis);
+            if (it != mAxes.end()) {
+                const Axis& axis = it->second;
+                dump += StringPrintf(INDENT5 " min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f,"
+                                             "resolution=%0.5f\n",
+                                     axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
+                dump += StringPrintf(INDENT5 "  scale=%0.5f, offset=%0.5f\n", axis.scale,
+                                     axis.offset);
+                dump += StringPrintf(INDENT5 " rawMin=%d, rawMax=%d, "
+                                             "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n",
+                                     axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
+                                     axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz,
+                                     axis.rawAxisInfo.resolution);
+            }
+        }
+    }
+}
+
+void SensorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
+                                  uint32_t changes) {
+    InputMapper::configure(when, config, changes);
+
+    if (!changes) { // first time only
+        mDeviceEnabled = true;
+        // Check if device has MSC_TIMESTAMP event.
+        mHasHardwareTimestamp = getDeviceContext().hasMscEvent(MSC_TIMESTAMP);
+        // Collect all axes.
+        for (int32_t abs = ABS_X; abs <= ABS_MAX; abs++) {
+            // axis must be claimed by sensor class device
+            if (!(getAbsAxisUsage(abs, getDeviceContext().getDeviceClasses())
+                          .test(InputDeviceClass::SENSOR))) {
+                continue;
+            }
+            RawAbsoluteAxisInfo rawAxisInfo;
+            getAbsoluteAxisInfo(abs, &rawAxisInfo);
+            if (rawAxisInfo.valid) {
+                AxisInfo axisInfo;
+                // Axis doesn't need to be mapped, as sensor mapper doesn't generate any motion
+                // input events
+                axisInfo.mode = AxisInfo::MODE_NORMAL;
+                axisInfo.axis = -1;
+                // Check key layout map for sensor data mapping to axes
+                auto ret = getDeviceContext().mapSensor(abs);
+                if (ret.ok()) {
+                    InputDeviceSensorType sensorType = (*ret).first;
+                    int32_t sensorDataIndex = (*ret).second;
+                    const Axis& axis = createAxis(axisInfo, rawAxisInfo);
+                    parseSensorConfiguration(sensorType, abs, sensorDataIndex, axis);
+
+                    mAxes.insert({abs, axis});
+                }
+            }
+        }
+    }
+}
+
+SensorInputMapper::Axis SensorInputMapper::createAxis(const AxisInfo& axisInfo,
+                                                      const RawAbsoluteAxisInfo& rawAxisInfo) {
+    // Apply flat override.
+    int32_t rawFlat = axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride;
+
+    float scale = std::numeric_limits<float>::signaling_NaN();
+    float offset = 0;
+
+    // resolution is 1 of sensor's unit.  For accelerometer, it is G, for gyroscope,
+    // it is degree/s.
+    scale = 1.0f / rawAxisInfo.resolution;
+    offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
+
+    const float max = rawAxisInfo.maxValue / rawAxisInfo.resolution;
+    const float min = rawAxisInfo.minValue / rawAxisInfo.resolution;
+    const float flat = rawFlat * scale;
+    const float fuzz = rawAxisInfo.fuzz * scale;
+    const float resolution = rawAxisInfo.resolution;
+
+    // To eliminate noise while the Sensor is at rest, filter out small variations
+    // in axis values up front.
+    const float filter = fuzz ? fuzz : flat * 0.25f;
+    return Axis(rawAxisInfo, axisInfo, scale, offset, min, max, flat, fuzz, resolution, filter);
+}
+
+void SensorInputMapper::reset(nsecs_t when) {
+    // Recenter all axes.
+    for (std::pair<const int32_t, Axis>& pair : mAxes) {
+        Axis& axis = pair.second;
+        axis.resetValue();
+    }
+    mHardwareTimestamp = 0;
+    mPrevMscTime = 0;
+    InputMapper::reset(when);
+}
+
+SensorInputMapper::Sensor SensorInputMapper::createSensor(InputDeviceSensorType sensorType,
+                                                          const Axis& axis) {
+    InputDeviceIdentifier identifier = getDeviceContext().getDeviceIdentifier();
+    // Sensor Id will be assigned to device Id to distinguish same sensor from multiple input
+    // devices, in such a way that the sensor Id will be same as input device Id.
+    // The sensorType is to distinguish different sensors within one device.
+    // One input device can only have 1 sensor for each sensor Type.
+    InputDeviceSensorInfo sensorInfo(identifier.name, std::to_string(identifier.vendor),
+                                     identifier.version, sensorType,
+                                     InputDeviceSensorAccuracy::ACCURACY_HIGH,
+                                     axis.max /* maxRange */, axis.scale /* resolution */,
+                                     0.0f /* power */, 0 /* minDelay */,
+                                     0 /* fifoReservedEventCount */, 0 /* fifoMaxEventCount */,
+                                     NamedEnum::string(sensorType), 0 /* maxDelay */, 0 /* flags */,
+                                     getDeviceId());
+
+    std::string prefix = "sensor." + NamedEnum::string(sensorType);
+    transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower);
+
+    int32_t reportingMode = 0;
+    if (!tryGetProperty(prefix + ".reportingMode", reportingMode)) {
+        sensorInfo.flags |= (reportingMode & REPORTING_MODE_MASK) << REPORTING_MODE_SHIFT;
+    }
+
+    tryGetProperty(prefix + ".maxDelay", sensorInfo.maxDelay);
+
+    tryGetProperty(prefix + ".minDelay", sensorInfo.minDelay);
+
+    tryGetProperty(prefix + ".power", sensorInfo.power);
+
+    tryGetProperty(prefix + ".fifoReservedEventCount", sensorInfo.fifoReservedEventCount);
+
+    tryGetProperty(prefix + ".fifoMaxEventCount", sensorInfo.fifoMaxEventCount);
+
+    return Sensor(sensorInfo);
+}
+
+void SensorInputMapper::processHardWareTimestamp(nsecs_t evTime, int32_t mscTime) {
+    // Since MSC_TIMESTAMP initial state is different from the system time, we
+    // calculate the difference between two MSC_TIMESTAMP events, and use that
+    // to calculate the system time that should be tagged on the event.
+    // if the first time MSC_TIMESTAMP, store it
+    // else calculate difference between previous and current MSC_TIMESTAMP
+    if (mPrevMscTime == 0) {
+        mHardwareTimestamp = evTime;
+        if (DEBUG_SENSOR_EVENT_DETAILS) {
+            ALOGD("Initialize hardware timestamp = %" PRId64, mHardwareTimestamp);
+        }
+    } else {
+        // Calculate the difference between current msc_timestamp and
+        // previous msc_timestamp, including when msc_timestamp wraps around.
+        uint32_t timeDiff = (mPrevMscTime > static_cast<uint32_t>(mscTime))
+                ? (UINT32_MAX - mPrevMscTime + static_cast<uint32_t>(mscTime + 1))
+                : (static_cast<uint32_t>(mscTime) - mPrevMscTime);
+
+        mHardwareTimestamp += timeDiff * 1000LL;
+    }
+    mPrevMscTime = static_cast<uint32_t>(mscTime);
+}
+
+void SensorInputMapper::process(const RawEvent* rawEvent) {
+    switch (rawEvent->type) {
+        case EV_ABS: {
+            auto it = mAxes.find(rawEvent->code);
+            if (it != mAxes.end()) {
+                Axis& axis = it->second;
+                axis.newValue = rawEvent->value * axis.scale + axis.offset;
+            }
+            break;
+        }
+
+        case EV_SYN:
+            switch (rawEvent->code) {
+                case SYN_REPORT:
+                    for (std::pair<const int32_t, Axis>& pair : mAxes) {
+                        Axis& axis = pair.second;
+                        axis.currentValue = axis.newValue;
+                    }
+                    sync(rawEvent->when, false /*force*/);
+                    break;
+            }
+            break;
+
+        case EV_MSC:
+            switch (rawEvent->code) {
+                case MSC_TIMESTAMP:
+                    // hardware timestamp is nano seconds
+                    processHardWareTimestamp(rawEvent->when, rawEvent->value);
+                    break;
+            }
+    }
+}
+
+bool SensorInputMapper::setSensorEnabled(InputDeviceSensorType sensorType, bool enabled) {
+    auto it = mSensors.find(sensorType);
+    if (it == mSensors.end()) {
+        return false;
+    }
+
+    it->second.enabled = enabled;
+    if (!enabled) {
+        it->second.resetValue();
+    }
+
+    /* Currently we can't enable/disable sensors individually. Enabling any sensor will enable
+     * the device
+     */
+    mDeviceEnabled = false;
+    for (const auto& [sensorType, sensor] : mSensors) {
+        // If any sensor is on we will turn on the device.
+        if (sensor.enabled) {
+            mDeviceEnabled = true;
+            break;
+        }
+    }
+    return true;
+}
+
+void SensorInputMapper::flushSensor(InputDeviceSensorType sensorType) {
+    auto it = mSensors.find(sensorType);
+    if (it == mSensors.end()) {
+        return;
+    }
+    auto& sensor = it->second;
+    sensor.lastSampleTimeNs = 0;
+    for (size_t i = 0; i < SENSOR_VEC_LEN; i++) {
+        int32_t abs = sensor.dataVec[i];
+        auto itAxis = mAxes.find(abs);
+        if (itAxis != mAxes.end()) {
+            Axis& axis = itAxis->second;
+            axis.resetValue();
+        }
+    }
+}
+
+bool SensorInputMapper::enableSensor(InputDeviceSensorType sensorType,
+                                     std::chrono::microseconds samplingPeriod,
+                                     std::chrono::microseconds maxBatchReportLatency) {
+    if (DEBUG_SENSOR_EVENT_DETAILS) {
+        ALOGD("Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld",
+              NamedEnum::string(sensorType).c_str(), samplingPeriod.count(),
+              maxBatchReportLatency.count());
+    }
+
+    if (!setSensorEnabled(sensorType, true /* enabled */)) {
+        return false;
+    }
+
+    // Enable device
+    if (mDeviceEnabled) {
+        getDeviceContext().enableDevice();
+    }
+
+    // We know the sensor exists now, update the sampling period and batch report latency.
+    auto it = mSensors.find(sensorType);
+    it->second.samplingPeriod =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(samplingPeriod);
+    it->second.maxBatchReportLatency =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(maxBatchReportLatency);
+    return true;
+}
+
+void SensorInputMapper::disableSensor(InputDeviceSensorType sensorType) {
+    if (DEBUG_SENSOR_EVENT_DETAILS) {
+        ALOGD("Disable Sensor %s", NamedEnum::string(sensorType).c_str());
+    }
+
+    if (!setSensorEnabled(sensorType, false /* enabled */)) {
+        return;
+    }
+
+    // Disable device
+    if (!mDeviceEnabled) {
+        mHardwareTimestamp = 0;
+        mPrevMscTime = 0;
+        getDeviceContext().disableDevice();
+    }
+}
+
+void SensorInputMapper::sync(nsecs_t when, bool force) {
+    for (auto& [sensorType, sensor] : mSensors) {
+        // Skip if sensor not enabled
+        if (!sensor.enabled) {
+            continue;
+        }
+        std::vector<float> values;
+        for (ssize_t i = 0; i < SENSOR_VEC_LEN; i++) {
+            int32_t abs = sensor.dataVec[i];
+            auto it = mAxes.find(abs);
+            if (it != mAxes.end()) {
+                const Axis& axis = it->second;
+                values.push_back(axis.currentValue);
+            }
+        }
+
+        nsecs_t timestamp = mHasHardwareTimestamp ? mHardwareTimestamp : when;
+        if (DEBUG_SENSOR_EVENT_DETAILS) {
+            ALOGD("Sensor %s timestamp %" PRIu64 " values [%f %f %f]",
+                  NamedEnum::string(sensorType).c_str(), timestamp, values[0], values[1],
+                  values[2]);
+        }
+        if (sensor.lastSampleTimeNs.has_value() &&
+            timestamp - sensor.lastSampleTimeNs.value() < sensor.samplingPeriod.count()) {
+            if (DEBUG_SENSOR_EVENT_DETAILS) {
+                ALOGD("Sensor %s Skip a sample.", NamedEnum::string(sensorType).c_str());
+            }
+        } else {
+            // Convert to Android unit
+            convertFromLinuxToAndroid(values, sensorType);
+            // Notify dispatcher for sensor event
+            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;
+        }
+    }
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
new file mode 100644
index 0000000..1797fe3
--- /dev/null
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+#ifndef _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H
+#define _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H
+
+#include "InputMapper.h"
+
+namespace android {
+// sensor data vector length
+static constexpr ssize_t SENSOR_VEC_LEN = 3;
+
+class SensorInputMapper : public InputMapper {
+public:
+    explicit SensorInputMapper(InputDeviceContext& deviceContext);
+    ~SensorInputMapper() override;
+
+    uint32_t getSources() override;
+    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    void dump(std::string& dump) override;
+    void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override;
+    void reset(nsecs_t when) override;
+    void process(const RawEvent* rawEvent) override;
+    bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
+                      std::chrono::microseconds maxBatchReportLatency) override;
+    void disableSensor(InputDeviceSensorType sensorType) override;
+    void flushSensor(InputDeviceSensorType sensorType) override;
+
+private:
+    struct Axis {
+        explicit Axis(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo, float scale,
+                      float offset, float min, float max, float flat, float fuzz, float resolution,
+                      float filter)
+              : rawAxisInfo(rawAxisInfo),
+                axisInfo(axisInfo),
+                scale(scale),
+                offset(offset),
+                min(min),
+                max(max),
+                flat(flat),
+                fuzz(fuzz),
+                resolution(resolution),
+                filter(filter) {
+            resetValue();
+        }
+
+        RawAbsoluteAxisInfo rawAxisInfo;
+        AxisInfo axisInfo;
+
+        float scale;  // scale factor from raw to normalized values
+        float offset; // offset to add after scaling for normalization
+
+        float min;        // normalized inclusive minimum
+        float max;        // normalized inclusive maximum
+        float flat;       // normalized flat region size
+        float fuzz;       // normalized error tolerance
+        float resolution; // normalized resolution in units
+
+        float filter;       // filter out small variations of this size
+        float currentValue; // current value
+        float newValue;     // most recent value
+
+        void resetValue() {
+            this->currentValue = 0;
+            this->newValue = 0;
+        }
+    };
+
+    struct Sensor {
+        explicit Sensor(const InputDeviceSensorInfo& sensorInfo) : sensorInfo(sensorInfo) {
+            resetValue();
+        }
+        bool enabled;
+        InputDeviceSensorAccuracy accuracy;
+        std::chrono::nanoseconds samplingPeriod;
+        std::chrono::nanoseconds maxBatchReportLatency;
+        // last sample time in nano seconds
+        std::optional<nsecs_t> lastSampleTimeNs;
+        InputDeviceSensorInfo sensorInfo;
+        // Sensor X, Y, Z data mapping to abs
+        std::array<int32_t, SENSOR_VEC_LEN> dataVec;
+        void resetValue() {
+            this->enabled = false;
+            this->accuracy = InputDeviceSensorAccuracy::ACCURACY_NONE;
+            this->samplingPeriod = std::chrono::nanoseconds(0);
+            this->maxBatchReportLatency = std::chrono::nanoseconds(0);
+            this->lastSampleTimeNs = std::nullopt;
+        }
+    };
+
+    static Axis createAxis(const AxisInfo& AxisInfo, const RawAbsoluteAxisInfo& rawAxisInfo);
+
+    // Axes indexed by raw ABS_* axis index.
+    std::unordered_map<int32_t, Axis> mAxes;
+
+    // hardware timestamp from MSC_TIMESTAMP
+    nsecs_t mHardwareTimestamp;
+    uint32_t mPrevMscTime;
+
+    bool mDeviceEnabled;
+    // Does device support MSC_TIMESTAMP
+    bool mHasHardwareTimestamp;
+
+    // Sensor list
+    std::unordered_map<InputDeviceSensorType, Sensor> mSensors;
+
+    void sync(nsecs_t when, bool force);
+
+    template <typename T>
+    bool tryGetProperty(std::string keyName, T& outValue);
+
+    void parseSensorConfiguration(InputDeviceSensorType sensorType, int32_t absCode,
+                                  int32_t sensorDataIndex, const Axis& axis);
+
+    void processHardWareTimestamp(nsecs_t evTime, int32_t evValue);
+
+    Sensor createSensor(InputDeviceSensorType sensorType, const Axis& axis);
+
+    bool setSensorEnabled(InputDeviceSensorType sensorType, bool enabled);
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_SENSOR_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 5ab2ae3..6dbc88f 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -19,10 +19,10 @@
 #include <android-base/stringprintf.h>
 #include <android-base/thread_annotations.h>
 #include <binder/Binder.h>
-#include <input/Input.h>
-
 #include <gtest/gtest.h>
+#include <input/Input.h>
 #include <linux/input.h>
+
 #include <cinttypes>
 #include <thread>
 #include <unordered_set>
@@ -48,6 +48,9 @@
 static const int32_t INJECTOR_PID = 999;
 static const int32_t INJECTOR_UID = 1001;
 
+// An arbitrary pid of the gesture monitor window
+static constexpr int32_t MONITOR_PID = 2001;
+
 struct PointF {
     float x;
     float y;
@@ -71,12 +74,10 @@
     InputDispatcherConfiguration mConfig;
 
 protected:
-    virtual ~FakeInputDispatcherPolicy() {
-    }
+    virtual ~FakeInputDispatcherPolicy() {}
 
 public:
-    FakeInputDispatcherPolicy() {
-    }
+    FakeInputDispatcherPolicy() {}
 
     void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
         assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action,
@@ -211,6 +212,33 @@
         mConfig.keyRepeatDelay = delay;
     }
 
+    void waitForSetPointerCapture(bool enabled) {
+        std::unique_lock lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+
+        if (!mPointerCaptureChangedCondition.wait_for(lock, 100ms,
+                                                      [this, enabled]() REQUIRES(mLock) {
+                                                          return mPointerCaptureEnabled &&
+                                                                  *mPointerCaptureEnabled ==
+                                                                  enabled;
+                                                      })) {
+            FAIL() << "Timed out waiting for setPointerCapture(" << enabled << ") to be called.";
+        }
+        mPointerCaptureEnabled.reset();
+    }
+
+    void assertSetPointerCaptureNotCalled() {
+        std::unique_lock lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+
+        if (mPointerCaptureChangedCondition.wait_for(lock, 100ms) != std::cv_status::timeout) {
+            FAIL() << "Expected setPointerCapture(enabled) to not be called, but was called. "
+                      "enabled = "
+                   << *mPointerCaptureEnabled;
+        }
+        mPointerCaptureEnabled.reset();
+    }
+
 private:
     std::mutex mLock;
     std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -218,6 +246,9 @@
     sp<IBinder> mOnPointerDownToken GUARDED_BY(mLock);
     std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
 
+    std::condition_variable mPointerCaptureChangedCondition;
+    std::optional<bool> mPointerCaptureEnabled GUARDED_BY(mLock);
+
     // ANR handling
     std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
     std::queue<sp<IBinder>> mAnrConnectionTokens GUARDED_BY(mLock);
@@ -254,6 +285,12 @@
     void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
 
     void notifyUntrustedTouch(const std::string& obscuringPackage) override {}
+    void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType,
+                           InputDeviceSensorAccuracy accuracy, nsecs_t timestamp,
+                           const std::vector<float>& values) override {}
+
+    void notifySensorAccuracy(int deviceId, InputDeviceSensorType sensorType,
+                              InputDeviceSensorAccuracy accuracy) override {}
 
     void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
         *outConfig = mConfig;
@@ -307,6 +344,12 @@
         mOnPointerDownToken = newToken;
     }
 
+    void setPointerCapture(bool enabled) override {
+        std::scoped_lock lock(mLock);
+        mPointerCaptureEnabled = {enabled};
+        mPointerCaptureChangedCondition.notify_all();
+    }
+
     void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
                                          int32_t displayId) {
         std::scoped_lock lock(mLock);
@@ -342,7 +385,7 @@
         mFakePolicy = new FakeInputDispatcherPolicy();
         mDispatcher = new InputDispatcher(mFakePolicy);
         mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
-        //Start InputDispatcher thread
+        // Start InputDispatcher thread
         ASSERT_EQ(OK, mDispatcher->start());
     }
 
@@ -379,7 +422,6 @@
     }
 };
 
-
 TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) {
     KeyEvent event;
 
@@ -574,9 +616,7 @@
     }
     virtual ~FakeApplicationHandle() {}
 
-    virtual bool updateInfo() override {
-        return true;
-    }
+    virtual bool updateInfo() override { return true; }
 
     void setDispatchingTimeout(std::chrono::milliseconds timeout) {
         mInfo.dispatchingTimeoutMillis = timeout.count();
@@ -674,6 +714,9 @@
             case AINPUT_EVENT_TYPE_FOCUS: {
                 FAIL() << "Use 'consumeFocusEvent' for FOCUS events";
             }
+            case AINPUT_EVENT_TYPE_CAPTURE: {
+                FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events";
+            }
             default: {
                 FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType;
             }
@@ -696,6 +739,21 @@
         EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
     }
 
+    void consumeCaptureEvent(bool hasCapture) {
+        const InputEvent* event = consume();
+        ASSERT_NE(nullptr, event) << mName.c_str()
+                                  << ": consumer should have returned non-NULL event.";
+        ASSERT_EQ(AINPUT_EVENT_TYPE_CAPTURE, event->getType())
+                << "Got " << inputEventTypeToString(event->getType())
+                << " event instead of CAPTURE event";
+
+        ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+                << mName.c_str() << ": event displayId should always be NONE.";
+
+        const auto& captureEvent = static_cast<const CaptureEvent&>(*event);
+        EXPECT_EQ(hasCapture, captureEvent.getPointerCaptureEnabled());
+    }
+
     void assertNoEvents() {
         InputEvent* event = consume();
         if (event == nullptr) {
@@ -713,6 +771,10 @@
             FocusEvent& focusEvent = static_cast<FocusEvent&>(*event);
             ADD_FAILURE() << "Received focus event, hasFocus = "
                           << (focusEvent.getHasFocus() ? "true" : "false");
+        } else if (event->getType() == AINPUT_EVENT_TYPE_CAPTURE) {
+            const auto& captureEvent = static_cast<CaptureEvent&>(*event);
+            ADD_FAILURE() << "Received capture event, pointerCaptureEnabled = "
+                          << (captureEvent.getPointerCaptureEnabled() ? "true" : "false");
         }
         FAIL() << mName.c_str()
                << ": should not have received any events, so consume() should return NULL";
@@ -800,6 +862,8 @@
 
     void setWindowScale(float xScale, float yScale) { setWindowTransform(xScale, 0, 0, yScale); }
 
+    void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
+
     void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
         consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
                      expectedFlags);
@@ -810,39 +874,40 @@
     }
 
     void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
-            int32_t expectedFlags = 0) {
+                             int32_t expectedFlags = 0) {
         consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, expectedDisplayId,
                      expectedFlags);
     }
 
     void consumeMotionMove(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
-            int32_t expectedFlags = 0) {
+                           int32_t expectedFlags = 0) {
         consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE, expectedDisplayId,
                      expectedFlags);
     }
 
     void consumeMotionDown(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
-            int32_t expectedFlags = 0) {
+                           int32_t expectedFlags = 0) {
         consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId,
                      expectedFlags);
     }
 
     void consumeMotionPointerDown(int32_t pointerIdx,
-            int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) {
-        int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN
-                | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+                                  int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+                                  int32_t expectedFlags = 0) {
+        int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
+                (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
         consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags);
     }
 
     void consumeMotionPointerUp(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
-            int32_t expectedFlags = 0) {
-        int32_t action = AMOTION_EVENT_ACTION_POINTER_UP
-                | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+                                int32_t expectedFlags = 0) {
+        int32_t action = AMOTION_EVENT_ACTION_POINTER_UP |
+                (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
         consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags);
     }
 
     void consumeMotionUp(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
-            int32_t expectedFlags = 0) {
+                         int32_t expectedFlags = 0) {
         consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId,
                      expectedFlags);
     }
@@ -859,6 +924,12 @@
         mInputReceiver->consumeFocusEvent(hasFocus, inTouchMode);
     }
 
+    void consumeCaptureEvent(bool hasCapture) {
+        ASSERT_NE(mInputReceiver, nullptr)
+                << "Cannot consume events from a window with no receiver";
+        mInputReceiver->consumeCaptureEvent(hasCapture);
+    }
+
     void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
                       int32_t expectedFlags) {
         ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver";
@@ -900,6 +971,11 @@
 
     const std::string& getName() { return mName; }
 
+    void setOwnerInfo(int32_t ownerPid, int32_t ownerUid) {
+        mInfo.ownerPid = ownerPid;
+        mInfo.ownerUid = ownerUid;
+    }
+
 private:
     const std::string mName;
     std::unique_ptr<FakeInputReceiver> mInputReceiver;
@@ -1138,10 +1214,14 @@
     return generateMotionArgs(action, source, displayId, {PointF{100, 200}});
 }
 
+static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs(bool enabled) {
+    return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), enabled);
+}
+
 TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
-    sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
-            ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -1204,10 +1284,10 @@
 // The foreground window should receive the first touch down event.
 TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
-    sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top",
-            ADISPLAY_ID_DEFAULT);
-    sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
-            ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> windowTop =
+            new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> windowSecond =
+            new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -1488,17 +1568,18 @@
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
     // Create a couple of windows
-    sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
-            "First Window", ADISPLAY_ID_DEFAULT);
-    sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
-            "Second Window", ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> firstWindow =
+            new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> secondWindow =
+            new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
 
     // Add the windows to the dispatcher
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
 
     // Send down to the first window
-    NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+    NotifyMotionArgs downMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
     mDispatcher->notifyMotion(&downMotionArgs);
     // Only the first window should get the down event
     firstWindow->consumeMotionDown();
@@ -1511,8 +1592,9 @@
     secondWindow->consumeMotionDown();
 
     // Send up event to the second window
-    NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+    NotifyMotionArgs upMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
     mDispatcher->notifyMotion(&upMotionArgs);
     // The first  window gets no events and the second gets up
     firstWindow->assertNoEvents();
@@ -1525,26 +1607,29 @@
     PointF touchPoint = {10, 10};
 
     // Create a couple of windows
-    sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
-            "First Window", ADISPLAY_ID_DEFAULT);
-    sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
-            "Second Window", ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> firstWindow =
+            new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> secondWindow =
+            new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
 
     // Add the windows to the dispatcher
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
 
     // Send down to the first window
-    NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint});
+    NotifyMotionArgs downMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {touchPoint});
     mDispatcher->notifyMotion(&downMotionArgs);
     // Only the first window should get the down event
     firstWindow->consumeMotionDown();
     secondWindow->assertNoEvents();
 
     // Send pointer down to the first window
-    NotifyMotionArgs pointerDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN
-            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint});
+    NotifyMotionArgs pointerDownMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
+                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {touchPoint, touchPoint});
     mDispatcher->notifyMotion(&pointerDownMotionArgs);
     // Only the first window should get the pointer down event
     firstWindow->consumeMotionPointerDown(1);
@@ -1558,17 +1643,20 @@
     secondWindow->consumeMotionPointerDown(1);
 
     // Send pointer up to the second window
-    NotifyMotionArgs pointerUpMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP
-            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint});
+    NotifyMotionArgs pointerUpMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
+                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {touchPoint, touchPoint});
     mDispatcher->notifyMotion(&pointerUpMotionArgs);
     // The first window gets nothing and the second gets pointer up
     firstWindow->assertNoEvents();
     secondWindow->consumeMotionPointerUp(1);
 
     // Send up event to the second window
-    NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+    NotifyMotionArgs upMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
     mDispatcher->notifyMotion(&upMotionArgs);
     // The first window gets nothing and the second gets up
     firstWindow->assertNoEvents();
@@ -1579,15 +1667,15 @@
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
 
     // Create a non touch modal window that supports split touch
-    sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
-            "First Window", ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> firstWindow =
+            new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
     firstWindow->setFrame(Rect(0, 0, 600, 400));
     firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
                           InputWindowInfo::Flag::SPLIT_TOUCH);
 
     // Create a non touch modal window that supports split touch
-    sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
-            "Second Window", ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> secondWindow =
+            new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
     secondWindow->setFrame(Rect(0, 400, 600, 800));
     secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
                            InputWindowInfo::Flag::SPLIT_TOUCH);
@@ -1599,17 +1687,20 @@
     PointF pointInSecond = {300, 600};
 
     // Send down to the first window
-    NotifyMotionArgs firstDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst});
+    NotifyMotionArgs firstDownMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {pointInFirst});
     mDispatcher->notifyMotion(&firstDownMotionArgs);
     // Only the first window should get the down event
     firstWindow->consumeMotionDown();
     secondWindow->assertNoEvents();
 
     // Send down to the second window
-    NotifyMotionArgs secondDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN
-            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst, pointInSecond});
+    NotifyMotionArgs secondDownMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
+                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {pointInFirst, pointInSecond});
     mDispatcher->notifyMotion(&secondDownMotionArgs);
     // The first window gets a move and the second a down
     firstWindow->consumeMotionMove();
@@ -1622,17 +1713,20 @@
     secondWindow->consumeMotionPointerDown(1);
 
     // Send pointer up to the second window
-    NotifyMotionArgs pointerUpMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP
-            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst, pointInSecond});
+    NotifyMotionArgs pointerUpMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
+                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {pointInFirst, pointInSecond});
     mDispatcher->notifyMotion(&pointerUpMotionArgs);
     // The first window gets nothing and the second gets pointer up
     firstWindow->assertNoEvents();
     secondWindow->consumeMotionPointerUp(1);
 
     // Send up event to the second window
-    NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+    NotifyMotionArgs upMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
     mDispatcher->notifyMotion(&upMotionArgs);
     // The first window gets nothing and the second gets up
     firstWindow->assertNoEvents();
@@ -1693,12 +1787,77 @@
     window->assertNoEvents(); // Key event or focus event will not be received
 }
 
+TEST_F(InputDispatcherTest, PointerCancel_SendCancelWhenSplitTouch) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+    // Create first non touch modal window that supports split touch
+    sp<FakeWindowHandle> firstWindow =
+            new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+    firstWindow->setFrame(Rect(0, 0, 600, 400));
+    firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+                          InputWindowInfo::Flag::SPLIT_TOUCH);
+
+    // Create second non touch modal window that supports split touch
+    sp<FakeWindowHandle> secondWindow =
+            new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT);
+    secondWindow->setFrame(Rect(0, 400, 600, 800));
+    secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+                           InputWindowInfo::Flag::SPLIT_TOUCH);
+
+    // Add the windows to the dispatcher
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+
+    PointF pointInFirst = {300, 200};
+    PointF pointInSecond = {300, 600};
+
+    // Send down to the first window
+    NotifyMotionArgs firstDownMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {pointInFirst});
+    mDispatcher->notifyMotion(&firstDownMotionArgs);
+    // Only the first window should get the down event
+    firstWindow->consumeMotionDown();
+    secondWindow->assertNoEvents();
+
+    // Send down to the second window
+    NotifyMotionArgs secondDownMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN |
+                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {pointInFirst, pointInSecond});
+    mDispatcher->notifyMotion(&secondDownMotionArgs);
+    // The first window gets a move and the second a down
+    firstWindow->consumeMotionMove();
+    secondWindow->consumeMotionDown();
+
+    // Send pointer cancel to the second window
+    NotifyMotionArgs pointerUpMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP |
+                                       (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                               AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {pointInFirst, pointInSecond});
+    pointerUpMotionArgs.flags |= AMOTION_EVENT_FLAG_CANCELED;
+    mDispatcher->notifyMotion(&pointerUpMotionArgs);
+    // The first window gets move and the second gets cancel.
+    firstWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
+    secondWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
+
+    // Send up event.
+    NotifyMotionArgs upMotionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&upMotionArgs);
+    // The first window gets up and the second gets nothing.
+    firstWindow->consumeMotionUp();
+    secondWindow->assertNoEvents();
+}
+
 class FakeMonitorReceiver {
 public:
     FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
                         int32_t displayId, bool isGestureMonitor = false) {
         base::Result<std::unique_ptr<InputChannel>> channel =
-                dispatcher->createInputMonitor(displayId, isGestureMonitor, name);
+                dispatcher->createInputMonitor(displayId, isGestureMonitor, name, MONITOR_PID);
         mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
     }
 
@@ -1959,6 +2118,50 @@
     EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
 }
 
+TEST_F(InputDispatcherTest, NonPointerMotionEvent_JoystickNotTransformed) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+    const std::string name = window->getName();
+
+    // Window gets transformed by offset values.
+    window->setWindowOffset(500.0f, 500.0f);
+
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+    window->setFocusable(true);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    // First, we set focused window so that focusedWindowHandle is not null.
+    setFocusedWindow(window);
+
+    // Second, we consume focus event if it is right or wrong according to onFocusChangedLocked.
+    window->consumeFocusEvent(true);
+
+    NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE,
+                                                     AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // Third, we consume motion event.
+    InputEvent* event = window->consume();
+    ASSERT_NE(event, nullptr);
+    ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
+            << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION)
+            << " event, got " << inputEventTypeToString(event->getType()) << " event";
+
+    const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+    EXPECT_EQ(AINPUT_EVENT_TYPE_MOTION, motionEvent.getAction());
+
+    float expectedX = motionArgs.pointerCoords[0].getX();
+    float expectedY = motionArgs.pointerCoords[0].getY();
+
+    // Finally we test if the axis values from the final motion event are not transformed
+    EXPECT_EQ(expectedX, motionEvent.getX(0)) << "expected " << expectedX << " for x coord of "
+                                              << name.c_str() << ", got " << motionEvent.getX(0);
+    EXPECT_EQ(expectedY, motionEvent.getY(0)) << "expected " << expectedY << " for y coord of "
+                                              << name.c_str() << ", got " << motionEvent.getY(0);
+}
+
 /**
  * Ensure that separate calls to sign the same data are generating the same key.
  * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
@@ -2161,6 +2364,72 @@
     window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
 }
 
+/**
+ * Launch two windows, with different owners. One window (slipperyExitWindow) has Flag::SLIPPERY,
+ * and overlaps the other window, slipperyEnterWindow. The window 'slipperyExitWindow' is on top
+ * of the 'slipperyEnterWindow'.
+ *
+ * Inject touch down into the top window. Upon receipt of the DOWN event, move the window in such
+ * a way so that the touched location is no longer covered by the top window.
+ *
+ * Next, inject a MOVE event. Because the top window already moved earlier, this event is now
+ * positioned over the bottom (slipperyEnterWindow) only. And because the top window had
+ * Flag::SLIPPERY, this will cause the top window to lose the touch event (it will receive
+ * ACTION_CANCEL instead), and the bottom window will receive a newly generated gesture (starting
+ * with ACTION_DOWN).
+ * Thus, the touch has been transferred from the top window into the bottom window, because the top
+ * window moved itself away from the touched location and had Flag::SLIPPERY.
+ *
+ * Even though the top window moved away from the touched location, it is still obscuring the bottom
+ * window. It's just not obscuring it at the touched location. That means, FLAG_WINDOW_IS_PARTIALLY_
+ * OBSCURED should be set for the MotionEvent that reaches the bottom window.
+ *
+ * In this test, we ensure that the event received by the bottom window has
+ * FLAG_WINDOW_IS_PARTIALLY_OBSCURED.
+ */
+TEST_F(InputDispatcherTest, SlipperyWindow_SetsFlagPartiallyObscured) {
+    constexpr int32_t SLIPPERY_PID = INJECTOR_PID + 1;
+    constexpr int32_t SLIPPERY_UID = INJECTOR_UID + 1;
+
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    sp<FakeWindowHandle> slipperyExitWindow =
+            new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+    slipperyExitWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL |
+                                 InputWindowInfo::Flag::SLIPPERY);
+    // Make sure this one overlaps the bottom window
+    slipperyExitWindow->setFrame(Rect(25, 25, 75, 75));
+    // Change the owner uid/pid of the window so that it is considered to be occluding the bottom
+    // one. Windows with the same owner are not considered to be occluding each other.
+    slipperyExitWindow->setOwnerInfo(SLIPPERY_PID, SLIPPERY_UID);
+
+    sp<FakeWindowHandle> slipperyEnterWindow =
+            new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+    slipperyExitWindow->setFrame(Rect(0, 0, 100, 100));
+
+    mDispatcher->setInputWindows(
+            {{ADISPLAY_ID_DEFAULT, {slipperyExitWindow, slipperyEnterWindow}}});
+
+    // Use notifyMotion instead of injecting to avoid dealing with injection permissions
+    NotifyMotionArgs args = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                               ADISPLAY_ID_DEFAULT, {{50, 50}});
+    mDispatcher->notifyMotion(&args);
+    slipperyExitWindow->consumeMotionDown();
+    slipperyExitWindow->setFrame(Rect(70, 70, 100, 100));
+    mDispatcher->setInputWindows(
+            {{ADISPLAY_ID_DEFAULT, {slipperyExitWindow, slipperyEnterWindow}}});
+
+    args = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                              ADISPLAY_ID_DEFAULT, {{51, 51}});
+    mDispatcher->notifyMotion(&args);
+
+    slipperyExitWindow->consumeMotionCancel();
+
+    slipperyEnterWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT,
+                                           AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
+}
+
 class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
 protected:
     static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
@@ -2308,8 +2577,8 @@
         InputDispatcherTest::SetUp();
 
         application1 = std::make_shared<FakeApplicationHandle>();
-        windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1",
-                ADISPLAY_ID_DEFAULT);
+        windowInPrimary =
+                new FakeWindowHandle(application1, mDispatcher, "D_1", ADISPLAY_ID_DEFAULT);
 
         // Set focus window for primary display, but focused display would be second one.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
@@ -2319,8 +2588,8 @@
         windowInPrimary->consumeFocusEvent(true);
 
         application2 = std::make_shared<FakeApplicationHandle>();
-        windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2",
-                SECOND_DISPLAY_ID);
+        windowInSecondary =
+                new FakeWindowHandle(application2, mDispatcher, "D_2", SECOND_DISPLAY_ID);
         // Set focus to second display window.
         // Set focus display to second one.
         mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
@@ -2431,7 +2700,7 @@
 
 // Test per-display input monitors for key event.
 TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) {
-    //Input monitor per display.
+    // Input monitor per display.
     FakeMonitorReceiver monitorInPrimary =
             FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
     FakeMonitorReceiver monitorInSecondary =
@@ -2470,11 +2739,11 @@
     void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
         NotifyMotionArgs motionArgs;
 
-        motionArgs = generateMotionArgs(
-                AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, displayId);
+        motionArgs =
+                generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, displayId);
         mDispatcher->notifyMotion(&motionArgs);
-        motionArgs = generateMotionArgs(
-                AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
+        motionArgs =
+                generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
         mDispatcher->notifyMotion(&motionArgs);
         ASSERT_TRUE(mDispatcher->waitForIdle());
         if (expectToBeFiltered) {
@@ -2542,8 +2811,8 @@
 
         std::shared_ptr<FakeApplicationHandle> application =
                 std::make_shared<FakeApplicationHandle>();
-        mUnfocusedWindow = new FakeWindowHandle(application, mDispatcher, "Top",
-                ADISPLAY_ID_DEFAULT);
+        mUnfocusedWindow =
+                new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
         mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
         // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
         // window.
@@ -2618,8 +2887,7 @@
 // Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
 // DOWN on the window that already has focus. Ensure no window received the
 // onPointerDownOutsideFocus callback.
-TEST_F(InputDispatcherOnPointerDownOutsideFocus,
-        OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                FOCUSED_WINDOW_TOUCH_POINT))
@@ -3824,4 +4092,88 @@
     // window gets the pending key event
     mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
 }
+
+class InputDispatcherPointerCaptureTests : public InputDispatcherTest {
+protected:
+    std::shared_ptr<FakeApplicationHandle> mApp;
+    sp<FakeWindowHandle> mWindow;
+    sp<FakeWindowHandle> mSecondWindow;
+
+    void SetUp() override {
+        InputDispatcherTest::SetUp();
+        mApp = std::make_shared<FakeApplicationHandle>();
+        mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+        mWindow->setFocusable(true);
+        mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
+        mSecondWindow->setFocusable(true);
+
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+
+        setFocusedWindow(mWindow);
+        mWindow->consumeFocusEvent(true);
+    }
+
+    void notifyPointerCaptureChanged(bool enabled) {
+        const NotifyPointerCaptureChangedArgs args = generatePointerCaptureChangedArgs(enabled);
+        mDispatcher->notifyPointerCaptureChanged(&args);
+    }
+
+    void requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window, bool enabled) {
+        mDispatcher->requestPointerCapture(window->getToken(), enabled);
+        mFakePolicy->waitForSetPointerCapture(enabled);
+        notifyPointerCaptureChanged(enabled);
+        window->consumeCaptureEvent(enabled);
+    }
+};
+
+TEST_F(InputDispatcherPointerCaptureTests, EnablePointerCaptureWhenFocused) {
+    // Ensure that capture cannot be obtained for unfocused windows.
+    mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true);
+    mFakePolicy->assertSetPointerCaptureNotCalled();
+    mSecondWindow->assertNoEvents();
+
+    // Ensure that capture can be enabled from the focus window.
+    requestAndVerifyPointerCapture(mWindow, true);
+
+    // Ensure that capture cannot be disabled from a window that does not have capture.
+    mDispatcher->requestPointerCapture(mSecondWindow->getToken(), false);
+    mFakePolicy->assertSetPointerCaptureNotCalled();
+
+    // Ensure that capture can be disabled from the window with capture.
+    requestAndVerifyPointerCapture(mWindow, false);
+}
+
+TEST_F(InputDispatcherPointerCaptureTests, DisablesPointerCaptureAfterWindowLosesFocus) {
+    requestAndVerifyPointerCapture(mWindow, true);
+
+    setFocusedWindow(mSecondWindow);
+
+    // Ensure that the capture disabled event was sent first.
+    mWindow->consumeCaptureEvent(false);
+    mWindow->consumeFocusEvent(false);
+    mSecondWindow->consumeFocusEvent(true);
+    mFakePolicy->waitForSetPointerCapture(false);
+
+    // Ensure that additional state changes from InputReader are not sent to the window.
+    notifyPointerCaptureChanged(false);
+    notifyPointerCaptureChanged(true);
+    notifyPointerCaptureChanged(false);
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+    mFakePolicy->assertSetPointerCaptureNotCalled();
+}
+
+TEST_F(InputDispatcherPointerCaptureTests, UnexpectedStateChangeDisablesPointerCapture) {
+    requestAndVerifyPointerCapture(mWindow, true);
+
+    // InputReader unexpectedly disables and enables pointer capture.
+    notifyPointerCaptureChanged(false);
+    notifyPointerCaptureChanged(true);
+
+    // Ensure that Pointer Capture is disabled.
+    mWindow->consumeCaptureEvent(false);
+    mWindow->assertNoEvents();
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index c26a389..6a06c9e 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -22,6 +22,7 @@
 #include <InputReaderFactory.h>
 #include <KeyboardInputMapper.h>
 #include <MultiTouchInputMapper.h>
+#include <SensorInputMapper.h>
 #include <SingleTouchInputMapper.h>
 #include <SwitchInputMapper.h>
 #include <TestInputListener.h>
@@ -364,6 +365,11 @@
         uint32_t flags;
     };
 
+    struct SensorInfo {
+        InputDeviceSensorType sensorType;
+        int32_t sensorDataIndex;
+    };
+
     struct Device {
         InputDeviceIdentifier identifier;
         Flags<InputDeviceClass> classes;
@@ -377,6 +383,8 @@
         KeyedVector<int32_t, KeyInfo> keysByScanCode;
         KeyedVector<int32_t, KeyInfo> keysByUsageCode;
         KeyedVector<int32_t, bool> leds;
+        std::unordered_map<int32_t, SensorInfo> sensorsByAbsCode;
+        BitArray<MSC_MAX> mscBitmask;
         std::vector<VirtualKeyDefinition> virtualKeys;
         bool enabled;
 
@@ -535,6 +543,22 @@
         device->leds.add(led, initialState);
     }
 
+    void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType,
+                       int32_t sensorDataIndex) {
+        Device* device = getDevice(deviceId);
+        SensorInfo info;
+        info.sensorType = sensorType;
+        info.sensorDataIndex = sensorDataIndex;
+        device->sensorsByAbsCode.emplace(absCode, info);
+    }
+
+    void setMscEvent(int32_t deviceId, int32_t mscEvent) {
+        Device* device = getDevice(deviceId);
+        typename BitArray<MSC_MAX>::Buffer buffer;
+        buffer[mscEvent / 32] = 1 << mscEvent % 32;
+        device->mscBitmask.loadFromBuffer(buffer);
+    }
+
     bool getLedState(int32_t deviceId, int32_t led) {
         Device* device = getDevice(deviceId);
         return device->leds.valueFor(led);
@@ -630,6 +654,14 @@
 
     bool hasInputProperty(int32_t, int) const override { return false; }
 
+    bool hasMscEvent(int32_t deviceId, int mscEvent) const override final {
+        Device* device = getDevice(deviceId);
+        if (device) {
+            return mscEvent >= 0 && mscEvent <= MSC_MAX ? device->mscBitmask.test(mscEvent) : false;
+        }
+        return false;
+    }
+
     status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
                     int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override {
         Device* device = getDevice(deviceId);
@@ -669,6 +701,20 @@
 
     status_t mapAxis(int32_t, int32_t, AxisInfo*) const override { return NAME_NOT_FOUND; }
 
+    base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t deviceId,
+                                                                      int32_t absCode) {
+        Device* device = getDevice(deviceId);
+        if (!device) {
+            return Errorf("Sensor device not found.");
+        }
+        auto it = device->sensorsByAbsCode.find(absCode);
+        if (it == device->sensorsByAbsCode.end()) {
+            return Errorf("Sensor map not found.");
+        }
+        const SensorInfo& info = it->second;
+        return std::make_pair(info.sensorType, info.sensorDataIndex);
+    }
+
     void setExcludedDevices(const std::vector<std::string>& devices) override {
         mExcludedDevices = devices;
     }
@@ -1435,6 +1481,32 @@
     ASSERT_EQ(1U, mReader->getInputDevices().size());
 }
 
+TEST_F(InputReaderTest, GetMergedInputDevicesEnabled) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1};
+    // Add two subdevices to device
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+    // Must add at least one mapper or the device will be ignored!
+    device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD);
+    device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD);
+
+    // Push same device instance for next device to be added, so they'll have same identifier.
+    mReader->pushNextDevice(device);
+    mReader->pushNextDevice(device);
+    // Sensor device is initially disabled
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1",
+                                      InputDeviceClass::KEYBOARD | InputDeviceClass::SENSOR,
+                                      nullptr));
+    // Device is disabled because the only sub device is a sensor device and disabled initially.
+    ASSERT_FALSE(mFakeEventHub->isDeviceEnabled(eventHubIds[0]));
+    ASSERT_FALSE(device->isEnabled());
+    ASSERT_NO_FATAL_FAILURE(
+            addDevice(eventHubIds[1], "fake2", InputDeviceClass::KEYBOARD, nullptr));
+    // The merged device is enabled if any sub device is enabled
+    ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[1]));
+    ASSERT_TRUE(device->isEnabled());
+}
+
 TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
     constexpr Flags<InputDeviceClass> deviceClass(InputDeviceClass::KEYBOARD);
@@ -2574,6 +2646,159 @@
     ASSERT_TRUE(mapper.isVibrating());
 }
 
+// --- SensorInputMapperTest ---
+
+class SensorInputMapperTest : public InputMapperTest {
+protected:
+    static const int32_t ACCEL_RAW_MIN;
+    static const int32_t ACCEL_RAW_MAX;
+    static const int32_t ACCEL_RAW_FUZZ;
+    static const int32_t ACCEL_RAW_FLAT;
+    static const int32_t ACCEL_RAW_RESOLUTION;
+
+    static const int32_t GYRO_RAW_MIN;
+    static const int32_t GYRO_RAW_MAX;
+    static const int32_t GYRO_RAW_FUZZ;
+    static const int32_t GYRO_RAW_FLAT;
+    static const int32_t GYRO_RAW_RESOLUTION;
+
+    static const float GRAVITY_MS2_UNIT;
+    static const float DEGREE_RADIAN_UNIT;
+
+    void prepareAccelAxes();
+    void prepareGyroAxes();
+    void setAccelProperties();
+    void setGyroProperties();
+    void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::SENSOR); }
+};
+
+const int32_t SensorInputMapperTest::ACCEL_RAW_MIN = -32768;
+const int32_t SensorInputMapperTest::ACCEL_RAW_MAX = 32768;
+const int32_t SensorInputMapperTest::ACCEL_RAW_FUZZ = 16;
+const int32_t SensorInputMapperTest::ACCEL_RAW_FLAT = 0;
+const int32_t SensorInputMapperTest::ACCEL_RAW_RESOLUTION = 8192;
+
+const int32_t SensorInputMapperTest::GYRO_RAW_MIN = -2097152;
+const int32_t SensorInputMapperTest::GYRO_RAW_MAX = 2097152;
+const int32_t SensorInputMapperTest::GYRO_RAW_FUZZ = 16;
+const int32_t SensorInputMapperTest::GYRO_RAW_FLAT = 0;
+const int32_t SensorInputMapperTest::GYRO_RAW_RESOLUTION = 1024;
+
+const float SensorInputMapperTest::GRAVITY_MS2_UNIT = 9.80665f;
+const float SensorInputMapperTest::DEGREE_RADIAN_UNIT = 0.0174533f;
+
+void SensorInputMapperTest::prepareAccelAxes() {
+    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ,
+                                   ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION);
+    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ,
+                                   ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION);
+    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Z, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ,
+                                   ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION);
+}
+
+void SensorInputMapperTest::prepareGyroAxes() {
+    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RX, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ,
+                                   GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION);
+    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RY, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ,
+                                   GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION);
+    mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RZ, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ,
+                                   GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION);
+}
+
+void SensorInputMapperTest::setAccelProperties() {
+    mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 0, InputDeviceSensorType::ACCELEROMETER,
+                                 /* sensorDataIndex */ 0);
+    mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 1, InputDeviceSensorType::ACCELEROMETER,
+                                 /* sensorDataIndex */ 1);
+    mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 2, InputDeviceSensorType::ACCELEROMETER,
+                                 /* sensorDataIndex */ 2);
+    mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP);
+    addConfigurationProperty("sensor.accelerometer.reportingMode", "0");
+    addConfigurationProperty("sensor.accelerometer.maxDelay", "100000");
+    addConfigurationProperty("sensor.accelerometer.minDelay", "5000");
+    addConfigurationProperty("sensor.accelerometer.power", "1.5");
+}
+
+void SensorInputMapperTest::setGyroProperties() {
+    mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 3, InputDeviceSensorType::GYROSCOPE,
+                                 /* sensorDataIndex */ 0);
+    mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 4, InputDeviceSensorType::GYROSCOPE,
+                                 /* sensorDataIndex */ 1);
+    mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 5, InputDeviceSensorType::GYROSCOPE,
+                                 /* sensorDataIndex */ 2);
+    mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP);
+    addConfigurationProperty("sensor.gyroscope.reportingMode", "0");
+    addConfigurationProperty("sensor.gyroscope.maxDelay", "100000");
+    addConfigurationProperty("sensor.gyroscope.minDelay", "5000");
+    addConfigurationProperty("sensor.gyroscope.power", "0.8");
+}
+
+TEST_F(SensorInputMapperTest, GetSources) {
+    SensorInputMapper& mapper = addMapperAndConfigure<SensorInputMapper>();
+
+    ASSERT_EQ(static_cast<uint32_t>(AINPUT_SOURCE_SENSOR), mapper.getSources());
+}
+
+TEST_F(SensorInputMapperTest, ProcessAccelerometerSensor) {
+    setAccelProperties();
+    prepareAccelAxes();
+    SensorInputMapper& mapper = addMapperAndConfigure<SensorInputMapper>();
+
+    ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::ACCELEROMETER,
+                                    std::chrono::microseconds(10000),
+                                    std::chrono::microseconds(0)));
+    ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID));
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, 20000);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, -20000);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Z, 40000);
+    process(mapper, ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+    NotifySensorArgs args;
+    std::vector<float> values = {20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT,
+                                 -20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT,
+                                 40000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT};
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySensorWasCalled(&args));
+    ASSERT_EQ(args.source, AINPUT_SOURCE_SENSOR);
+    ASSERT_EQ(args.deviceId, DEVICE_ID);
+    ASSERT_EQ(args.sensorType, InputDeviceSensorType::ACCELEROMETER);
+    ASSERT_EQ(args.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH);
+    ASSERT_EQ(args.hwTimestamp, ARBITRARY_TIME);
+    ASSERT_EQ(args.values, values);
+    mapper.flushSensor(InputDeviceSensorType::ACCELEROMETER);
+}
+
+TEST_F(SensorInputMapperTest, ProcessGyroscopeSensor) {
+    setGyroProperties();
+    prepareGyroAxes();
+    SensorInputMapper& mapper = addMapperAndConfigure<SensorInputMapper>();
+
+    ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::GYROSCOPE,
+                                    std::chrono::microseconds(10000),
+                                    std::chrono::microseconds(0)));
+    ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID));
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RX, 20000);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RY, -20000);
+    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RZ, 40000);
+    process(mapper, ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+    NotifySensorArgs args;
+    std::vector<float> values = {20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT,
+                                 -20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT,
+                                 40000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT};
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySensorWasCalled(&args));
+    ASSERT_EQ(args.source, AINPUT_SOURCE_SENSOR);
+    ASSERT_EQ(args.deviceId, DEVICE_ID);
+    ASSERT_EQ(args.sensorType, InputDeviceSensorType::GYROSCOPE);
+    ASSERT_EQ(args.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH);
+    ASSERT_EQ(args.hwTimestamp, ARBITRARY_TIME);
+    ASSERT_EQ(args.values, values);
+    mapper.flushSensor(InputDeviceSensorType::GYROSCOPE);
+}
+
 // --- KeyboardInputMapperTest ---
 
 class KeyboardInputMapperTest : public InputMapperTest {
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 1050ab8..295c6e3 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -80,6 +80,12 @@
                                            "Expected notifySwitch() to have been called."));
 }
 
+void TestInputListener::assertNotifySensorWasCalled(NotifySensorArgs* outEventArgs) {
+    ASSERT_NO_FATAL_FAILURE(
+            assertCalled<NotifySensorArgs>(outEventArgs,
+                                           "Expected notifySensor() to have been called."));
+}
+
 void TestInputListener::assertNotifyCaptureWasCalled(
         NotifyPointerCaptureChangedArgs* outEventArgs) {
     ASSERT_NO_FATAL_FAILURE(
@@ -155,4 +161,8 @@
     notify<NotifyPointerCaptureChangedArgs>(args);
 }
 
+void TestInputListener::notifySensor(const NotifySensorArgs* args) {
+    notify<NotifySensorArgs>(args);
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 887d4ea..e54350a 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -55,6 +55,7 @@
     void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
 
     void assertNotifyCaptureWasCalled(NotifyPointerCaptureChangedArgs* outEventArgs = nullptr);
+    void assertNotifySensorWasCalled(NotifySensorArgs* outEventArgs = nullptr);
 
 private:
     template <class NotifyArgsType>
@@ -76,6 +77,8 @@
 
     virtual void notifySwitch(const NotifySwitchArgs* args) override;
 
+    virtual void notifySensor(const NotifySensorArgs* args) override;
+
     virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
 
     std::mutex mLock;
@@ -88,6 +91,7 @@
                std::vector<NotifyKeyArgs>,                   //
                std::vector<NotifyMotionArgs>,                //
                std::vector<NotifySwitchArgs>,                //
+               std::vector<NotifySensorArgs>,                //
                std::vector<NotifyPointerCaptureChangedArgs>> //
             mQueues GUARDED_BY(mLock);
 };
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index fdb8aaf..a7cd258 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -3,6 +3,7 @@
     cflags: [
         "-Wall",
         "-Werror",
+        "-Wextra",
         "-Wformat",
         "-Wthread-safety",
         "-Wunused",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index fa75ffa..6561707 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -389,6 +389,20 @@
     mBufferInfo.mFrameLatencyNeeded = true;
 }
 
+bool BufferLayer::frameIsEarly(nsecs_t expectedPresentTime) const {
+    // TODO(b/169901895): kEarlyLatchVsyncThreshold should be based on the
+    // vsync period. We can do this change as soon as ag/13100772 is merged.
+    constexpr static std::chrono::nanoseconds kEarlyLatchVsyncThreshold = 5ms;
+
+    const auto presentTime = nextPredictedPresentTime();
+    if (std::abs(presentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold.count()) {
+        return false;
+    }
+
+    return presentTime >= expectedPresentTime &&
+            presentTime - expectedPresentTime >= kEarlyLatchVsyncThreshold.count();
+}
+
 bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
                               nsecs_t expectedPresentTime) {
     ATRACE_CALL();
@@ -421,6 +435,12 @@
         return false;
     }
 
+    if (frameIsEarly(expectedPresentTime)) {
+        ATRACE_NAME("frameIsEarly()");
+        mFlinger->signalLayerUpdate();
+        return false;
+    }
+
     // If the head buffer's acquire fence hasn't signaled yet, return and
     // try again later
     if (!fenceHasSignaled()) {
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 63dfe5f..5cd9a7c 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -224,6 +224,18 @@
     std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
 
     FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
+
+    // Returns true if the next frame is considered too early to present
+    // at the given expectedPresentTime
+    bool frameIsEarly(nsecs_t expectedPresentTime) const;
+
+    // Returns the predicted present time of the next frame if available or
+    // 0 otherwise.
+    virtual nsecs_t nextPredictedPresentTime() const = 0;
+
+    // The amount of time SF can delay a frame if it is considered early based
+    // on the VsyncModulator::VsyncConfig::appWorkDuration
+    static constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 325ecfe..04cec4f 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -17,6 +17,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #undef LOG_TAG
 #define LOG_TAG "BufferQueueLayer"
@@ -222,6 +223,21 @@
     return mQueuedFrames > 0;
 }
 
+nsecs_t BufferQueueLayer::nextPredictedPresentTime() const {
+    Mutex::Autolock lock(mQueueItemLock);
+    if (mQueueItems.empty()) {
+        return 0;
+    }
+
+    const auto& bufferData = mQueueItems[0];
+
+    if (!bufferData.item.mIsAutoTimestamp || !bufferData.surfaceFrame) {
+        return 0;
+    }
+
+    return bufferData.surfaceFrame->getPredictions().presentTime;
+}
+
 status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
                                           nsecs_t expectedPresentTime) {
     // This boolean is used to make sure that SurfaceFlinger's shadow copy
@@ -274,8 +290,8 @@
             mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
             mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
             if (mQueueItems[0].surfaceFrame) {
-                mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
-                                                          PresentState::Dropped);
+                mQueueItems[0].surfaceFrame->setPresentState(PresentState::Dropped);
+                mFlinger->mFrameTimeline->addSurfaceFrame(mQueueItems[0].surfaceFrame);
             }
             mQueueItems.erase(mQueueItems.begin());
             mQueuedFrames--;
@@ -290,8 +306,8 @@
             Mutex::Autolock lock(mQueueItemLock);
             for (auto& [item, surfaceFrame] : mQueueItems) {
                 if (surfaceFrame) {
-                    mFlinger->mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
-                                                              PresentState::Dropped);
+                    surfaceFrame->setPresentState(PresentState::Dropped);
+                    mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
                 }
             }
             mQueueItems.clear();
@@ -321,8 +337,8 @@
             mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
             mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
             if (mQueueItems[0].surfaceFrame) {
-                mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
-                                                          PresentState::Dropped);
+                mQueueItems[0].surfaceFrame->setPresentState(PresentState::Dropped);
+                mFlinger->mFrameTimeline->addSurfaceFrame(mQueueItems[0].surfaceFrame);
             }
             mQueueItems.erase(mQueueItems.begin());
             mQueuedFrames--;
@@ -336,8 +352,9 @@
         if (mQueueItems[0].surfaceFrame) {
             mQueueItems[0].surfaceFrame->setAcquireFenceTime(
                     mQueueItems[0].item.mFenceTime->getSignalTime());
-            mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
-                                                      PresentState::Presented);
+            mQueueItems[0].surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime);
+            mFlinger->mFrameTimeline->addSurfaceFrame(mQueueItems[0].surfaceFrame);
+            mLastLatchTime = latchTime;
         }
         mQueueItems.erase(mQueueItems.begin());
     }
@@ -436,8 +453,9 @@
         }
 
         auto surfaceFrame =
-                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName,
-                                                                     mName, mFrameTimelineVsyncId);
+                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mFrameTimelineVsyncId,
+                                                                     mOwnerPid, mOwnerUid, mName,
+                                                                     mName);
         surfaceFrame->setActualQueueTime(systemTime());
 
         mQueueItems.push_back({item, surfaceFrame});
@@ -475,8 +493,9 @@
         }
 
         auto surfaceFrame =
-                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName,
-                                                                     mName, mFrameTimelineVsyncId);
+                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mFrameTimelineVsyncId,
+                                                                     mOwnerPid, mOwnerUid, mName,
+                                                                     mName);
         surfaceFrame->setActualQueueTime(systemTime());
         mQueueItems[mQueueItems.size() - 1].item = item;
         mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame);
@@ -643,4 +662,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 5a6b9bc..2347d8c 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -116,6 +116,8 @@
     // Temporary - Used only for LEGACY camera mode.
     uint32_t getProducerStickyTransform() const;
 
+    nsecs_t nextPredictedPresentTime() const override;
+
     sp<BufferLayerConsumer> mConsumer;
     sp<IGraphicBufferProducer> mProducer;
 
@@ -146,6 +148,11 @@
     // a buffer to correlate the buffer with the vsync id. Can only be accessed
     // with the SF state lock held.
     std::optional<int64_t> mFrameTimelineVsyncId;
+
+    // Keeps track of the time SF latched the last buffer from this layer.
+    // Used in buffer stuffing analysis in FrameTimeline.
+    // TODO(b/176106798): Find a way to do this for BLASTBufferQueue as well.
+    nsecs_t mLastLatchTime = 0;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index b6c59cd..bca1c69 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -17,6 +17,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 //#define LOG_NDEBUG 0
 #undef LOG_TAG
@@ -341,12 +342,18 @@
 }
 
 bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
-                                 nsecs_t postTime, nsecs_t desiredPresentTime,
+                                 nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp,
                                  const client_cache_t& clientCacheId, uint64_t frameNumber) {
     ATRACE_CALL();
 
     if (mCurrentState.buffer) {
         mReleasePreviousBuffer = true;
+        if (mCurrentState.buffer != mDrawingState.buffer) {
+            // If mCurrentState has a buffer, and we are about to update again
+            // before swapping to drawing state, then the first buffer will be
+            // dropped and we should decrement the pending buffer count.
+            decrementPendingBufferCount();
+        }
     }
 
     mCurrentState.frameNumber = frameNumber;
@@ -359,13 +366,13 @@
     const int32_t layerId = getSequence();
     mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
                                       mOwnerUid, postTime);
-    desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime;
     mCurrentState.desiredPresentTime = desiredPresentTime;
+    mCurrentState.isAutoTimestamp = isAutoTimestamp;
 
-    mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime,
+    mFlinger->mScheduler->recordLayerHistory(this, isAutoTimestamp ? 0 : desiredPresentTime,
                                              LayerHistory::LayerUpdateType::Buffer);
 
-    addFrameEvent(acquireFence, postTime, desiredPresentTime);
+    addFrameEvent(acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime);
     return true;
 }
 
@@ -527,7 +534,7 @@
         return true;
     }
 
-    return mCurrentState.desiredPresentTime <= expectedPresentTime;
+    return mCurrentState.isAutoTimestamp || mCurrentState.desiredPresentTime <= expectedPresentTime;
 }
 
 bool BufferStateLayer::onPreComposition(nsecs_t refreshStartTime) {
@@ -595,6 +602,14 @@
     return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
 }
 
+nsecs_t BufferStateLayer::nextPredictedPresentTime() const {
+    if (!getDrawingState().isAutoTimestamp || !mSurfaceFrame) {
+        return 0;
+    }
+
+    return mSurfaceFrame->getPredictions().presentTime;
+}
+
 status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
                                           nsecs_t /*expectedPresentTime*/) {
     const State& s(getDrawingState());
@@ -629,6 +644,7 @@
     if (s.buffer == nullptr) {
         return BAD_VALUE;
     }
+    decrementPendingBufferCount();
 
     mPreviousBufferId = getCurrentBufferId();
     mBufferInfo.mBuffer = s.buffer;
@@ -826,7 +842,33 @@
     const Rect layerSize{getBounds()};
     return layerSize.width() != bufferWidth || layerSize.height() != bufferHeight;
 }
+
+void BufferStateLayer::incrementPendingBufferCount() {
+    mPendingBufferTransactions++;
+    tracePendingBufferCount();
+}
+
+void BufferStateLayer::decrementPendingBufferCount() {
+    mPendingBufferTransactions--;
+    tracePendingBufferCount();
+}
+
+void BufferStateLayer::tracePendingBufferCount() {
+    ATRACE_INT(mBlastTransactionName.c_str(), mPendingBufferTransactions);
+}
+
+uint32_t BufferStateLayer::doTransaction(uint32_t flags) {
+    if (mDrawingState.buffer != nullptr && mDrawingState.buffer != mBufferInfo.mBuffer) {
+        // If we are about to update mDrawingState.buffer but it has not yet latched
+        // then we will drop a buffer and should decrement the pending buffer count.
+        // This logic may not work perfectly in the face of a BufferStateLayer being the
+        // deferred side of a deferred transaction, but we don't expect this use case.
+        decrementPendingBufferCount();
+    }
+    return Layer::doTransaction(flags);
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 69b27e4..6414e6b 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -70,8 +70,8 @@
     bool setCrop(const Rect& crop) override;
     bool setFrame(const Rect& frame) override;
     bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
-                   nsecs_t desiredPresentTime, const client_cache_t& clientCacheId,
-                   uint64_t frameNumber) override;
+                   nsecs_t desiredPresentTime, bool isAutoTimestamp,
+                   const client_cache_t& clientCacheId, uint64_t frameNumber) override;
     bool setAcquireFence(const sp<Fence>& fence) override;
     bool setDataspace(ui::Dataspace dataspace) override;
     bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -112,6 +112,11 @@
     bool onPreComposition(nsecs_t refreshStartTime) override;
     uint32_t getEffectiveScalingMode() const override;
 
+    // See mPendingBufferTransactions
+    void incrementPendingBufferCount() override;
+    void decrementPendingBufferCount();
+    uint32_t doTransaction(uint32_t flags) override;
+
 protected:
     void gatherBufferInfo() override;
     uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
@@ -119,6 +124,7 @@
 
 private:
     friend class SlotGenerationTest;
+    inline void tracePendingBufferCount();
 
     bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
                                  nsecs_t requestedPresentTime);
@@ -146,6 +152,8 @@
 
     bool bufferNeedsFiltering() const override;
 
+    nsecs_t nextPredictedPresentTime() const override;
+
     static const std::array<float, 16> IDENTITY_MATRIX;
 
     std::unique_ptr<renderengine::Image> mTextureImage;
@@ -163,6 +171,18 @@
 
     std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications;
 
+    const std::string mBlastTransactionName{"BufferTX - " + mName};
+    // This integer is incremented everytime a buffer arrives at the server for this layer,
+    // and decremented when a buffer is dropped or latched. When changed the integer is exported
+    // to systrace with ATRACE_INT and mBlastTransactionName. This way when debugging perf it is
+    // possible to see when a buffer arrived at the server, and in which frame it latched.
+    //
+    // You can understand the trace this way:
+    //     - If the integer increases, a buffer arrived at the server.
+    //     - If the integer decreases in latchBuffer, that buffer was latched
+    //     - If the integer decreases in setBuffer or doTransaction, a buffer was dropped
+    uint64_t mPendingBufferTransactions{0};
+
     // TODO(marissaw): support sticky transform for LEGACY camera mode
 
     class HwcSlotGenerator : public ClientCache::ErasedRecipient {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
index 67e6deb..df44e75 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
@@ -22,11 +22,12 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include <ui/GraphicTypes.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 namespace android {
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
index 7eb8eb1..1136e3d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
@@ -23,11 +23,12 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include <ui/GraphicTypes.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 #include <ui/HdrCapabilities.h>
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 26f7f68..018a687 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -23,11 +23,12 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include <renderengine/LayerSettings.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 5a3b9ac..c445d5b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -29,6 +29,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include <gui/BufferQueue.h>
 #include <ui/GraphicBuffer.h>
@@ -37,7 +38,7 @@
 #include "DisplayHardware/Hal.h"
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 namespace android::compositionengine {
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index aa70ef8..fb19216 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -25,12 +25,13 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include "DisplayHardware/ComposerHal.h"
 #include "DisplayHardware/DisplayIdentification.h"
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 namespace android {
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index 2864c10..aa049a8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -22,11 +22,12 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include <gui/BufferQueue.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 #include <utils/StrongPointer.h>
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 06e6a6f..8f767d3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -23,11 +23,12 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include <ui/GraphicTypes.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 #include <compositionengine/ProjectionSpace.h>
 #include <ui/Rect.h>
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index d2b38d1..9a118d3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -30,11 +30,12 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include "DisplayHardware/ComposerHal.h"
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 namespace android {
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 1befbf8..348ec39 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
 #include <cmath>
 
 #include <compositionengine/DisplayColorProfileCreationArgs.h>
@@ -1043,3 +1047,6 @@
 
 } // namespace
 } // namespace android::compositionengine
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index 87911cc..9518659 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -27,12 +27,13 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include <ui/GraphicTypes.h>
 #include "DisplayHardware/HWC2.h"
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 namespace android {
 namespace HWC2 {
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 84c027b..e64a9f1 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -22,11 +22,12 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include "DisplayHardware/HWComposer.h"
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 namespace android {
 namespace mock {
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 6ce8a6b..cd39733 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
 #include <cstdarg>
 #include <cstdint>
 
@@ -359,3 +363,6 @@
 
 } // namespace
 } // namespace android::compositionengine
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 5b66809..c756d65 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -26,6 +26,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include <android/hardware/graphics/common/1.1/types.h>
 #include <android/hardware/graphics/composer/2.4/IComposer.h>
@@ -38,7 +39,7 @@
 #include <utils/StrongPointer.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 namespace android {
 
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index e6bff04..426092d 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -26,18 +26,17 @@
 
 #include "HWC2.h"
 
+#include <android/configuration.h>
+#include <ftl/future.h>
 #include <ui/Fence.h>
 #include <ui/FloatRect.h>
 #include <ui/GraphicBuffer.h>
 
-#include <android/configuration.h>
-
-#include <inttypes.h>
 #include <algorithm>
+#include <cinttypes>
 #include <iterator>
 #include <set>
 
-#include "../Promise.h"
 #include "ComposerHal.h"
 
 namespace android {
@@ -647,7 +646,7 @@
 }
 
 std::future<Error> Display::setDisplayBrightness(float brightness) {
-    return promise::defer([composer = &mComposer, id = mId, brightness] {
+    return ftl::defer([composer = &mComposer, id = mId, brightness] {
         const auto intError = composer->setDisplayBrightness(id, brightness);
         return static_cast<Error>(intError);
     });
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 1548d18..6f3987f 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -30,6 +30,7 @@
 #include <compositionengine/Output.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <ftl/future.h>
 #include <log/log.h>
 #include <ui/DebugUtils.h>
 #include <ui/GraphicBuffer.h>
@@ -37,7 +38,6 @@
 #include <utils/Trace.h>
 
 #include "../Layer.h" // needed only for debugging
-#include "../Promise.h"
 #include "../SurfaceFlinger.h"
 #include "../SurfaceFlingerProperties.h"
 #include "ComposerHal.h"
@@ -74,6 +74,7 @@
 
 namespace hal = android::hardware::graphics::composer::hal;
 
+namespace android {
 namespace {
 
 using android::hardware::Return;
@@ -88,47 +89,46 @@
             mSequenceId(sequenceId),
             mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
 
-    android::hardware::Return<void> onHotplug(hal::HWDisplayId display,
-                                              hal::Connection conn) override {
+    Return<void> onHotplug(hal::HWDisplayId display, hal::Connection conn) override {
         mCallback->onHotplugReceived(mSequenceId, display, conn);
-        return android::hardware::Void();
+        return Void();
     }
 
-    android::hardware::Return<void> onRefresh(hal::HWDisplayId display) override {
+    Return<void> onRefresh(hal::HWDisplayId display) override {
         mCallback->onRefreshReceived(mSequenceId, display);
-        return android::hardware::Void();
+        return Void();
     }
 
-    android::hardware::Return<void> onVsync(hal::HWDisplayId display, int64_t timestamp) override {
+    Return<void> onVsync(hal::HWDisplayId display, int64_t timestamp) override {
         if (!mVsyncSwitchingSupported) {
             mCallback->onVsyncReceived(mSequenceId, display, timestamp, std::nullopt);
         } else {
             ALOGW("Unexpected onVsync callback on composer >= 2.4, ignoring.");
         }
-        return android::hardware::Void();
+        return Void();
     }
 
-    android::hardware::Return<void> onVsync_2_4(hal::HWDisplayId display, int64_t timestamp,
-                                                hal::VsyncPeriodNanos vsyncPeriodNanos) override {
+    Return<void> onVsync_2_4(hal::HWDisplayId display, int64_t timestamp,
+                             hal::VsyncPeriodNanos vsyncPeriodNanos) override {
         if (mVsyncSwitchingSupported) {
             mCallback->onVsyncReceived(mSequenceId, display, timestamp,
                                        std::make_optional(vsyncPeriodNanos));
         } else {
             ALOGW("Unexpected onVsync_2_4 callback on composer <= 2.3, ignoring.");
         }
-        return android::hardware::Void();
+        return Void();
     }
 
-    android::hardware::Return<void> onVsyncPeriodTimingChanged(
+    Return<void> onVsyncPeriodTimingChanged(
             hal::HWDisplayId display,
             const hal::VsyncPeriodChangeTimeline& updatedTimeline) override {
         mCallback->onVsyncPeriodTimingChangedReceived(mSequenceId, display, updatedTimeline);
-        return android::hardware::Void();
+        return Void();
     }
 
-    android::hardware::Return<void> onSeamlessPossible(hal::HWDisplayId display) override {
+    Return<void> onSeamlessPossible(hal::HWDisplayId display) override {
         mCallback->onSeamlessPossible(mSequenceId, display);
-        return android::hardware::Void();
+        return Void();
     }
 
 private:
@@ -139,8 +139,6 @@
 
 } // namespace
 
-namespace android {
-
 HWComposer::~HWComposer() = default;
 
 namespace impl {
@@ -295,6 +293,7 @@
                                                   hal::DisplayType::PHYSICAL);
     newDisplay->setConnected(true);
     displayData.hwcDisplay = std::move(newDisplay);
+    displayData.configs = displayData.hwcDisplay->getConfigs();
     mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
 }
 
@@ -335,14 +334,9 @@
         PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, {});
 
-    const auto& displayData = mDisplayData.at(displayId);
-    auto configs = displayData.hwcDisplay->getConfigs();
-    if (displayData.configMap.empty()) {
-        for (size_t i = 0; i < configs.size(); ++i) {
-            displayData.configMap[i] = configs[i];
-        }
-    }
-    return configs;
+    // We cache the configs when the DisplayData is created on hotplug. If the configs need to
+    // change HWC will send a hotplug event which will recreate displayData.
+    return mDisplayData.at(displayId).configs;
 }
 
 std::shared_ptr<const HWC2::Display::Config> HWComposer::getActiveConfig(
@@ -653,13 +647,13 @@
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     auto& displayData = mDisplayData[displayId];
-    if (displayData.configMap.count(configId) == 0) {
+    if (configId >= displayData.configs.size()) {
         LOG_DISPLAY_ERROR(displayId, ("Invalid config " + std::to_string(configId)).c_str());
         return BAD_INDEX;
     }
 
     auto error =
-            displayData.hwcDisplay->setActiveConfigWithConstraints(displayData.configMap[configId],
+            displayData.hwcDisplay->setActiveConfigWithConstraints(displayData.configs[configId],
                                                                    constraints, outTimeline);
     RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
     return NO_ERROR;
@@ -792,10 +786,10 @@
 
 std::future<status_t> HWComposer::setDisplayBrightness(PhysicalDisplayId displayId,
                                                        float brightness) {
-    RETURN_IF_INVALID_DISPLAY(displayId, promise::yield<status_t>(BAD_INDEX));
+    RETURN_IF_INVALID_DISPLAY(displayId, ftl::yield<status_t>(BAD_INDEX));
     auto& display = mDisplayData[displayId].hwcDisplay;
 
-    return promise::chain(display->setDisplayBrightness(brightness))
+    return ftl::chain(display->setDisplayBrightness(brightness))
             .then([displayId](hal::Error error) -> status_t {
                 if (error == hal::Error::UNSUPPORTED) {
                     RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index d8af5bf..7e1da25 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -32,8 +32,9 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 #include <ui/GraphicTypes.h>
-#pragma clang diagnostic pop
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 #include <utils/StrongPointer.h>
 #include <utils/Timers.h>
@@ -183,7 +184,6 @@
     virtual nsecs_t getRefreshTimestamp(PhysicalDisplayId) const = 0;
     virtual bool isConnected(PhysicalDisplayId) const = 0;
 
-    // Non-const because it can update configMap inside of mDisplayData
     virtual std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
             PhysicalDisplayId) const = 0;
 
@@ -319,7 +319,6 @@
     nsecs_t getRefreshTimestamp(PhysicalDisplayId) const override;
     bool isConnected(PhysicalDisplayId) const override;
 
-    // Non-const because it can update configMap inside of mDisplayData
     std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(
             PhysicalDisplayId) const override;
 
@@ -378,8 +377,7 @@
         std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
         buffer_handle_t outbufHandle = nullptr;
         sp<Fence> outbufAcquireFence = Fence::NO_FENCE;
-        mutable std::unordered_map<int32_t,
-                std::shared_ptr<const HWC2::Display::Config>> configMap;
+        std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
 
         bool validateWasSkipped;
         hal::Error presentError;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index b09f07a..a8fef04 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -26,7 +26,7 @@
 #include <cinttypes>
 #include <numeric>
 
-namespace android::frametimeline::impl {
+namespace android::frametimeline {
 
 using base::StringAppendF;
 using FrameTimelineEvent = perfetto::protos::pbzero::FrameTimelineEvent;
@@ -60,7 +60,8 @@
         StringAppendF(&result, "\t%10.2f\t|",
                       std::chrono::duration<double, std::milli>(startTime).count());
     }
-    if (actuals.endTime == 0) {
+    if (actuals.endTime <= 0) {
+        // Animation leashes can send the endTime as -1
         StringAppendF(&result, "\t\tN/A\t|");
     } else {
         std::chrono::nanoseconds endTime(actuals.endTime - baseTime);
@@ -89,124 +90,160 @@
         case PredictionState::Expired:
             return "Expired";
         case PredictionState::None:
-        default:
             return "None";
     }
 }
 
-std::string toString(JankType jankType) {
-    switch (jankType) {
-        case JankType::None:
-            return "None";
-        case JankType::Display:
-            return "Composer/Display - outside SF and App";
-        case JankType::SurfaceFlingerDeadlineMissed:
-            return "SurfaceFlinger Deadline Missed";
-        case JankType::AppDeadlineMissed:
-            return "App Deadline Missed";
-        case JankType::PredictionExpired:
-            return "Prediction Expired";
-        case JankType::SurfaceFlingerEarlyLatch:
-            return "SurfaceFlinger Early Latch";
-        default:
-            return "Unclassified";
-    }
-}
-
-std::string jankMetadataBitmaskToString(int32_t jankMetadata) {
-    std::vector<std::string> jankInfo;
-
-    if (jankMetadata & EarlyStart) {
-        jankInfo.emplace_back("Early Start");
-    } else if (jankMetadata & LateStart) {
-        jankInfo.emplace_back("Late Start");
-    }
-
-    if (jankMetadata & EarlyFinish) {
-        jankInfo.emplace_back("Early Finish");
-    } else if (jankMetadata & LateFinish) {
-        jankInfo.emplace_back("Late Finish");
-    }
-
-    if (jankMetadata & EarlyPresent) {
-        jankInfo.emplace_back("Early Present");
-    } else if (jankMetadata & LatePresent) {
-        jankInfo.emplace_back("Late Present");
-    }
-    // TODO(b/169876734): add GPU composition metadata here
-
-    if (jankInfo.empty()) {
+std::string jankTypeBitmaskToString(int32_t jankType) {
+    // TODO(b/175843808): Make this a switch case if jankType becomes an enum class
+    std::vector<std::string> janks;
+    if (jankType == JankType::None) {
         return "None";
     }
-    return std::accumulate(jankInfo.begin(), jankInfo.end(), std::string(),
+    if (jankType & JankType::DisplayHAL) {
+        janks.emplace_back("Display HAL");
+    }
+    if (jankType & JankType::SurfaceFlingerCpuDeadlineMissed) {
+        janks.emplace_back("SurfaceFlinger CPU Deadline Missed");
+    }
+    if (jankType & JankType::SurfaceFlingerGpuDeadlineMissed) {
+        janks.emplace_back("SurfaceFlinger GPU Deadline Missed");
+    }
+    if (jankType & JankType::AppDeadlineMissed) {
+        janks.emplace_back("App Deadline Missed");
+    }
+    if (jankType & JankType::PredictionError) {
+        janks.emplace_back("Prediction Error");
+    }
+    if (jankType & JankType::SurfaceFlingerScheduling) {
+        janks.emplace_back("SurfaceFlinger Scheduling");
+    }
+    if (jankType & JankType::BufferStuffing) {
+        janks.emplace_back("Buffer Stuffing");
+    }
+    if (jankType & JankType::Unknown) {
+        janks.emplace_back("Unknown jank");
+    }
+    return std::accumulate(janks.begin(), janks.end(), std::string(),
                            [](const std::string& l, const std::string& r) {
                                return l.empty() ? r : l + ", " + r;
                            });
 }
 
-FrameTimelineEvent::PresentType presentTypeToProto(int32_t jankMetadata) {
-    if (jankMetadata & EarlyPresent) {
-        return FrameTimelineEvent::PRESENT_EARLY;
+std::string toString(FramePresentMetadata presentMetadata) {
+    switch (presentMetadata) {
+        case FramePresentMetadata::OnTimePresent:
+            return "On Time Present";
+        case FramePresentMetadata::LatePresent:
+            return "Late Present";
+        case FramePresentMetadata::EarlyPresent:
+            return "Early Present";
+        case FramePresentMetadata::UnknownPresent:
+            return "Unknown Present";
     }
-    if (jankMetadata & LatePresent) {
-        return FrameTimelineEvent::PRESENT_LATE;
-    }
-    return FrameTimelineEvent::PRESENT_ON_TIME;
 }
 
-FrameTimelineEvent::JankType JankTypeToProto(JankType jankType) {
+std::string toString(FrameReadyMetadata finishMetadata) {
+    switch (finishMetadata) {
+        case FrameReadyMetadata::OnTimeFinish:
+            return "On Time Finish";
+        case FrameReadyMetadata::LateFinish:
+            return "Late Finish";
+        case FrameReadyMetadata::UnknownFinish:
+            return "Unknown Finish";
+    }
+}
+
+std::string toString(FrameStartMetadata startMetadata) {
+    switch (startMetadata) {
+        case FrameStartMetadata::OnTimeStart:
+            return "On Time Start";
+        case FrameStartMetadata::LateStart:
+            return "Late Start";
+        case FrameStartMetadata::EarlyStart:
+            return "Early Start";
+        case FrameStartMetadata::UnknownStart:
+            return "Unknown Start";
+    }
+}
+
+std::string toString(SurfaceFrame::PresentState presentState) {
+    using PresentState = SurfaceFrame::PresentState;
+    switch (presentState) {
+        case PresentState::Presented:
+            return "Presented";
+        case PresentState::Dropped:
+            return "Dropped";
+        case PresentState::Unknown:
+            return "Unknown";
+    }
+}
+
+FrameTimelineEvent::PresentType toProto(FramePresentMetadata presentMetadata) {
+    switch (presentMetadata) {
+        case FramePresentMetadata::EarlyPresent:
+            return FrameTimelineEvent::PRESENT_EARLY;
+        case FramePresentMetadata::LatePresent:
+            return FrameTimelineEvent::PRESENT_LATE;
+        case FramePresentMetadata::OnTimePresent:
+            return FrameTimelineEvent::PRESENT_ON_TIME;
+        case FramePresentMetadata::UnknownPresent:
+            return FrameTimelineEvent::PRESENT_UNSPECIFIED;
+    }
+}
+
+FrameTimelineEvent::JankType jankTypeBitmaskToProto(int32_t jankType) {
+    // TODO(b/175843808): Either make the proto a bitmask or jankType an enum class
     switch (jankType) {
         case JankType::None:
             return FrameTimelineEvent::JANK_NONE;
-        case JankType::Display:
+        case JankType::DisplayHAL:
             return FrameTimelineEvent::JANK_DISPLAY_HAL;
-        case JankType::SurfaceFlingerDeadlineMissed:
+        case JankType::SurfaceFlingerCpuDeadlineMissed:
+        case JankType::SurfaceFlingerGpuDeadlineMissed:
             return FrameTimelineEvent::JANK_SF_DEADLINE_MISSED;
         case JankType::AppDeadlineMissed:
-        case JankType::PredictionExpired:
+        case JankType::PredictionError:
             return FrameTimelineEvent::JANK_APP_DEADLINE_MISSED;
+        case JankType::SurfaceFlingerScheduling:
+            return FrameTimelineEvent::JANK_SF_SCHEDULING;
+        case JankType::BufferStuffing:
+            return FrameTimelineEvent::JANK_BUFFER_STUFFING;
         default:
+            // TODO(b/175843808): Remove default if JankType becomes an enum class
             return FrameTimelineEvent::JANK_UNKNOWN;
     }
 }
 
-int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> lock(mMutex);
-    const int64_t assignedToken = mCurrentToken++;
-    mPredictions[assignedToken] = predictions;
-    mTokens.emplace_back(std::make_pair(assignedToken, systemTime()));
-    flushTokens(systemTime());
-    return assignedToken;
-}
-
-std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    flushTokens(systemTime());
-    auto predictionsIterator = mPredictions.find(token);
-    if (predictionsIterator != mPredictions.end()) {
-        return predictionsIterator->second;
+// Returns the smallest timestamp from the set of predictions and actuals.
+nsecs_t getMinTime(PredictionState predictionState, TimelineItem predictions,
+                   TimelineItem actuals) {
+    nsecs_t minTime = std::numeric_limits<nsecs_t>::max();
+    if (predictionState == PredictionState::Valid) {
+        // Checking start time for predictions is enough because start time is always lesser than
+        // endTime and presentTime.
+        minTime = std::min(minTime, predictions.startTime);
     }
-    return {};
-}
 
-void TokenManager::flushTokens(nsecs_t flushTime) {
-    for (size_t i = 0; i < mTokens.size(); i++) {
-        if (flushTime - mTokens[i].second >= kMaxRetentionTime) {
-            mPredictions.erase(mTokens[i].first);
-            mTokens.erase(mTokens.begin() + static_cast<int>(i));
-            --i;
-        } else {
-            // Tokens are ordered by time. If i'th token is within the retention time, then the
-            // i+1'th token will also be within retention time.
-            break;
-        }
+    // Need to check startTime, endTime and presentTime for actuals because some frames might not
+    // have them set.
+    if (actuals.startTime != 0) {
+        minTime = std::min(minTime, actuals.startTime);
     }
+    if (actuals.endTime != 0) {
+        minTime = std::min(minTime, actuals.endTime);
+    }
+    if (actuals.presentTime != 0) {
+        minTime = std::min(minTime, actuals.endTime);
+    }
+    return minTime;
 }
 
 SurfaceFrame::SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
                            std::string debugName, PredictionState predictionState,
-                           frametimeline::TimelineItem&& predictions)
+                           frametimeline::TimelineItem&& predictions,
+                           std::shared_ptr<TimeStats> timeStats,
+                           JankClassificationThresholds thresholds)
       : mToken(token),
         mOwnerPid(ownerPid),
         mOwnerUid(ownerUid),
@@ -216,29 +253,8 @@
         mPredictionState(predictionState),
         mPredictions(predictions),
         mActuals({0, 0, 0}),
-        mActualQueueTime(0),
-        mJankType(JankType::None),
-        mJankMetadata(0) {}
-
-void SurfaceFrame::setPresentState(PresentState state) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mPresentState = state;
-}
-
-SurfaceFrame::PresentState SurfaceFrame::getPresentState() const {
-    std::lock_guard<std::mutex> lock(mMutex);
-    return mPresentState;
-}
-
-TimelineItem SurfaceFrame::getActuals() const {
-    std::lock_guard<std::mutex> lock(mMutex);
-    return mActuals;
-}
-
-nsecs_t SurfaceFrame::getActualQueueTime() const {
-    std::lock_guard<std::mutex> lock(mMutex);
-    return mActualQueueTime;
-}
+        mTimeStats(timeStats),
+        mJankClassificationThresholds(thresholds) {}
 
 void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
     std::lock_guard<std::mutex> lock(mMutex);
@@ -254,18 +270,13 @@
     mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime);
 }
 
-void SurfaceFrame::setActualPresentTime(nsecs_t presentTime) {
+void SurfaceFrame::setPresentState(PresentState presentState, nsecs_t lastLatchTime) {
     std::lock_guard<std::mutex> lock(mMutex);
-    mActuals.presentTime = presentTime;
+    mPresentState = presentState;
+    mLastLatchTime = lastLatchTime;
 }
 
-void SurfaceFrame::setJankInfo(JankType jankType, int32_t jankMetadata) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    mJankType = jankType;
-    mJankMetadata = jankMetadata;
-}
-
-std::optional<JankType> SurfaceFrame::getJankType() const {
+std::optional<int32_t> SurfaceFrame::getJankType() const {
     std::lock_guard<std::mutex> lock(mMutex);
     if (mActuals.presentTime == 0) {
         return std::nullopt;
@@ -275,31 +286,30 @@
 
 nsecs_t SurfaceFrame::getBaseTime() const {
     std::lock_guard<std::mutex> lock(mMutex);
-    nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
-    if (mPredictionState == PredictionState::Valid) {
-        baseTime = std::min(baseTime, mPredictions.startTime);
-    }
-    if (mActuals.startTime != 0) {
-        baseTime = std::min(baseTime, mActuals.startTime);
-    }
-    baseTime = std::min(baseTime, mActuals.endTime);
-    return baseTime;
+    return getMinTime(mPredictionState, mPredictions, mActuals);
 }
 
-std::string presentStateToString(SurfaceFrame::PresentState presentState) {
-    using PresentState = SurfaceFrame::PresentState;
-    switch (presentState) {
-        case PresentState::Presented:
-            return "Presented";
-        case PresentState::Dropped:
-            return "Dropped";
-        case PresentState::Unknown:
-        default:
-            return "Unknown";
-    }
+TimelineItem SurfaceFrame::getActuals() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mActuals;
 }
 
-void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) {
+SurfaceFrame::PresentState SurfaceFrame::getPresentState() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mPresentState;
+}
+
+FramePresentMetadata SurfaceFrame::getFramePresentMetadata() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mFramePresentMetadata;
+}
+
+FrameReadyMetadata SurfaceFrame::getFrameReadyMetadata() const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mFrameReadyMetadata;
+}
+
+void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) const {
     std::lock_guard<std::mutex> lock(mMutex);
     StringAppendF(&result, "%s", indent.c_str());
     StringAppendF(&result, "Layer - %s", mDebugName.c_str());
@@ -309,21 +319,130 @@
     }
     StringAppendF(&result, "\n");
     StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Token: %" PRId64 "\n", mToken);
+    StringAppendF(&result, "%s", indent.c_str());
     StringAppendF(&result, "Owner Pid : %d\n", mOwnerPid);
     StringAppendF(&result, "%s", indent.c_str());
-    StringAppendF(&result, "Present State : %s\n", presentStateToString(mPresentState).c_str());
+    StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
     StringAppendF(&result, "%s", indent.c_str());
     StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
     StringAppendF(&result, "%s", indent.c_str());
-    StringAppendF(&result, "Jank Type : %s\n", toString(mJankType).c_str());
+    StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
     StringAppendF(&result, "%s", indent.c_str());
-    StringAppendF(&result, "Jank Metadata: %s\n",
-                  jankMetadataBitmaskToString(mJankMetadata).c_str());
+    StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
+    std::chrono::nanoseconds latchTime(
+            std::max(static_cast<int64_t>(0), mLastLatchTime - baseTime));
+    StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Last latch time: %10f\n",
+                  std::chrono::duration<double, std::milli>(latchTime).count());
+    if (mPredictionState == PredictionState::Valid) {
+        nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
+        std::chrono::nanoseconds presentDeltaNs(std::abs(presentDelta));
+        StringAppendF(&result, "%s", indent.c_str());
+        StringAppendF(&result, "Present delta: %10f\n",
+                      std::chrono::duration<double, std::milli>(presentDeltaNs).count());
+    }
     dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
 }
 
-void SurfaceFrame::traceSurfaceFrame(int64_t displayFrameToken) {
-    using FrameTimelineDataSource = FrameTimeline::FrameTimelineDataSource;
+void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType,
+                             nsecs_t vsyncPeriod) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mPresentState != PresentState::Presented) {
+        // No need to update dropped buffers
+        return;
+    }
+
+    mActuals.presentTime = presentTime;
+    // Jank Analysis for SurfaceFrame
+    if (mPredictionState == PredictionState::None) {
+        // Cannot do jank classification on frames that don't have a token.
+        return;
+    }
+    if (mPredictionState == PredictionState::Expired) {
+        // We do not know what happened here to classify this correctly. This could
+        // potentially be AppDeadlineMissed but that's assuming no app will request frames
+        // 120ms apart.
+        mJankType = JankType::Unknown;
+        mFramePresentMetadata = FramePresentMetadata::UnknownPresent;
+        mFrameReadyMetadata = FrameReadyMetadata::UnknownFinish;
+        mTimeStats->incrementJankyFrames(mOwnerUid, mLayerName, mJankType);
+        return;
+    }
+
+    const nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
+    const nsecs_t deadlineDelta = mActuals.endTime - mPredictions.endTime;
+    const nsecs_t deltaToVsync = std::abs(presentDelta) % vsyncPeriod;
+
+    if (deadlineDelta > mJankClassificationThresholds.deadlineThreshold) {
+        mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
+    } else {
+        mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
+    }
+
+    if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
+        mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
+                                                 : FramePresentMetadata::EarlyPresent;
+    } else {
+        mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
+    }
+
+    if (mFramePresentMetadata == FramePresentMetadata::OnTimePresent) {
+        // Frames presented on time are not janky.
+        mJankType = JankType::None;
+    } else if (mFramePresentMetadata == FramePresentMetadata::EarlyPresent) {
+        if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+            // Finish on time, Present early
+            if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+                deltaToVsync >= vsyncPeriod - mJankClassificationThresholds.presentThreshold) {
+                // Delta factor of vsync
+                mJankType = JankType::SurfaceFlingerScheduling;
+            } else {
+                // Delta not a factor of vsync
+                mJankType = JankType::PredictionError;
+            }
+        } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+            // Finish late, Present early
+            mJankType = JankType::Unknown;
+        }
+    } else {
+        if (mLastLatchTime != 0 && mPredictions.endTime <= mLastLatchTime) {
+            // Buffer Stuffing.
+            mJankType |= JankType::BufferStuffing;
+        }
+        if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+            // Finish on time, Present late
+            if (displayFrameJankType != JankType::None) {
+                // Propagate displayFrame's jank if it exists
+                mJankType |= displayFrameJankType;
+            } else {
+                if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+                    deltaToVsync >= vsyncPeriod - mJankClassificationThresholds.presentThreshold) {
+                    // Delta factor of vsync
+                    mJankType |= JankType::SurfaceFlingerScheduling;
+                } else {
+                    // Delta not a factor of vsync
+                    mJankType |= JankType::PredictionError;
+                }
+            }
+        } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+            // Finish late, Present late
+            if (displayFrameJankType == JankType::None) {
+                // Display frame is not janky, so purely app's fault
+                mJankType |= JankType::AppDeadlineMissed;
+            } else {
+                // Propagate DisplayFrame's jankType if it is janky
+                mJankType |= displayFrameJankType;
+            }
+        }
+    }
+    mTimeStats->incrementJankyFrames(mOwnerUid, mLayerName, mJankType);
+}
+
+void SurfaceFrame::trace(int64_t displayFrameToken) {
+    using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource;
     FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
         std::lock_guard<std::mutex> lock(mMutex);
         if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
@@ -349,11 +468,12 @@
         } else if (mPresentState == PresentState::Unknown) {
             surfaceFrameEvent->set_present_type(FrameTimelineEvent::PRESENT_UNSPECIFIED);
         } else {
-            surfaceFrameEvent->set_present_type(presentTypeToProto(mJankMetadata));
+            surfaceFrameEvent->set_present_type(toProto(mFramePresentMetadata));
         }
-        surfaceFrameEvent->set_on_time_finish(!(mJankMetadata & LateFinish));
-        surfaceFrameEvent->set_gpu_composition(mJankMetadata & GpuComposition);
-        surfaceFrameEvent->set_jank_type(JankTypeToProto(mJankType));
+        surfaceFrameEvent->set_on_time_finish(mFrameReadyMetadata ==
+                                              FrameReadyMetadata::OnTimeFinish);
+        surfaceFrameEvent->set_gpu_composition(mGpuComposition);
+        surfaceFrameEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
 
         surfaceFrameEvent->set_expected_start_ns(mPredictions.startTime);
         surfaceFrameEvent->set_expected_end_ns(mPredictions.endTime);
@@ -366,10 +486,45 @@
     });
 }
 
-FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats)
-      : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()),
+namespace impl {
+
+int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> lock(mMutex);
+    const int64_t assignedToken = mCurrentToken++;
+    mPredictions[assignedToken] = {systemTime(), predictions};
+    flushTokens(systemTime());
+    return assignedToken;
+}
+
+std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    auto predictionsIterator = mPredictions.find(token);
+    if (predictionsIterator != mPredictions.end()) {
+        return predictionsIterator->second.predictions;
+    }
+    return {};
+}
+
+void TokenManager::flushTokens(nsecs_t flushTime) {
+    for (auto it = mPredictions.begin(); it != mPredictions.end();) {
+        if (flushTime - it->second.timestamp >= kMaxRetentionTime) {
+            it = mPredictions.erase(it);
+        } else {
+            // Tokens are ordered by time. If i'th token is within the retention time, then the
+            // i+1'th token will also be within retention time.
+            break;
+        }
+    }
+}
+
+FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
+                             JankClassificationThresholds thresholds)
+      : mCurrentDisplayFrame(std::make_shared<DisplayFrame>(timeStats, thresholds)),
         mMaxDisplayFrames(kDefaultMaxDisplayFrames),
-        mTimeStats(std::move(timeStats)) {}
+        mTimeStats(std::move(timeStats)),
+        mSurfaceFlingerPid(surfaceFlingerPid),
+        mJankClassificationThresholds(thresholds) {}
 
 void FrameTimeline::onBootFinished() {
     perfetto::TracingInitArgs args;
@@ -384,67 +539,225 @@
     FrameTimelineDataSource::Register(dsd);
 }
 
-FrameTimeline::DisplayFrame::DisplayFrame()
-      : surfaceFlingerPredictions(TimelineItem()), surfaceFlingerActuals(TimelineItem()) {
-    this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
-}
-
-std::shared_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
-        pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
-        std::optional<int64_t> token) {
+std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
+        std::optional<int64_t> token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
+        std::string debugName) {
     ATRACE_CALL();
     if (!token) {
-        return std::make_shared<impl::SurfaceFrame>(ISurfaceComposer::INVALID_VSYNC_ID,ownerPid,
-                                                    ownerUid, std::move(layerName),
-                                                    std::move(debugName), PredictionState::None,
-                                                    TimelineItem());
+        return std::make_shared<SurfaceFrame>(ISurfaceComposer::INVALID_VSYNC_ID, ownerPid,
+                                              ownerUid, std::move(layerName), std::move(debugName),
+                                              PredictionState::None, TimelineItem(), mTimeStats,
+                                              mJankClassificationThresholds);
     }
     std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
     if (predictions) {
-        return std::make_shared<impl::SurfaceFrame>(*token, ownerPid, ownerUid,
-                                                    std::move(layerName), std::move(debugName),
-                                                    PredictionState::Valid,
-                                                    std::move(*predictions));
+        return std::make_shared<SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
+                                              std::move(debugName), PredictionState::Valid,
+                                              std::move(*predictions), mTimeStats,
+                                              mJankClassificationThresholds);
     }
-    return std::make_shared<impl::SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
-                                                std::move(debugName), PredictionState::Expired,
-                                                TimelineItem());
+    return std::make_shared<SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
+                                          std::move(debugName), PredictionState::Expired,
+                                          TimelineItem(), mTimeStats,
+                                          mJankClassificationThresholds);
 }
 
-void FrameTimeline::addSurfaceFrame(
-        std::shared_ptr<android::frametimeline::SurfaceFrame> surfaceFrame,
-        SurfaceFrame::PresentState state) {
-    ATRACE_CALL();
-    surfaceFrame->setPresentState(state);
-    std::lock_guard<std::mutex> lock(mMutex);
-    mCurrentDisplayFrame->surfaceFrames.push_back(
-            std::static_pointer_cast<impl::SurfaceFrame>(surfaceFrame));
+FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
+                                          JankClassificationThresholds thresholds)
+      : mSurfaceFlingerPredictions(TimelineItem()),
+        mSurfaceFlingerActuals(TimelineItem()),
+        mTimeStats(timeStats),
+        mJankClassificationThresholds(thresholds) {
+    mSurfaceFrames.reserve(kNumSurfaceFramesInitial);
 }
 
-void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime) {
+void FrameTimeline::addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) {
     ATRACE_CALL();
-    const std::optional<TimelineItem> prediction = mTokenManager.getPredictionsForToken(token);
     std::lock_guard<std::mutex> lock(mMutex);
-    mCurrentDisplayFrame->token = token;
-    if (!prediction) {
-        mCurrentDisplayFrame->predictionState = PredictionState::Expired;
-    } else {
-        mCurrentDisplayFrame->surfaceFlingerPredictions = *prediction;
-        mCurrentDisplayFrame->predictionState = PredictionState::Valid;
-    }
-    mCurrentDisplayFrame->surfaceFlingerActuals.startTime = wakeUpTime;
+    mCurrentDisplayFrame->addSurfaceFrame(surfaceFrame);
+}
+
+void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime, nsecs_t vsyncPeriod) {
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> lock(mMutex);
+    mCurrentDisplayFrame->onSfWakeUp(token, vsyncPeriod,
+                                     mTokenManager.getPredictionsForToken(token), wakeUpTime);
 }
 
 void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
                                  const std::shared_ptr<FenceTime>& presentFence) {
     ATRACE_CALL();
     std::lock_guard<std::mutex> lock(mMutex);
-    mCurrentDisplayFrame->surfaceFlingerActuals.endTime = sfPresentTime;
+    mCurrentDisplayFrame->setActualEndTime(sfPresentTime);
     mPendingPresentFences.emplace_back(std::make_pair(presentFence, mCurrentDisplayFrame));
     flushPendingPresentFences();
     finalizeCurrentDisplayFrame();
 }
 
+void FrameTimeline::DisplayFrame::addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) {
+    mSurfaceFrames.push_back(surfaceFrame);
+}
+
+void FrameTimeline::DisplayFrame::onSfWakeUp(int64_t token, nsecs_t vsyncPeriod,
+                                             std::optional<TimelineItem> predictions,
+                                             nsecs_t wakeUpTime) {
+    mToken = token;
+    mVsyncPeriod = vsyncPeriod;
+    if (!predictions) {
+        mPredictionState = PredictionState::Expired;
+    } else {
+        mPredictionState = PredictionState::Valid;
+        mSurfaceFlingerPredictions = *predictions;
+    }
+    mSurfaceFlingerActuals.startTime = wakeUpTime;
+}
+
+void FrameTimeline::DisplayFrame::setTokenAndVsyncPeriod(int64_t token, nsecs_t vsyncPeriod) {
+    mToken = token;
+    mVsyncPeriod = vsyncPeriod;
+}
+
+void FrameTimeline::DisplayFrame::setPredictions(PredictionState predictionState,
+                                                 TimelineItem predictions) {
+    mPredictionState = predictionState;
+    mSurfaceFlingerPredictions = predictions;
+}
+
+void FrameTimeline::DisplayFrame::setActualStartTime(nsecs_t actualStartTime) {
+    mSurfaceFlingerActuals.startTime = actualStartTime;
+}
+
+void FrameTimeline::DisplayFrame::setActualEndTime(nsecs_t actualEndTime) {
+    mSurfaceFlingerActuals.endTime = actualEndTime;
+}
+
+void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) {
+    mSurfaceFlingerActuals.presentTime = signalTime;
+    int32_t totalJankReasons = JankType::None;
+
+    // Delta between the expected present and the actual present
+    const nsecs_t presentDelta =
+            mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
+    // How far off was the presentDelta when compared to the vsyncPeriod. Used in checking if there
+    // was a prediction error or not.
+    nsecs_t deltaToVsync = std::abs(presentDelta) % mVsyncPeriod;
+    if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
+        mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
+                                                 : FramePresentMetadata::EarlyPresent;
+    } else {
+        mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
+    }
+
+    if (mSurfaceFlingerActuals.endTime - mSurfaceFlingerPredictions.endTime >
+        mJankClassificationThresholds.deadlineThreshold) {
+        mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
+    } else {
+        mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
+    }
+
+    if (std::abs(mSurfaceFlingerActuals.startTime - mSurfaceFlingerPredictions.startTime) >
+        mJankClassificationThresholds.startThreshold) {
+        mFrameStartMetadata =
+                mSurfaceFlingerActuals.startTime > mSurfaceFlingerPredictions.startTime
+                ? FrameStartMetadata::LateStart
+                : FrameStartMetadata::EarlyStart;
+    }
+
+    if (mFramePresentMetadata != FramePresentMetadata::OnTimePresent) {
+        // Do jank classification only if present is not on time
+        if (mFramePresentMetadata == FramePresentMetadata::EarlyPresent) {
+            if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+                // Finish on time, Present early
+                if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+                    deltaToVsync >=
+                            (mVsyncPeriod - mJankClassificationThresholds.presentThreshold)) {
+                    // Delta is a factor of vsync if its within the presentTheshold on either side
+                    // of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold
+                    // of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms.
+                    mJankType = JankType::SurfaceFlingerScheduling;
+                } else {
+                    // Delta is not a factor of vsync,
+                    mJankType = JankType::PredictionError;
+                }
+            } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+                // Finish late, Present early
+                mJankType = JankType::SurfaceFlingerScheduling;
+            } else {
+                // Finish time unknown
+                mJankType = JankType::Unknown;
+            }
+        } else if (mFramePresentMetadata == FramePresentMetadata::LatePresent) {
+            if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+                // Finish on time, Present late
+                if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+                    deltaToVsync >=
+                            (mVsyncPeriod - mJankClassificationThresholds.presentThreshold)) {
+                    // Delta is a factor of vsync if its within the presentTheshold on either side
+                    // of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold
+                    // of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms.
+                    mJankType = JankType::DisplayHAL;
+                } else {
+                    // Delta is not a factor of vsync
+                    mJankType = JankType::PredictionError;
+                }
+            } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+                // Finish late, Present late
+                mJankType = JankType::SurfaceFlingerCpuDeadlineMissed;
+            } else {
+                // Finish time unknown
+                mJankType = JankType::Unknown;
+            }
+        } else {
+            // Present unknown
+            mJankType = JankType::Unknown;
+        }
+    }
+    totalJankReasons |= mJankType;
+
+    for (auto& surfaceFrame : mSurfaceFrames) {
+        surfaceFrame->onPresent(signalTime, mJankType, mVsyncPeriod);
+        auto surfaceFrameJankType = surfaceFrame->getJankType();
+        if (surfaceFrameJankType != std::nullopt) {
+            totalJankReasons |= *surfaceFrameJankType;
+        }
+    }
+    mTimeStats->incrementJankyFrames(totalJankReasons);
+}
+
+void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const {
+    FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+        if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
+            ALOGD("Cannot trace DisplayFrame with invalid token");
+            return;
+        }
+        auto packet = ctx.NewTracePacket();
+        packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+        packet->set_timestamp(static_cast<uint64_t>(systemTime()));
+
+        auto* event = packet->set_frame_timeline_event();
+        auto* displayFrameEvent = event->set_display_frame();
+
+        displayFrameEvent->set_token(mToken);
+        displayFrameEvent->set_present_type(toProto(mFramePresentMetadata));
+        displayFrameEvent->set_on_time_finish(mFrameReadyMetadata ==
+                                              FrameReadyMetadata::OnTimeFinish);
+        displayFrameEvent->set_gpu_composition(mGpuComposition);
+        displayFrameEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+
+        displayFrameEvent->set_expected_start_ns(mSurfaceFlingerPredictions.startTime);
+        displayFrameEvent->set_expected_end_ns(mSurfaceFlingerPredictions.endTime);
+
+        displayFrameEvent->set_actual_start_ns(mSurfaceFlingerActuals.startTime);
+        displayFrameEvent->set_actual_end_ns(mSurfaceFlingerActuals.endTime);
+
+        displayFrameEvent->set_pid(surfaceFlingerPid);
+    });
+
+    for (auto& surfaceFrame : mSurfaceFrames) {
+        surfaceFrame->trace(mToken);
+    }
+}
+
 void FrameTimeline::flushPendingPresentFences() {
     for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
         const auto& pendingPresentFence = mPendingPresentFences[i];
@@ -456,93 +769,9 @@
             }
         }
         if (signalTime != Fence::SIGNAL_TIME_INVALID) {
-            int32_t totalJankReasons = JankType::None;
             auto& displayFrame = pendingPresentFence.second;
-            displayFrame->surfaceFlingerActuals.presentTime = signalTime;
-
-            // Jank Analysis for DisplayFrame
-            const auto& sfActuals = displayFrame->surfaceFlingerActuals;
-            const auto& sfPredictions = displayFrame->surfaceFlingerPredictions;
-            if (std::abs(sfActuals.presentTime - sfPredictions.presentTime) > kPresentThreshold) {
-                displayFrame->jankMetadata |= sfActuals.presentTime > sfPredictions.presentTime
-                        ? LatePresent
-                        : EarlyPresent;
-            }
-            if (std::abs(sfActuals.endTime - sfPredictions.endTime) > kDeadlineThreshold) {
-                if (sfActuals.endTime > sfPredictions.endTime) {
-                    displayFrame->jankMetadata |= LateFinish;
-                } else {
-                    displayFrame->jankMetadata |= EarlyFinish;
-                }
-
-                if ((displayFrame->jankMetadata & EarlyFinish) &&
-                    (displayFrame->jankMetadata & EarlyPresent)) {
-                    displayFrame->jankType = JankType::SurfaceFlingerEarlyLatch;
-                } else if ((displayFrame->jankMetadata & LateFinish) &&
-                           (displayFrame->jankMetadata & LatePresent)) {
-                    displayFrame->jankType = JankType::SurfaceFlingerDeadlineMissed;
-                } else if (displayFrame->jankMetadata & EarlyPresent ||
-                           displayFrame->jankMetadata & LatePresent) {
-                    // Cases where SF finished early but frame was presented late and vice versa
-                    displayFrame->jankType = JankType::Display;
-                }
-            }
-
-            if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) {
-                displayFrame->jankMetadata |=
-                        sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart;
-            }
-
-            totalJankReasons |= displayFrame->jankType;
-            traceDisplayFrame(*displayFrame);
-
-            for (auto& surfaceFrame : displayFrame->surfaceFrames) {
-                if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
-                    // Only presented SurfaceFrames need to be updated
-                    surfaceFrame->setActualPresentTime(signalTime);
-
-                    // Jank Analysis for SurfaceFrame
-                    const auto& predictionState = surfaceFrame->getPredictionState();
-                    if (predictionState == PredictionState::Expired) {
-                        // Jank analysis cannot be done on apps that don't use predictions
-                        surfaceFrame->setJankInfo(JankType::PredictionExpired, 0);
-                    } else if (predictionState == PredictionState::Valid) {
-                        const auto& actuals = surfaceFrame->getActuals();
-                        const auto& predictions = surfaceFrame->getPredictions();
-                        int32_t jankMetadata = 0;
-                        JankType jankType = JankType::None;
-                        if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) {
-                            jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish
-                                                                                  : EarlyFinish;
-                        }
-                        if (std::abs(actuals.presentTime - predictions.presentTime) >
-                            kPresentThreshold) {
-                            jankMetadata |= actuals.presentTime > predictions.presentTime
-                                    ? LatePresent
-                                    : EarlyPresent;
-                        }
-                        if (jankMetadata & EarlyPresent) {
-                            jankType = JankType::SurfaceFlingerEarlyLatch;
-                        } else if (jankMetadata & LatePresent) {
-                            if (jankMetadata & EarlyFinish) {
-                                // TODO(b/169890654): Classify this properly
-                                jankType = JankType::Display;
-                            } else {
-                                jankType = JankType::AppDeadlineMissed;
-                            }
-                        }
-
-                        totalJankReasons |= jankType;
-                        mTimeStats->incrementJankyFrames(surfaceFrame->getOwnerUid(),
-                                                         surfaceFrame->getName(),
-                                                         jankType | displayFrame->jankType);
-                        surfaceFrame->setJankInfo(jankType, jankMetadata);
-                    }
-                }
-                surfaceFrame->traceSurfaceFrame(displayFrame->token);
-            }
-
-            mTimeStats->incrementJankyFrames(totalJankReasons);
+            displayFrame->onPresent(signalTime);
+            displayFrame->trace(mSurfaceFlingerPid);
         }
 
         mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
@@ -557,16 +786,14 @@
     }
     mDisplayFrames.push_back(mCurrentDisplayFrame);
     mCurrentDisplayFrame.reset();
-    mCurrentDisplayFrame = std::make_shared<DisplayFrame>();
+    mCurrentDisplayFrame =
+            std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds);
 }
 
-nsecs_t FrameTimeline::findBaseTime(const std::shared_ptr<DisplayFrame>& displayFrame) {
-    nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
-    if (displayFrame->predictionState == PredictionState::Valid) {
-        baseTime = std::min(baseTime, displayFrame->surfaceFlingerPredictions.startTime);
-    }
-    baseTime = std::min(baseTime, displayFrame->surfaceFlingerActuals.startTime);
-    for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+nsecs_t FrameTimeline::DisplayFrame::getBaseTime() const {
+    nsecs_t baseTime =
+            getMinTime(mPredictionState, mSurfaceFlingerPredictions, mSurfaceFlingerActuals);
+    for (const auto& surfaceFrame : mSurfaceFrames) {
         nsecs_t surfaceFrameBaseTime = surfaceFrame->getBaseTime();
         if (surfaceFrameBaseTime != 0) {
             baseTime = std::min(baseTime, surfaceFrameBaseTime);
@@ -575,60 +802,79 @@
     return baseTime;
 }
 
-void FrameTimeline::dumpDisplayFrame(std::string& result,
-                                     const std::shared_ptr<DisplayFrame>& displayFrame,
-                                     nsecs_t baseTime) {
-    if (displayFrame->jankType != JankType::None) {
+void FrameTimeline::DisplayFrame::dumpJank(std::string& result, nsecs_t baseTime,
+                                           int displayFrameCount) const {
+    if (mJankType == JankType::None) {
+        // Check if any Surface Frame has been janky
+        bool isJanky = false;
+        for (const auto& surfaceFrame : mSurfaceFrames) {
+            if (surfaceFrame->getJankType() != JankType::None) {
+                isJanky = true;
+                break;
+            }
+        }
+        if (!isJanky) {
+            return;
+        }
+    }
+    StringAppendF(&result, "Display Frame %d", displayFrameCount);
+    dump(result, baseTime);
+}
+
+void FrameTimeline::DisplayFrame::dumpAll(std::string& result, nsecs_t baseTime) const {
+    dump(result, baseTime);
+}
+
+void FrameTimeline::DisplayFrame::dump(std::string& result, nsecs_t baseTime) const {
+    if (mJankType != JankType::None) {
         // Easily identify a janky Display Frame in the dump
         StringAppendF(&result, " [*] ");
     }
     StringAppendF(&result, "\n");
-    StringAppendF(&result, "Prediction State : %s\n",
-                  toString(displayFrame->predictionState).c_str());
-    StringAppendF(&result, "Jank Type : %s\n", toString(displayFrame->jankType).c_str());
-    StringAppendF(&result, "Jank Metadata: %s\n",
-                  jankMetadataBitmaskToString(displayFrame->jankMetadata).c_str());
-    dumpTable(result, displayFrame->surfaceFlingerPredictions, displayFrame->surfaceFlingerActuals,
-              "", displayFrame->predictionState, baseTime);
+    StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
+    StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
+    StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
+    StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
+    StringAppendF(&result, "Start Metadata: %s\n", toString(mFrameStartMetadata).c_str());
+    std::chrono::nanoseconds vsyncPeriod(mVsyncPeriod);
+    StringAppendF(&result, "Vsync Period: %10f\n",
+                  std::chrono::duration<double, std::milli>(vsyncPeriod).count());
+    nsecs_t presentDelta =
+            mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
+    std::chrono::nanoseconds presentDeltaNs(std::abs(presentDelta));
+    StringAppendF(&result, "Present delta: %10f\n",
+                  std::chrono::duration<double, std::milli>(presentDeltaNs).count());
+    std::chrono::nanoseconds deltaToVsync(std::abs(presentDelta) % mVsyncPeriod);
+    StringAppendF(&result, "Present delta %% refreshrate: %10f\n",
+                  std::chrono::duration<double, std::milli>(deltaToVsync).count());
+    dumpTable(result, mSurfaceFlingerPredictions, mSurfaceFlingerActuals, "", mPredictionState,
+              baseTime);
     StringAppendF(&result, "\n");
     std::string indent = "    "; // 4 spaces
-    for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+    for (const auto& surfaceFrame : mSurfaceFrames) {
         surfaceFrame->dump(result, indent, baseTime);
     }
     StringAppendF(&result, "\n");
 }
+
 void FrameTimeline::dumpAll(std::string& result) {
     std::lock_guard<std::mutex> lock(mMutex);
     StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
-    nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+    nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : mDisplayFrames[0]->getBaseTime();
     for (size_t i = 0; i < mDisplayFrames.size(); i++) {
         StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
-        dumpDisplayFrame(result, mDisplayFrames[i], baseTime);
+        mDisplayFrames[i]->dumpAll(result, baseTime);
     }
 }
 
 void FrameTimeline::dumpJank(std::string& result) {
     std::lock_guard<std::mutex> lock(mMutex);
-    nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+    nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : mDisplayFrames[0]->getBaseTime();
     for (size_t i = 0; i < mDisplayFrames.size(); i++) {
-        const auto& displayFrame = mDisplayFrames[i];
-        if (displayFrame->jankType == JankType::None) {
-            // Check if any Surface Frame has been janky
-            bool isJanky = false;
-            for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
-                if (surfaceFrame->getJankType() != JankType::None) {
-                    isJanky = true;
-                    break;
-                }
-            }
-            if (!isJanky) {
-                continue;
-            }
-        }
-        StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
-        dumpDisplayFrame(result, displayFrame, baseTime);
+        mDisplayFrames[i]->dumpJank(result, baseTime, static_cast<int>(i));
     }
 }
+
 void FrameTimeline::parseArgs(const Vector<String16>& args, std::string& result) {
     ATRACE_CALL();
     std::unordered_map<std::string, bool> argsMap;
@@ -656,31 +902,5 @@
     setMaxDisplayFrames(kDefaultMaxDisplayFrames);
 }
 
-void FrameTimeline::traceDisplayFrame(const DisplayFrame& displayFrame) {
-    FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
-        if (displayFrame.token == ISurfaceComposer::INVALID_VSYNC_ID) {
-            ALOGD("Cannot trace DisplayFrame with invalid token");
-            return;
-        }
-        auto packet = ctx.NewTracePacket();
-        packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
-        packet->set_timestamp(static_cast<uint64_t>(systemTime()));
-
-        auto* event = packet->set_frame_timeline_event();
-        auto* displayFrameEvent = event->set_display_frame();
-
-        displayFrameEvent->set_token(displayFrame.token);
-        displayFrameEvent->set_present_type(presentTypeToProto(displayFrame.jankMetadata));
-        displayFrameEvent->set_on_time_finish(!(displayFrame.jankMetadata & LateFinish));
-        displayFrameEvent->set_gpu_composition(displayFrame.jankMetadata & GpuComposition);
-        displayFrameEvent->set_jank_type(JankTypeToProto(displayFrame.jankType));
-
-        displayFrameEvent->set_expected_start_ns(displayFrame.surfaceFlingerPredictions.startTime);
-        displayFrameEvent->set_expected_end_ns(displayFrame.surfaceFlingerPredictions.endTime);
-
-        displayFrameEvent->set_actual_start_ns(displayFrame.surfaceFlingerActuals.startTime);
-        displayFrameEvent->set_actual_end_ns(displayFrame.surfaceFlingerActuals.endTime);
-    });
-}
-
-} // namespace android::frametimeline::impl
+} // namespace impl
+} // namespace android::frametimeline
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 084935b..f5239aa 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -32,24 +32,44 @@
 
 namespace android::frametimeline {
 
-enum JankMetadata {
-    // Frame was presented earlier than expected
-    EarlyPresent = 0x1,
-    // Frame was presented later than expected
-    LatePresent = 0x2,
-    // App/SF started earlier than expected
-    EarlyStart = 0x4,
-    // App/SF started later than expected
-    LateStart = 0x8,
-    // App/SF finished work earlier than the deadline
-    EarlyFinish = 0x10,
-    // App/SF finished work later than the deadline
-    LateFinish = 0x20,
-    // SF was in GPU composition
-    GpuComposition = 0x40,
+class FrameTimelineTest;
+
+using namespace std::chrono_literals;
+
+// Metadata indicating how the frame was presented w.r.t expected present time.
+enum class FramePresentMetadata : int8_t {
+    // Frame was presented on time
+    OnTimePresent,
+    // Frame was presented late
+    LatePresent,
+    // Frame was presented early
+    EarlyPresent,
+    // Unknown/initial state
+    UnknownPresent,
 };
 
-class FrameTimelineTest;
+// Metadata comparing the frame's actual finish time to the expected deadline.
+enum class FrameReadyMetadata : int8_t {
+    // App/SF finished on time. Early finish is treated as on time since the goal of any component
+    // is to finish before the deadline.
+    OnTimeFinish,
+    // App/SF finished work later than expected
+    LateFinish,
+    // Unknown/initial state
+    UnknownFinish,
+};
+
+// Metadata comparing the frame's actual start time to the expected start time.
+enum class FrameStartMetadata : int8_t {
+    // App/SF started on time
+    OnTimeStart,
+    // App/SF started later than expected
+    LateStart,
+    // App/SF started earlier than expected
+    EarlyStart,
+    // Unknown/initial state
+    UnknownStart,
+};
 
 /*
  * Collection of timestamps that can be used for both predictions and actual times.
@@ -71,6 +91,19 @@
     bool operator!=(const TimelineItem& other) const { return !(*this == other); }
 };
 
+struct TokenManagerPrediction {
+    nsecs_t timestamp = 0;
+    TimelineItem predictions;
+};
+
+struct JankClassificationThresholds {
+    // The various thresholds for App and SF. If the actual timestamp falls within the threshold
+    // compared to prediction, we treat it as on time.
+    nsecs_t presentThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+    nsecs_t deadlineThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+    nsecs_t startThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+};
+
 /*
  * TokenManager generates a running number token for a set of predictions made by VsyncPredictor. It
  * saves these predictions for a short period of time and returns the predictions for a given token,
@@ -83,6 +116,9 @@
     // Generates a token for the given set of predictions. Stores the predictions for 120ms and
     // destroys it later.
     virtual int64_t generateTokenForPredictions(TimelineItem&& prediction) = 0;
+
+    // Returns the stored predictions for a given token, if the predictions haven't expired.
+    virtual std::optional<TimelineItem> getPredictionsForToken(int64_t token) const = 0;
 };
 
 enum class PredictionState {
@@ -91,10 +127,6 @@
     None,    // Predictions are either not present or didn't come from TokenManager
 };
 
-/*
- * Stores a set of predictions and the corresponding actual timestamps pertaining to a single frame
- * from the app
- */
 class SurfaceFrame {
 public:
     enum class PresentState {
@@ -103,29 +135,76 @@
         Unknown,   // Initial state, SurfaceFlinger hasn't seen this buffer yet
     };
 
-    virtual ~SurfaceFrame() = default;
+    // Only FrameTimeline can construct a SurfaceFrame as it provides Predictions(through
+    // TokenManager), Thresholds and TimeStats pointer.
+    SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
+                 std::string debugName, PredictionState predictionState, TimelineItem&& predictions,
+                 std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds);
+    ~SurfaceFrame() = default;
 
-    virtual TimelineItem getPredictions() const = 0;
-    virtual TimelineItem getActuals() const = 0;
-    virtual nsecs_t getActualQueueTime() const = 0;
-    virtual PresentState getPresentState() const = 0;
-    virtual PredictionState getPredictionState() const = 0;
-    virtual pid_t getOwnerPid() const = 0;
+    // Returns std::nullopt if the frame hasn't been classified yet.
+    // Used by both SF and FrameTimeline.
+    std::optional<int32_t> getJankType() const;
 
-    virtual void setPresentState(PresentState state) = 0;
-
+    // Functions called by SF
+    int64_t getToken() const { return mToken; };
+    TimelineItem getPredictions() const { return mPredictions; };
     // Actual timestamps of the app are set individually at different functions.
     // Start time (if the app provides) and Queue time are accessible after queueing the frame,
     // whereas Acquire Fence time is available only during latch.
-    virtual void setActualStartTime(nsecs_t actualStartTime) = 0;
-    virtual void setActualQueueTime(nsecs_t actualQueueTime) = 0;
-    virtual void setAcquireFenceTime(nsecs_t acquireFenceTime) = 0;
+    void setActualStartTime(nsecs_t actualStartTime);
+    void setActualQueueTime(nsecs_t actualQueueTime);
+    void setAcquireFenceTime(nsecs_t acquireFenceTime);
+    void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0);
 
-    // Retrieves jank classification, if it's already been classified.
-    virtual std::optional<JankType> getJankType() const = 0;
+    // Functions called by FrameTimeline
+    // BaseTime is the smallest timestamp in this SurfaceFrame.
+    // Used for dumping all timestamps relative to the oldest, making it easy to read.
+    nsecs_t getBaseTime() const;
+    // Sets the actual present time, appropriate metadata and classifies the jank.
+    void onPresent(nsecs_t presentTime, int32_t displayFrameJankType, nsecs_t vsyncPeriod);
+    // All the timestamps are dumped relative to the baseTime
+    void dump(std::string& result, const std::string& indent, nsecs_t baseTime) const;
+    // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
+    // enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding
+    // DisplayFrame at the trace processor side.
+    void trace(int64_t displayFrameToken);
 
-    // Token identifying the frame.
-    virtual int64_t getToken() const = 0;
+    // Getter functions used only by FrameTimelineTests
+    TimelineItem getActuals() const;
+    pid_t getOwnerPid() const { return mOwnerPid; };
+    PredictionState getPredictionState() const { return mPredictionState; };
+    PresentState getPresentState() const;
+    FrameReadyMetadata getFrameReadyMetadata() const;
+    FramePresentMetadata getFramePresentMetadata() const;
+
+private:
+    const int64_t mToken;
+    const pid_t mOwnerPid;
+    const uid_t mOwnerUid;
+    const std::string mLayerName;
+    const std::string mDebugName;
+    PresentState mPresentState GUARDED_BY(mMutex);
+    const PredictionState mPredictionState;
+    const TimelineItem mPredictions;
+    TimelineItem mActuals GUARDED_BY(mMutex);
+    std::shared_ptr<TimeStats> mTimeStats;
+    const JankClassificationThresholds mJankClassificationThresholds;
+    nsecs_t mActualQueueTime GUARDED_BY(mMutex) = 0;
+    mutable std::mutex mMutex;
+    // Bitmask for the type of jank
+    int32_t mJankType GUARDED_BY(mMutex) = JankType::None;
+    // Indicates if this frame was composited by the GPU or not
+    bool mGpuComposition GUARDED_BY(mMutex) = false;
+    // Enum for the type of present
+    FramePresentMetadata mFramePresentMetadata GUARDED_BY(mMutex) =
+            FramePresentMetadata::UnknownPresent;
+    // Enum for the type of finish
+    FrameReadyMetadata mFrameReadyMetadata GUARDED_BY(mMutex) = FrameReadyMetadata::UnknownFinish;
+    // Time when the previous buffer from the same layer was latched by SF. This is used in checking
+    // for BufferStuffing where the current buffer is expected to be ready but the previous buffer
+    // was latched instead.
+    nsecs_t mLastLatchTime GUARDED_BY(mMutex) = 0;
 };
 
 /*
@@ -142,20 +221,19 @@
     virtual void onBootFinished() = 0;
 
     // Create a new surface frame, set the predictions based on a token and return it to the caller.
-    // Sets the PredictionState of SurfaceFrame.
     // Debug name is the human-readable debugging string for dumpsys.
-    virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
-            pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
-            std::optional<int64_t> token) = 0;
+    virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(std::optional<int64_t> token,
+                                                                     pid_t ownerPid, uid_t ownerUid,
+                                                                     std::string layerName,
+                                                                     std::string debugName) = 0;
 
     // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
     // composited into one display frame.
-    virtual void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame,
-                                 SurfaceFrame::PresentState state) = 0;
+    virtual void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) = 0;
 
     // The first function called by SF for the current DisplayFrame. Fetches SF predictions based on
     // the token and sets the actualSfWakeTime for the current DisplayFrame.
-    virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime) = 0;
+    virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime, nsecs_t vsyncPeriod) = 0;
 
     // Sets the sfPresentTime and finalizes the current DisplayFrame. Tracks the given present fence
     // until it's signaled, and updates the present timestamps of all presented SurfaceFrames in
@@ -178,15 +256,13 @@
 
 namespace impl {
 
-using namespace std::chrono_literals;
-
 class TokenManager : public android::frametimeline::TokenManager {
 public:
     TokenManager() : mCurrentToken(ISurfaceComposer::INVALID_VSYNC_ID + 1) {}
     ~TokenManager() = default;
 
     int64_t generateTokenForPredictions(TimelineItem&& predictions) override;
-    std::optional<TimelineItem> getPredictionsForToken(int64_t token);
+    std::optional<TimelineItem> getPredictionsForToken(int64_t token) const override;
 
 private:
     // Friend class for testing
@@ -194,64 +270,13 @@
 
     void flushTokens(nsecs_t flushTime) REQUIRES(mMutex);
 
-    std::unordered_map<int64_t, TimelineItem> mPredictions GUARDED_BY(mMutex);
-    std::vector<std::pair<int64_t, nsecs_t>> mTokens GUARDED_BY(mMutex);
+    std::map<int64_t, TokenManagerPrediction> mPredictions GUARDED_BY(mMutex);
     int64_t mCurrentToken GUARDED_BY(mMutex);
-    std::mutex mMutex;
+    mutable std::mutex mMutex;
     static constexpr nsecs_t kMaxRetentionTime =
             std::chrono::duration_cast<std::chrono::nanoseconds>(120ms).count();
 };
 
-class SurfaceFrame : public android::frametimeline::SurfaceFrame {
-public:
-    SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
-                 std::string debugName, PredictionState predictionState,
-                 TimelineItem&& predictions);
-    ~SurfaceFrame() = default;
-
-    TimelineItem getPredictions() const override { return mPredictions; };
-    TimelineItem getActuals() const override;
-    nsecs_t getActualQueueTime() const override;
-    PresentState getPresentState() const override;
-    PredictionState getPredictionState() const override { return mPredictionState; };
-    pid_t getOwnerPid() const override { return mOwnerPid; };
-    std::optional<JankType> getJankType() const override;
-    int64_t getToken() const override { return mToken; };
-    nsecs_t getBaseTime() const;
-    uid_t getOwnerUid() const { return mOwnerUid; };
-    const std::string& getName() const { return mLayerName; };
-
-    void setActualStartTime(nsecs_t actualStartTime) override;
-    void setActualQueueTime(nsecs_t actualQueueTime) override;
-    void setAcquireFenceTime(nsecs_t acquireFenceTime) override;
-    void setPresentState(PresentState state) override;
-    void setActualPresentTime(nsecs_t presentTime);
-    void setJankInfo(JankType jankType, int32_t jankMetadata);
-
-    // All the timestamps are dumped relative to the baseTime
-    void dump(std::string& result, const std::string& indent, nsecs_t baseTime);
-
-    // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
-    // enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding
-    // DisplayFrame at the trace processor side.
-    void traceSurfaceFrame(int64_t displayFrameToken);
-
-private:
-    const int64_t mToken;
-    const pid_t mOwnerPid;
-    const uid_t mOwnerUid;
-    const std::string mLayerName;
-    const std::string mDebugName;
-    PresentState mPresentState GUARDED_BY(mMutex);
-    const PredictionState mPredictionState;
-    const TimelineItem mPredictions;
-    TimelineItem mActuals GUARDED_BY(mMutex);
-    nsecs_t mActualQueueTime GUARDED_BY(mMutex);
-    mutable std::mutex mMutex;
-    JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank
-    int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank
-};
-
 class FrameTimeline : public android::frametimeline::FrameTimeline {
 public:
     class FrameTimelineDataSource : public perfetto::DataSource<FrameTimelineDataSource> {
@@ -260,16 +285,94 @@
         void OnStop(const StopArgs&) override{};
     };
 
-    FrameTimeline(std::shared_ptr<TimeStats> timeStats);
+    /*
+     * DisplayFrame should be used only internally within FrameTimeline. All members and methods are
+     * guarded by FrameTimeline's mMutex.
+     */
+    class DisplayFrame {
+    public:
+        DisplayFrame(std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds);
+        virtual ~DisplayFrame() = default;
+        // Dumpsys interface - dumps only if the DisplayFrame itself is janky or is at least one
+        // SurfaceFrame is janky.
+        void dumpJank(std::string& result, nsecs_t baseTime, int displayFrameCount) const;
+        // Dumpsys interface - dumps all data irrespective of jank
+        void dumpAll(std::string& result, nsecs_t baseTime) const;
+        // Emits a packet for perfetto tracing. The function body will be executed only if tracing
+        // is enabled.
+        void trace(pid_t surfaceFlingerPid) const;
+        // Sets the token, vsyncPeriod, predictions and SF start time.
+        void onSfWakeUp(int64_t token, nsecs_t vsyncPeriod, std::optional<TimelineItem> predictions,
+                        nsecs_t wakeUpTime);
+        // Sets the appropriate metadata, classifies the jank and returns the classified jankType.
+        void onPresent(nsecs_t signalTime);
+        // Adds the provided SurfaceFrame to the current display frame.
+        void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame);
+
+        void setTokenAndVsyncPeriod(int64_t token, nsecs_t vsyncPeriod);
+        void setPredictions(PredictionState predictionState, TimelineItem predictions);
+        void setActualStartTime(nsecs_t actualStartTime);
+        void setActualEndTime(nsecs_t actualEndTime);
+
+        // BaseTime is the smallest timestamp in a DisplayFrame.
+        // Used for dumping all timestamps relative to the oldest, making it easy to read.
+        nsecs_t getBaseTime() const;
+
+        // Functions to be used only in testing.
+        TimelineItem getActuals() const { return mSurfaceFlingerActuals; };
+        TimelineItem getPredictions() const { return mSurfaceFlingerPredictions; };
+        FramePresentMetadata getFramePresentMetadata() const { return mFramePresentMetadata; };
+        FrameReadyMetadata getFrameReadyMetadata() const { return mFrameReadyMetadata; };
+        int32_t getJankType() const { return mJankType; }
+        const std::vector<std::shared_ptr<SurfaceFrame>>& getSurfaceFrames() const {
+            return mSurfaceFrames;
+        }
+
+    private:
+        void dump(std::string& result, nsecs_t baseTime) const;
+
+        int64_t mToken = ISurfaceComposer::INVALID_VSYNC_ID;
+
+        /* Usage of TimelineItem w.r.t SurfaceFlinger
+         * startTime    Time when SurfaceFlinger wakes up to handle transactions and buffer updates
+         * endTime      Time when SurfaceFlinger sends a composited frame to Display
+         * presentTime  Time when the composited frame was presented on screen
+         */
+        TimelineItem mSurfaceFlingerPredictions;
+        TimelineItem mSurfaceFlingerActuals;
+        std::shared_ptr<TimeStats> mTimeStats;
+        const JankClassificationThresholds mJankClassificationThresholds;
+
+        // Collection of predictions and actual values sent over by Layers
+        std::vector<std::shared_ptr<SurfaceFrame>> mSurfaceFrames;
+
+        PredictionState mPredictionState = PredictionState::None;
+        // Bitmask for the type of jank
+        int32_t mJankType = JankType::None;
+        // Indicates if this frame was composited by the GPU or not
+        bool mGpuComposition = false;
+        // Enum for the type of present
+        FramePresentMetadata mFramePresentMetadata = FramePresentMetadata::UnknownPresent;
+        // Enum for the type of finish
+        FrameReadyMetadata mFrameReadyMetadata = FrameReadyMetadata::UnknownFinish;
+        // Enum for the type of start
+        FrameStartMetadata mFrameStartMetadata = FrameStartMetadata::UnknownStart;
+        // The refresh rate (vsync period) in nanoseconds as seen by SF during this DisplayFrame's
+        // timeline
+        nsecs_t mVsyncPeriod = 0;
+    };
+
+    FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
+                  JankClassificationThresholds thresholds = {});
     ~FrameTimeline() = default;
 
     frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
-    std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken(
-            pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
-            std::optional<int64_t> token) override;
-    void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame,
-                         SurfaceFrame::PresentState state) override;
-    void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override;
+    std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(std::optional<int64_t> token,
+                                                             pid_t ownerPid, uid_t ownerUid,
+                                                             std::string layerName,
+                                                             std::string debugName) override;
+    void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
+    void setSfWakeUp(int64_t token, nsecs_t wakeupTime, nsecs_t vsyncPeriod) override;
     void setSfPresent(nsecs_t sfPresentTime,
                       const std::shared_ptr<FenceTime>& presentFence) override;
     void parseArgs(const Vector<String16>& args, std::string& result) override;
@@ -288,67 +391,28 @@
     // Friend class for testing
     friend class android::frametimeline::FrameTimelineTest;
 
-    /*
-     * DisplayFrame should be used only internally within FrameTimeline.
-     */
-    struct DisplayFrame {
-        DisplayFrame();
-
-        int64_t token = ISurfaceComposer::INVALID_VSYNC_ID;
-
-        /* Usage of TimelineItem w.r.t SurfaceFlinger
-         * startTime    Time when SurfaceFlinger wakes up to handle transactions and buffer updates
-         * endTime      Time when SurfaceFlinger sends a composited frame to Display
-         * presentTime  Time when the composited frame was presented on screen
-         */
-        TimelineItem surfaceFlingerPredictions;
-        TimelineItem surfaceFlingerActuals;
-
-        // Collection of predictions and actual values sent over by Layers
-        std::vector<std::shared_ptr<SurfaceFrame>> surfaceFrames;
-
-        PredictionState predictionState = PredictionState::None;
-        JankType jankType = JankType::None; // Enum for the type of jank
-        int32_t jankMetadata = 0x0; // Additional details about the jank
-    };
-
     void flushPendingPresentFences() REQUIRES(mMutex);
     void finalizeCurrentDisplayFrame() REQUIRES(mMutex);
-    // BaseTime is the smallest timestamp in a DisplayFrame.
-    // Used for dumping all timestamps relative to the oldest, making it easy to read.
-    nsecs_t findBaseTime(const std::shared_ptr<DisplayFrame>&) REQUIRES(mMutex);
-    void dumpDisplayFrame(std::string& result, const std::shared_ptr<DisplayFrame>&,
-                          nsecs_t baseTime) REQUIRES(mMutex);
     void dumpAll(std::string& result);
     void dumpJank(std::string& result);
 
-    // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
-    // enabled.
-    void traceDisplayFrame(const DisplayFrame& displayFrame) REQUIRES(mMutex);
-
     // Sliding window of display frames. TODO(b/168072834): compare perf with fixed size array
     std::deque<std::shared_ptr<DisplayFrame>> mDisplayFrames GUARDED_BY(mMutex);
     std::vector<std::pair<std::shared_ptr<FenceTime>, std::shared_ptr<DisplayFrame>>>
             mPendingPresentFences GUARDED_BY(mMutex);
     std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex);
     TokenManager mTokenManager;
-    std::mutex mMutex;
+    mutable std::mutex mMutex;
     uint32_t mMaxDisplayFrames;
     std::shared_ptr<TimeStats> mTimeStats;
+    const pid_t mSurfaceFlingerPid;
+    const JankClassificationThresholds mJankClassificationThresholds;
     static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
     // The initial container size for the vector<SurfaceFrames> inside display frame. Although
     // this number doesn't represent any bounds on the number of surface frames that can go in a
     // display frame, this is a good starting size for the vector so that we can avoid the
     // internal vector resizing that happens with push_back.
     static constexpr uint32_t kNumSurfaceFramesInitial = 10;
-    // The various thresholds for App and SF. If the actual timestamp falls within the threshold
-    // compared to prediction, we don't treat it as a jank.
-    static constexpr nsecs_t kPresentThreshold =
-            std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
-    static constexpr nsecs_t kDeadlineThreshold =
-            std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
-    static constexpr nsecs_t kSFStartThreshold =
-            std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count();
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index d5b599a..a545d18 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -656,6 +656,8 @@
         layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
         layerSettings.blurRegions = getBlurRegions();
     }
+    // Record the name of the layer for debugging further down the stack.
+    layerSettings.name = getName();
     return layerSettings;
 }
 
@@ -686,6 +688,7 @@
     shadowLayer.source.buffer.fence = nullptr;
     shadowLayer.frameNumber = 0;
     shadowLayer.bufferId = 0;
+    shadowLayer.name = getName();
 
     if (shadowLayer.shadow.ambientColor.a <= 0.f && shadowLayer.shadow.spotColor.a <= 0.f) {
         return {};
@@ -721,6 +724,7 @@
 
     // If layer is blacked out, force alpha to 1 so that we draw a black color layer.
     layerSettings.alpha = blackout ? 1.0f : 0.0f;
+    layerSettings.name = getName();
 }
 
 std::vector<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompositionList(
@@ -909,9 +913,8 @@
                 : std::make_optional(stateToCommit->frameTimelineVsyncId);
 
         mSurfaceFrame =
-                mFlinger->mFrameTimeline->createSurfaceFrameForToken(getOwnerPid(), getOwnerUid(),
-                                                                     mName, mTransactionName,
-                                                                     vsyncId);
+                mFlinger->mFrameTimeline->createSurfaceFrameForToken(vsyncId, mOwnerPid, mOwnerUid,
+                                                                     mName, mTransactionName);
         mSurfaceFrame->setActualQueueTime(stateToCommit->postTime);
         // For transactions we set the acquire fence time to the post time as we
         // don't have a buffer. For BufferStateLayer it is overridden in
@@ -1064,7 +1067,8 @@
 
 void Layer::commitTransaction(const State& stateToCommit) {
     mDrawingState = stateToCommit;
-    mFlinger->mFrameTimeline->addSurfaceFrame(mSurfaceFrame, PresentState::Presented);
+    mSurfaceFrame->setPresentState(PresentState::Presented);
+    mFlinger->mFrameTimeline->addSurfaceFrame(mSurfaceFrame);
 }
 
 uint32_t Layer::getTransactionFlags(uint32_t flags) {
@@ -2185,7 +2189,10 @@
 }
 
 int32_t Layer::getBackgroundBlurRadius() const {
-    return getDrawingState().backgroundBlurRadius;
+    const auto& p = mDrawingParent.promote();
+
+    half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf;
+    return parentAlpha * getDrawingState().backgroundBlurRadius;
 }
 
 const std::vector<BlurRegion>& Layer::getBlurRegions() const {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 2cfdba3..d6023b6 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -275,7 +275,8 @@
         // recent callback handle.
         std::deque<sp<CallbackHandle>> callbackHandles;
         bool colorSpaceAgnostic;
-        nsecs_t desiredPresentTime = -1;
+        nsecs_t desiredPresentTime = 0;
+        bool isAutoTimestamp = true;
 
         // Length of the cast shadow. If the radius is > 0, a shadow of length shadowRadius will
         // be rendered around the layer.
@@ -444,7 +445,8 @@
     virtual bool setFrame(const Rect& /*frame*/) { return false; };
     virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
                            nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
-                           const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */) {
+                           bool /*isAutoTimestamp*/, const client_cache_t& /*clientCacheId*/,
+                           uint64_t /* frameNumber */) {
         return false;
     };
     virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
@@ -481,6 +483,8 @@
     virtual void useSurfaceDamage() {}
     virtual void useEmptyDamage() {}
 
+    virtual void incrementPendingBufferCount() {}
+
     /*
      * isOpaque - true if this surface is opaque
      *
@@ -745,7 +749,7 @@
      * doTransaction - process the transaction. This is a good place to figure
      * out which attributes of the surface have changed.
      */
-    uint32_t doTransaction(uint32_t transactionFlags);
+    virtual uint32_t doTransaction(uint32_t transactionFlags);
 
     /*
      * Remove relative z for the layer if its relative parent is not part of the
@@ -879,7 +883,7 @@
      */
     bool hasInputInfo() const;
 
-    uid_t getOwnerUid() { return mOwnerUid; }
+    virtual uid_t getOwnerUid() const { return mOwnerUid; }
 
     pid_t getOwnerPid() { return mOwnerPid; }
 
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 59fad9b..b1db6d3 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -17,6 +17,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include "LayerProtoHelper.h"
 
@@ -173,4 +174,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/Promise.h b/services/surfaceflinger/Promise.h
deleted file mode 100644
index a80d441..0000000
--- a/services/surfaceflinger/Promise.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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 <future>
-#include <type_traits>
-#include <utility>
-
-namespace android::promise {
-namespace impl {
-
-template <typename T>
-struct FutureResult {
-    using Type = T;
-};
-
-template <typename T>
-struct FutureResult<std::future<T>> {
-    using Type = T;
-};
-
-} // namespace impl
-
-template <typename T>
-using FutureResult = typename impl::FutureResult<T>::Type;
-
-template <typename... Args>
-inline auto defer(Args... args) {
-    return std::async(std::launch::deferred, std::forward<Args>(args)...);
-}
-
-template <typename T>
-inline std::future<T> yield(T&& v) {
-    return defer([](T&& v) { return std::forward<T>(v); }, std::forward<T>(v));
-}
-
-template <typename T>
-struct Chain {
-    Chain(std::future<T>&& f) : future(std::move(f)) {}
-    operator std::future<T>&&() && { return std::move(future); }
-
-    T get() && { return future.get(); }
-
-    template <typename F, typename R = std::invoke_result_t<F, T>>
-    auto then(F&& op) && -> Chain<FutureResult<R>> {
-        return defer(
-                [](auto&& f, F&& op) {
-                    R r = op(f.get());
-                    if constexpr (std::is_same_v<R, FutureResult<R>>) {
-                        return r;
-                    } else {
-                        return r.get();
-                    }
-                },
-                std::move(future), std::forward<F>(op));
-    }
-
-    std::future<T> future;
-};
-
-template <typename T>
-inline Chain<T> chain(std::future<T>&& f) {
-    return std::move(f);
-}
-
-} // namespace android::promise
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index f99d54a..6a511a8 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -17,6 +17,9 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
+
+#include <algorithm>
 
 #include "RefreshRateOverlay.h"
 #include "Client.h"
@@ -172,7 +175,7 @@
 RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger, bool showSpinner)
       : mFlinger(flinger), mClient(new Client(&mFlinger)), mShowSpinner(showSpinner) {
     createLayer();
-    primeCache();
+    reset();
 }
 
 bool RefreshRateOverlay::createLayer() {
@@ -202,26 +205,15 @@
     return true;
 }
 
-void RefreshRateOverlay::primeCache() {
-    auto& allRefreshRates = mFlinger.mRefreshRateConfigs->getAllRefreshRates();
-    if (allRefreshRates.size() == 1) {
-        int fps = allRefreshRates.begin()->second->getFps().getIntValue();
-        half4 color = {LOW_FPS_COLOR, ALPHA};
-        mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner));
-        return;
-    }
-
-    std::vector<uint32_t> supportedFps;
-    supportedFps.reserve(allRefreshRates.size());
-    for (auto& [ignored, refreshRate] : allRefreshRates) {
-        supportedFps.push_back(refreshRate->getFps().getIntValue());
-    }
-
-    std::sort(supportedFps.begin(), supportedFps.end());
-    const auto mLowFps = supportedFps[0];
-    const auto mHighFps = supportedFps[supportedFps.size() - 1];
-    for (auto fps : supportedFps) {
-        const auto fpsScale = float(fps - mLowFps) / (mHighFps - mLowFps);
+const std::vector<sp<GraphicBuffer>>& RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) {
+    if (mBufferCache.find(fps) == mBufferCache.end()) {
+        // Ensure the range is > 0, so we don't divide by 0.
+        const auto rangeLength = std::max(1u, mHighFps - mLowFps);
+        // Clip values outside the range [mLowFps, mHighFps]. The current fps may be outside
+        // of this range if the display has changed its set of supported refresh rates.
+        fps = std::max(fps, mLowFps);
+        fps = std::min(fps, mHighFps);
+        const auto fpsScale = static_cast<float>(fps - mLowFps) / rangeLength;
         half4 color;
         color.r = HIGH_FPS_COLOR.r * fpsScale + LOW_FPS_COLOR.r * (1 - fpsScale);
         color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale);
@@ -229,6 +221,8 @@
         color.a = ALPHA;
         mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner));
     }
+
+    return mBufferCache[fps];
 }
 
 void RefreshRateOverlay::setViewport(ui::Size viewport) {
@@ -241,8 +235,8 @@
 
 void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
     mCurrentFps = refreshRate.getFps().getIntValue();
-    auto buffer = mBufferCache[*mCurrentFps][mFrame];
-    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {},
+    auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
+    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
                       mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
 
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
@@ -251,16 +245,22 @@
 void RefreshRateOverlay::onInvalidate() {
     if (!mCurrentFps.has_value()) return;
 
-    const auto& buffers = mBufferCache[*mCurrentFps];
+    const auto& buffers = getOrCreateBuffers(*mCurrentFps);
     mFrame = (mFrame + 1) % buffers.size();
     auto buffer = buffers[mFrame];
-    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {},
+    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
                       mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
 
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
 
+void RefreshRateOverlay::reset() {
+    mBufferCache.clear();
+    mLowFps = mFlinger.mRefreshRateConfigs->getMinRefreshRate().getFps().getIntValue();
+    mHighFps = mFlinger.mRefreshRateConfigs->getMaxRefreshRate().getFps().getIntValue();
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 1a8938f..4ca1337 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -43,6 +43,7 @@
     void setViewport(ui::Size);
     void changeRefreshRate(const RefreshRate&);
     void onInvalidate();
+    void reset();
 
 private:
     class SevenSegmentDrawer {
@@ -71,7 +72,7 @@
     };
 
     bool createLayer();
-    void primeCache();
+    const std::vector<sp<GraphicBuffer>>& getOrCreateBuffers(uint32_t fps);
 
     SurfaceFlinger& mFlinger;
     const sp<Client> mClient;
@@ -87,6 +88,10 @@
     const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f);
 
     const bool mShowSpinner;
+
+    // Interpolate the colors between these values.
+    uint32_t mLowFps;
+    uint32_t mHighFps;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 2511eb3..19b3d6e 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -17,6 +17,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 //#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -28,6 +29,7 @@
 #include <compositionengine/Display.h>
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <cutils/properties.h>
+#include <ftl/future.h>
 #include <gui/IRegionSamplingListener.h>
 #include <gui/SyncScreenCaptureListener.h>
 #include <ui/DisplayStatInfo.h>
@@ -38,7 +40,6 @@
 #include "DisplayDevice.h"
 #include "DisplayRenderArea.h"
 #include "Layer.h"
-#include "Promise.h"
 #include "Scheduler/VsyncController.h"
 #include "SurfaceFlinger.h"
 
@@ -250,8 +251,7 @@
         // If there is relatively little time left for surfaceflinger
         // until the next vsync deadline, defer this sampling work
         // to a later frame, when hopefully there will be more time.
-        DisplayStatInfo stats;
-        mScheduler.getDisplayStatInfo(&stats, systemTime());
+        const DisplayStatInfo stats = mScheduler.getDisplayStatInfo(systemTime());
         if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) {
             ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
             mDiscardedFrames++;
@@ -389,7 +389,7 @@
 
     const Rect sampledBounds = sampleRegion.bounds();
 
-    SurfaceFlinger::RenderAreaFuture renderAreaFuture = promise::defer([=] {
+    SurfaceFlinger::RenderAreaFuture renderAreaFuture = ftl::defer([=] {
         return DisplayRenderArea::create(displayWeak, screencapRegion.bounds(),
                                          sampledBounds.getSize(), ui::Dataspace::V0_SRGB,
                                          orientation);
@@ -501,4 +501,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 499daad..170933d 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -86,9 +86,8 @@
 
 LayerHistory::~LayerHistory() = default;
 
-void LayerHistory::registerLayer(Layer* layer, Fps highRefreshRate, LayerVoteType type) {
-    const nsecs_t highRefreshRatePeriod = highRefreshRate.getPeriodNsecs();
-    auto info = std::make_unique<LayerInfo>(layer->getName(), highRefreshRatePeriod, type);
+void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
+    auto info = std::make_unique<LayerInfo>(layer->getName(), type);
     std::lock_guard lock(mLock);
     mLayerInfos.emplace_back(layer, std::move(info));
 }
@@ -143,8 +142,8 @@
 
         const float layerArea = transformed.getWidth() * transformed.getHeight();
         float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
-        summary.push_back(
-                {strong->getName(), vote.type, vote.fps, vote.seamlessness, weight, layerFocused});
+        summary.push_back({strong->getName(), strong->getOwnerUid(), vote.type, vote.fps,
+                           vote.seamlessness, weight, layerFocused});
 
         if (CC_UNLIKELY(mTraceEnabled)) {
             trace(layer, *info, vote.type, vote.fps.getIntValue());
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 4214bab..bae9b5a 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -46,7 +46,7 @@
     ~LayerHistory();
 
     // Layers are unregistered when the weak reference expires.
-    void registerLayer(Layer*, Fps highRefreshRate, LayerVoteType type);
+    void registerLayer(Layer*, LayerVoteType type);
 
     // Sets the display size. Client is responsible for synchronization.
     void setDisplayArea(uint32_t displayArea) { mDisplayArea = displayArea; }
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 1c0065c..0fa71f1 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
 // #define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
@@ -33,10 +37,8 @@
 const RefreshRateConfigs* LayerInfo::sRefreshRateConfigs = nullptr;
 bool LayerInfo::sTraceEnabled = false;
 
-LayerInfo::LayerInfo(const std::string& name, nsecs_t highRefreshRatePeriod,
-                     LayerHistory::LayerVoteType defaultVote)
+LayerInfo::LayerInfo(const std::string& name, LayerHistory::LayerVoteType defaultVote)
       : mName(name),
-        mHighRefreshRatePeriod(highRefreshRatePeriod),
         mDefaultVote(defaultVote),
         mLayerVote({defaultVote, Fps(0.0f)}),
         mRefreshRateHistory(name) {}
@@ -133,7 +135,7 @@
         }
 
         totalQueueTimeDeltas +=
-                std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
+                std::max(((it + 1)->queueTime - it->queueTime), kMinPeriodBetweenFrames);
         numFrames++;
 
         if (!missingPresentTime && (it->presetTime == 0 || (it + 1)->presetTime == 0)) {
@@ -147,7 +149,7 @@
         }
 
         totalPresentTimeDeltas +=
-                std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
+                std::max(((it + 1)->presetTime - it->presetTime), kMinPeriodBetweenFrames);
     }
 
     // Calculate the average frame time based on presentation timestamps. If those
@@ -298,3 +300,6 @@
 }
 
 } // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 9304e62..427cc9e 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -70,8 +70,7 @@
         sRefreshRateConfigs = &refreshRateConfigs;
     }
 
-    LayerInfo(const std::string& name, nsecs_t highRefreshRatePeriod,
-              LayerHistory::LayerVoteType defaultVote);
+    LayerInfo(const std::string& name, LayerHistory::LayerVoteType defaultVote);
 
     LayerInfo(const LayerInfo&) = delete;
     LayerInfo& operator=(const LayerInfo&) = delete;
@@ -194,8 +193,9 @@
 
     const std::string mName;
 
-    // Used for sanitizing the heuristic data
-    const nsecs_t mHighRefreshRatePeriod;
+    // Used for sanitizing the heuristic data. If two frames are less than
+    // this period apart from each other they'll be considered as duplicates.
+    static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(120.f).getPeriodNsecs();
     LayerHistory::LayerVoteType mDefaultVote;
 
     LayerVote mLayerVote;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 47a4f42..7ff0ddf 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -69,17 +69,33 @@
 
 // TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly
 // and remove the EventThread from MessageQueue
-void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) {
-    if (mEventTube.getFd() >= 0) {
-        mLooper->removeFd(mEventTube.getFd());
+void MessageQueue::setInjector(sp<EventThreadConnection> connection) {
+    auto& tube = mInjector.tube;
+
+    if (const int fd = tube.getFd(); fd >= 0) {
+        mLooper->removeFd(fd);
     }
 
-    mEvents = connection;
-    if (mEvents) {
-        mEvents->stealReceiveChannel(&mEventTube);
-        mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
-                       this);
+    if (connection) {
+        // The EventThreadConnection is retained when disabling injection, so avoid subsequently
+        // stealing invalid FDs. Note that the stolen FDs are kept open.
+        if (tube.getFd() < 0) {
+            connection->stealReceiveChannel(&tube);
+        } else {
+            ALOGW("Recycling channel for VSYNC injection.");
+        }
+
+        mLooper->addFd(
+                tube.getFd(), 0, Looper::EVENT_INPUT,
+                [](int, int, void* data) {
+                    reinterpret_cast<MessageQueue*>(data)->injectorCallback();
+                    return 1; // Keep registration.
+                },
+                this);
     }
+
+    std::lock_guard lock(mInjector.mutex);
+    mInjector.connection = std::move(connection);
 }
 
 void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
@@ -149,29 +165,31 @@
 
 void MessageQueue::invalidate() {
     ATRACE_CALL();
-    if (mEvents) {
-        mEvents->requestNextVsync();
-    } else {
-        std::lock_guard lock(mVsync.mutex);
-        mVsync.mScheduled = true;
-        mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
-                                       mVsync.lastCallbackTime.count()});
+
+    {
+        std::lock_guard lock(mInjector.mutex);
+        if (CC_UNLIKELY(mInjector.connection)) {
+            ALOGD("%s while injecting VSYNC", __FUNCTION__);
+            mInjector.connection->requestNextVsync();
+            return;
+        }
     }
+
+    std::lock_guard lock(mVsync.mutex);
+    mVsync.mScheduled = true;
+    mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
+                                   .readyDuration = 0,
+                                   .earliestVsync = mVsync.lastCallbackTime.count()});
 }
 
 void MessageQueue::refresh() {
     mHandler->dispatchRefresh();
 }
 
-int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
-    MessageQueue* queue = reinterpret_cast<MessageQueue*>(data);
-    return queue->eventReceiver(fd, events);
-}
-
-int MessageQueue::eventReceiver(int /*fd*/, int /*events*/) {
+void MessageQueue::injectorCallback() {
     ssize_t n;
     DisplayEventReceiver::Event buffer[8];
-    while ((n = DisplayEventReceiver::getEvents(&mEventTube, buffer, 8)) > 0) {
+    while ((n = DisplayEventReceiver::getEvents(&mInjector.tube, buffer, 8)) > 0) {
         for (int i = 0; i < n; i++) {
             if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
                 mHandler->dispatchInvalidate(buffer[i].vsync.vsyncId,
@@ -180,8 +198,6 @@
             }
         }
     }
-    return 1;
 }
 
 } // namespace android::impl
-
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 99ce3a6..2934af0 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -21,12 +21,11 @@
 #include <type_traits>
 #include <utility>
 
-#include <utils/Looper.h>
-#include <utils/Timers.h>
-#include <utils/threads.h>
-
+#include <android-base/thread_annotations.h>
 #include <gui/IDisplayEventConnection.h>
 #include <private/gui/BitTube.h>
+#include <utils/Looper.h>
+#include <utils/Timers.h>
 
 #include "EventThread.h"
 #include "TracedOrdinal.h"
@@ -68,7 +67,7 @@
     virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
                            std::chrono::nanoseconds workDuration) = 0;
     virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
-    virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0;
+    virtual void setInjector(sp<EventThreadConnection>) = 0;
     virtual void waitMessage() = 0;
     virtual void postMessage(sp<MessageHandler>&&) = 0;
     virtual void invalidate() = 0;
@@ -99,7 +98,6 @@
 
     sp<SurfaceFlinger> mFlinger;
     sp<Looper> mLooper;
-    sp<EventThreadConnection> mEvents;
 
     struct Vsync {
         frametimeline::TokenManager* tokenManager = nullptr;
@@ -113,14 +111,19 @@
         TracedOrdinal<int> value = {"VSYNC-sf", 0};
     };
 
-    Vsync mVsync;
+    struct Injector {
+        gui::BitTube tube;
+        std::mutex mutex;
+        sp<EventThreadConnection> connection GUARDED_BY(mutex);
+    };
 
-    gui::BitTube mEventTube;
+    Vsync mVsync;
+    Injector mInjector;
+
     sp<Handler> mHandler;
 
-    static int cb_eventReceiver(int fd, int events, void* data);
-    int eventReceiver(int fd, int events);
     void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
+    void injectorCallback();
 
 public:
     ~MessageQueue() override = default;
@@ -128,7 +131,7 @@
     void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
                    std::chrono::nanoseconds workDuration) override;
     void setDuration(std::chrono::nanoseconds workDuration) override;
-    void setEventConnection(const sp<EventThreadConnection>& connection) override;
+    void setInjector(sp<EventThreadConnection>) override;
 
     void waitMessage() override;
     void postMessage(sp<MessageHandler>&&) override;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 4b7251b..35b382e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -17,6 +17,10 @@
 // #define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
 #include "RefreshRateConfigs.h"
 #include <android-base/stringprintf.h>
 #include <utils/Trace.h>
@@ -71,16 +75,84 @@
 
 std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod,
                                                                  nsecs_t displayPeriod) const {
-    auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod);
-    if (displayFramesRem <= MARGIN_FOR_PERIOD_CALCULATION ||
-        std::abs(displayFramesRem - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
-        displayFramesQuot++;
-        displayFramesRem = 0;
+    auto [quotient, remainder] = std::div(layerPeriod, displayPeriod);
+    if (remainder <= MARGIN_FOR_PERIOD_CALCULATION ||
+        std::abs(remainder - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
+        quotient++;
+        remainder = 0;
     }
 
-    return {displayFramesQuot, displayFramesRem};
+    return {quotient, remainder};
 }
 
+float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
+                                                    const RefreshRate& refreshRate,
+                                                    bool isSeamlessSwitch) const {
+    // Slightly prefer seamless switches.
+    constexpr float kSeamedSwitchPenalty = 0.95f;
+    const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
+
+    // If the layer wants Max, give higher score to the higher refresh rate
+    if (layer.vote == LayerVoteType::Max) {
+        const auto ratio =
+                refreshRate.fps.getValue() / mAppRequestRefreshRates.back()->fps.getValue();
+        // use ratio^2 to get a lower score the more we get further from peak
+        return ratio * ratio;
+    }
+
+    const auto displayPeriod = refreshRate.getVsyncPeriod();
+    const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
+    if (layer.vote == LayerVoteType::ExplicitDefault) {
+        // Find the actual rate the layer will render, assuming
+        // that layerPeriod is the minimal time to render a frame
+        auto actualLayerPeriod = displayPeriod;
+        int multiplier = 1;
+        while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
+            multiplier++;
+            actualLayerPeriod = displayPeriod * multiplier;
+        }
+        return std::min(1.0f,
+                        static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
+    }
+
+    if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
+        layer.vote == LayerVoteType::Heuristic) {
+        // Calculate how many display vsyncs we need to present a single frame for this
+        // layer
+        const auto [displayFramesQuotient, displayFramesRemainder] =
+                getDisplayFrames(layerPeriod, displayPeriod);
+        static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
+        if (displayFramesRemainder == 0) {
+            // Layer desired refresh rate matches the display rate.
+            return 1.0f * seamlessness;
+        }
+
+        if (displayFramesQuotient == 0) {
+            // Layer desired refresh rate is higher than the display rate.
+            return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
+                    (1.0f / (MAX_FRAMES_TO_FIT + 1));
+        }
+
+        // Layer desired refresh rate is lower than the display rate. Check how well it fits
+        // the cadence.
+        auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder));
+        int iter = 2;
+        while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
+            diff = diff - (displayPeriod - diff);
+            iter++;
+        }
+
+        return (1.0f / iter) * seamlessness;
+    }
+
+    return 0;
+}
+
+struct RefreshRateScore {
+    const RefreshRate* refreshRate;
+    float score;
+};
+
 const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
         const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
         GlobalSignals* outSignalsConsidered) const {
@@ -165,11 +237,11 @@
     }
 
     // Find the best refresh rate based on score
-    std::vector<std::pair<const RefreshRate*, float>> scores;
+    std::vector<RefreshRateScore> scores;
     scores.reserve(mAppRequestRefreshRates.size());
 
     for (const auto refreshRate : mAppRequestRefreshRates) {
-        scores.emplace_back(refreshRate, 0.0f);
+        scores.emplace_back(RefreshRateScore{refreshRate, 0.0f});
     }
 
     const auto& defaultConfig = mRefreshRates.at(policy->defaultConfig);
@@ -184,12 +256,13 @@
         auto weight = layer.weight;
 
         for (auto i = 0u; i < scores.size(); i++) {
-            const bool isSeamlessSwitch =
-                    scores[i].first->getConfigGroup() == mCurrentRefreshRate->getConfigGroup();
+            const bool isSeamlessSwitch = scores[i].refreshRate->getConfigGroup() ==
+                    mCurrentRefreshRate->getConfigGroup();
 
             if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
                 ALOGV("%s ignores %s to avoid non-seamless switch. Current config = %s",
-                      formatLayerInfo(layer, weight).c_str(), scores[i].first->toString().c_str(),
+                      formatLayerInfo(layer, weight).c_str(),
+                      scores[i].refreshRate->toString().c_str(),
                       mCurrentRefreshRate->toString().c_str());
                 continue;
             }
@@ -198,7 +271,8 @@
                 !layer.focused) {
                 ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
                       " Current config = %s",
-                      formatLayerInfo(layer, weight).c_str(), scores[i].first->toString().c_str(),
+                      formatLayerInfo(layer, weight).c_str(),
+                      scores[i].refreshRate->toString().c_str(),
                       mCurrentRefreshRate->toString().c_str());
                 continue;
             }
@@ -209,18 +283,20 @@
             // from the default, this means a layer with seamlessness=SeamedAndSeamless has just
             // disappeared.
             const bool isInPolicyForDefault = seamedLayers > 0
-                    ? scores[i].first->getConfigGroup() == mCurrentRefreshRate->getConfigGroup()
-                    : scores[i].first->getConfigGroup() == defaultConfig->getConfigGroup();
+                    ? scores[i].refreshRate->getConfigGroup() ==
+                            mCurrentRefreshRate->getConfigGroup()
+                    : scores[i].refreshRate->getConfigGroup() == defaultConfig->getConfigGroup();
 
             if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault &&
                 !layer.focused) {
                 ALOGV("%s ignores %s. Current config = %s", formatLayerInfo(layer, weight).c_str(),
-                      scores[i].first->toString().c_str(), mCurrentRefreshRate->toString().c_str());
+                      scores[i].refreshRate->toString().c_str(),
+                      mCurrentRefreshRate->toString().c_str());
                 continue;
             }
 
-            bool inPrimaryRange =
-                    scores[i].first->inPolicy(policy->primaryRange.min, policy->primaryRange.max);
+            bool inPrimaryRange = scores[i].refreshRate->inPolicy(policy->primaryRange.min,
+                                                                  policy->primaryRange.max);
             if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
                 !(layer.focused && layer.vote == LayerVoteType::ExplicitDefault)) {
                 // Only focused layers with ExplicitDefault frame rate settings are allowed to score
@@ -228,81 +304,11 @@
                 continue;
             }
 
-            // If the layer wants Max, give higher score to the higher refresh rate
-            if (layer.vote == LayerVoteType::Max) {
-                const auto ratio =
-                        scores[i].first->fps.getValue() / scores.back().first->fps.getValue();
-                // use ratio^2 to get a lower score the more we get further from peak
-                const auto layerScore = ratio * ratio;
-                ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
-                      scores[i].first->getName().c_str(), layerScore);
-                scores[i].second += weight * layerScore;
-                continue;
-            }
-
-            const auto displayPeriod = scores[i].first->hwcConfig->getVsyncPeriod();
-            const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
-            if (layer.vote == LayerVoteType::ExplicitDefault) {
-                const auto layerScore = [&]() {
-                    // Find the actual rate the layer will render, assuming
-                    // that layerPeriod is the minimal time to render a frame
-                    auto actualLayerPeriod = displayPeriod;
-                    int multiplier = 1;
-                    while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
-                        multiplier++;
-                        actualLayerPeriod = displayPeriod * multiplier;
-                    }
-                    return std::min(1.0f,
-                                    static_cast<float>(layerPeriod) /
-                                            static_cast<float>(actualLayerPeriod));
-                }();
-
-                ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
-                      scores[i].first->getName().c_str(), layerScore);
-                scores[i].second += weight * layerScore;
-                continue;
-            }
-
-            if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
-                layer.vote == LayerVoteType::Heuristic) {
-                const auto layerScore = [&] {
-                    // Calculate how many display vsyncs we need to present a single frame for this
-                    // layer
-                    const auto [displayFramesQuot, displayFramesRem] =
-                            getDisplayFrames(layerPeriod, displayPeriod);
-                    static constexpr size_t MAX_FRAMES_TO_FIT =
-                            10; // Stop calculating when score < 0.1
-                    if (displayFramesRem == 0) {
-                        // Layer desired refresh rate matches the display rate.
-                        return 1.0f;
-                    }
-
-                    if (displayFramesQuot == 0) {
-                        // Layer desired refresh rate is higher the display rate.
-                        return (static_cast<float>(layerPeriod) /
-                                static_cast<float>(displayPeriod)) *
-                                (1.0f / (MAX_FRAMES_TO_FIT + 1));
-                    }
-
-                    // Layer desired refresh rate is lower the display rate. Check how well it fits
-                    // the cadence
-                    auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
-                    int iter = 2;
-                    while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
-                        diff = diff - (displayPeriod - diff);
-                        iter++;
-                    }
-
-                    return 1.0f / iter;
-                }();
-                // Slightly prefer seamless switches.
-                constexpr float kSeamedSwitchPenalty = 0.95f;
-                const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
-                ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
-                      scores[i].first->getName().c_str(), layerScore);
-                scores[i].second += weight * layerScore * seamlessness;
-                continue;
-            }
+            const auto layerScore =
+                    calculateLayerScoreLocked(layer, *scores[i].refreshRate, isSeamlessSwitch);
+            ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
+                  scores[i].refreshRate->getName().c_str(), layerScore);
+            scores[i].score += weight * layerScore;
         }
     }
 
@@ -317,7 +323,7 @@
         // If we never scored any layers, then choose the rate from the primary
         // range instead of picking a random score from the app range.
         if (std::all_of(scores.begin(), scores.end(),
-                        [](std::pair<const RefreshRate*, float> p) { return p.second == 0; })) {
+                        [](RefreshRateScore score) { return score.score == 0; })) {
             ALOGV("layers not scored - choose %s",
                   getMaxRefreshRateByPolicyLocked().getName().c_str());
             return getMaxRefreshRateByPolicyLocked();
@@ -342,11 +348,111 @@
     return *bestRefreshRate;
 }
 
+std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>>
+groupLayersByUid(const std::vector<RefreshRateConfigs::LayerRequirement>& layers) {
+    std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>> layersByUid;
+    for (const auto& layer : layers) {
+        auto iter = layersByUid.emplace(layer.ownerUid,
+                                        std::vector<const RefreshRateConfigs::LayerRequirement*>());
+        auto& layersWithSameUid = iter.first->second;
+        layersWithSameUid.push_back(&layer);
+    }
+
+    // Remove uids that can't have a frame rate override
+    for (auto iter = layersByUid.begin(); iter != layersByUid.end();) {
+        const auto& layersWithSameUid = iter->second;
+        bool skipUid = false;
+        for (const auto& layer : layersWithSameUid) {
+            if (layer->vote == RefreshRateConfigs::LayerVoteType::Max ||
+                layer->vote == RefreshRateConfigs::LayerVoteType::Heuristic) {
+                skipUid = true;
+                break;
+            }
+        }
+        if (skipUid) {
+            iter = layersByUid.erase(iter);
+        } else {
+            ++iter;
+        }
+    }
+
+    return layersByUid;
+}
+
+std::vector<RefreshRateScore> initializeScoresForAllRefreshRates(
+        const AllRefreshRatesMapType& refreshRates) {
+    std::vector<RefreshRateScore> scores;
+    scores.reserve(refreshRates.size());
+    for (const auto& [ignored, refreshRate] : refreshRates) {
+        scores.emplace_back(RefreshRateScore{refreshRate.get(), 0.0f});
+    }
+    std::sort(scores.begin(), scores.end(),
+              [](const auto& a, const auto& b) { return *a.refreshRate < *b.refreshRate; });
+    return scores;
+}
+
+RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides(
+        const std::vector<LayerRequirement>& layers, Fps displayFrameRate) const {
+    ATRACE_CALL();
+    if (!mSupportsFrameRateOverride) return {};
+
+    ALOGV("getFrameRateOverrides %zu layers", layers.size());
+    std::lock_guard lock(mLock);
+    std::vector<RefreshRateScore> scores = initializeScoresForAllRefreshRates(mRefreshRates);
+    std::unordered_map<uid_t, std::vector<const LayerRequirement*>> layersByUid =
+            groupLayersByUid(layers);
+    UidToFrameRateOverride frameRateOverrides;
+    for (const auto& [uid, layersWithSameUid] : layersByUid) {
+        for (auto& score : scores) {
+            score.score = 0;
+        }
+
+        for (const auto& layer : layersWithSameUid) {
+            if (layer->vote == LayerVoteType::NoVote || layer->vote == LayerVoteType::Min) {
+                continue;
+            }
+
+            LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
+                                layer->vote != LayerVoteType::ExplicitExactOrMultiple);
+            for (RefreshRateScore& score : scores) {
+                const auto layerScore = calculateLayerScoreLocked(*layer, *score.refreshRate,
+                                                                  /*isSeamlessSwitch*/ true);
+                score.score += layer->weight * layerScore;
+            }
+        }
+
+        // We just care about the refresh rates which are a divider of the
+        // display refresh rate
+        auto iter =
+                std::remove_if(scores.begin(), scores.end(), [&](const RefreshRateScore& score) {
+                    return getFrameRateDivider(displayFrameRate, score.refreshRate->getFps()) == 0;
+                });
+        scores.erase(iter, scores.end());
+
+        // If we never scored any layers, we don't have a preferred frame rate
+        if (std::all_of(scores.begin(), scores.end(),
+                        [](const RefreshRateScore& score) { return score.score == 0; })) {
+            continue;
+        }
+
+        // Now that we scored all the refresh rates we need to pick the one that got the highest
+        // score.
+        const RefreshRate* bestRefreshRate = getBestRefreshRate(scores.begin(), scores.end());
+
+        // If the nest refresh rate is the current one, we don't have an override
+        if (!bestRefreshRate->getFps().equalsWithMargin(displayFrameRate)) {
+            frameRateOverrides.emplace(uid, bestRefreshRate->getFps());
+        }
+    }
+
+    return frameRateOverrides;
+}
+
 template <typename Iter>
 const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
     constexpr auto EPSILON = 0.001f;
-    const RefreshRate* bestRefreshRate = begin->first;
-    float max = begin->second;
+    const RefreshRate* bestRefreshRate = begin->refreshRate;
+    float max = begin->score;
     for (auto i = begin; i != end; ++i) {
         const auto [refreshRate, score] = *i;
         ALOGV("%s scores %.2f", refreshRate->getName().c_str(), score);
@@ -362,10 +468,6 @@
     return bestRefreshRate;
 }
 
-const AllRefreshRatesMapType& RefreshRateConfigs::getAllRefreshRates() const {
-    return mRefreshRates;
-}
-
 const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
     std::lock_guard lock(mLock);
     return getMinRefreshRateByPolicyLocked();
@@ -431,6 +533,7 @@
         HwcConfigIndexType currentConfigId)
       : mKnownFrameRates(constructKnownFrameRates(configs)) {
     LOG_ALWAYS_FATAL_IF(configs.empty());
+    LOG_ALWAYS_FATAL_IF(currentConfigId.value() < 0);
     LOG_ALWAYS_FATAL_IF(currentConfigId.value() >= configs.size());
 
     for (auto configId = HwcConfigIndexType(0); configId.value() < configs.size(); configId++) {
@@ -450,6 +553,16 @@
     mDisplayManagerPolicy.defaultConfig = currentConfigId;
     mMinSupportedRefreshRate = sortedConfigs.front();
     mMaxSupportedRefreshRate = sortedConfigs.back();
+
+    mSupportsFrameRateOverride = false;
+    for (const auto& config1 : sortedConfigs) {
+        for (const auto& config2 : sortedConfigs) {
+            if (getFrameRateDivider(config1->getFps(), config2->getFps()) >= 2) {
+                mSupportsFrameRateOverride = true;
+                break;
+            }
+        }
+    }
     constructAvailableRefreshRates();
 }
 
@@ -649,50 +762,22 @@
     return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
 }
 
-void RefreshRateConfigs::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
-    if (frameRateOverride.frameRateHz > 0 && frameRateOverride.frameRateHz < 1) {
-        return;
-    }
-
-    std::lock_guard lock(mLock);
-    if (frameRateOverride.frameRateHz != 0) {
-        mPreferredRefreshRateForUid[frameRateOverride.uid] = Fps(frameRateOverride.frameRateHz);
-    } else {
-        mPreferredRefreshRateForUid.erase(frameRateOverride.uid);
-    }
-}
-
-int RefreshRateConfigs::getRefreshRateDividerForUid(uid_t uid) const {
-    std::lock_guard lock(mLock);
-
-    const auto iter = mPreferredRefreshRateForUid.find(uid);
-    if (iter == mPreferredRefreshRateForUid.end()) {
-        return 1;
-    }
-
+int RefreshRateConfigs::getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate) {
     // This calculation needs to be in sync with the java code
     // in DisplayManagerService.getDisplayInfoForFrameRateOverride
     constexpr float kThreshold = 0.1f;
-    const auto refreshRateHz = iter->second;
-    const auto numPeriods = mCurrentRefreshRate->getFps().getValue() / refreshRateHz.getValue();
+    const auto numPeriods = displayFrameRate.getValue() / layerFrameRate.getValue();
     const auto numPeriodsRounded = std::round(numPeriods);
     if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
-        return 1;
+        return 0;
     }
 
     return static_cast<int>(numPeriodsRounded);
 }
 
-std::vector<FrameRateOverride> RefreshRateConfigs::getFrameRateOverrides() {
+int RefreshRateConfigs::getRefreshRateDivider(Fps frameRate) const {
     std::lock_guard lock(mLock);
-    std::vector<FrameRateOverride> overrides;
-    overrides.reserve(mPreferredRefreshRateForUid.size());
-
-    for (const auto [uid, frameRate] : mPreferredRefreshRateForUid) {
-        overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
-    }
-
-    return overrides;
+    return getFrameRateDivider(mCurrentRefreshRate->getFps(), frameRate);
 }
 
 void RefreshRateConfigs::dump(std::string& result) const {
@@ -714,7 +799,12 @@
         base::StringAppendF(&result, "\t%s\n", refreshRate->toString().c_str());
     }
 
+    base::StringAppendF(&result, "Supports Frame Rate Override: %s\n",
+                        mSupportsFrameRateOverride ? "yes" : "no");
     result.append("\n");
 }
 
 } // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index ec7ffe5..e4bbf7f 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -221,6 +221,8 @@
     struct LayerRequirement {
         // Layer's name. Used for debugging purposes.
         std::string name;
+        // Layer's owner uid
+        uid_t ownerUid = static_cast<uid_t>(-1);
         // Layer vote type.
         LayerVoteType vote = LayerVoteType::NoVote;
         // Layer's desired refresh rate, if applicable.
@@ -261,9 +263,6 @@
                                           GlobalSignals* outSignalsConsidered = nullptr) const
             EXCLUDES(mLock);
 
-    // Returns all the refresh rates supported by the device. This won't change at runtime.
-    const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock);
-
     // Returns the lowest refresh rate supported by the device. This won't change at runtime.
     const RefreshRate& getMinRefreshRate() const { return *mMinSupportedRefreshRate; }
 
@@ -319,18 +318,18 @@
     // refresh rates.
     KernelIdleTimerAction getIdleTimerAction() const;
 
-    // Stores the preferred refresh rate that an app should run at.
-    // FrameRateOverride.refreshRateHz == 0 means no preference.
-    void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mLock);
+    bool supportsFrameRateOverride() const { return mSupportsFrameRateOverride; }
 
     // Returns a divider for the current refresh rate
-    int getRefreshRateDividerForUid(uid_t) const EXCLUDES(mLock);
+    int getRefreshRateDivider(Fps frameRate) const EXCLUDES(mLock);
+
+    // Returns the frame rate override for each uid
+    using UidToFrameRateOverride = std::map<uid_t, Fps>;
+    UidToFrameRateOverride getFrameRateOverrides(const std::vector<LayerRequirement>& layers,
+                                                 Fps displayFrameRate) const EXCLUDES(mLock);
 
     void dump(std::string& result) const EXCLUDES(mLock);
 
-    // Returns the current frame rate overrides
-    std::vector<FrameRateOverride> getFrameRateOverrides() EXCLUDES(mLock);
-
 private:
     friend class RefreshRateConfigsTest;
 
@@ -367,6 +366,16 @@
     const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
     bool isPolicyValid(const Policy& policy);
 
+    // Return the display refresh rate divider to match the layer
+    // frame rate, or 0 if the display refresh rate is not a multiple of the
+    // layer refresh rate.
+    static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate);
+
+    // calculates a score for a layer. Used to determine the display refresh rate
+    // and the frame rate override for certains applications.
+    float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
+                                    bool isSeamlessSwitch) const REQUIRES(mLock);
+
     // The list of refresh rates, indexed by display config ID. This must not change after this
     // object is initialized.
     AllRefreshRatesMapType mRefreshRates;
@@ -388,10 +397,6 @@
     Policy mDisplayManagerPolicy GUARDED_BY(mLock);
     std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
 
-    // A mapping between a UID and a preferred refresh rate that this app would
-    // run at.
-    std::unordered_map<uid_t, Fps> mPreferredRefreshRateForUid GUARDED_BY(mLock);
-
     // The min and max refresh rates supported by the device.
     // This will not change at runtime.
     const RefreshRate* mMinSupportedRefreshRate;
@@ -402,6 +407,8 @@
     // A sorted list of known frame rates that a Heuristic layer will choose
     // from based on the closest value.
     const std::vector<Fps> mKnownFrameRates;
+
+    bool mSupportsFrameRateOverride;
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 07411b0..18c899b 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -206,8 +206,36 @@
                                                        readyDuration, traceVsync, name);
 }
 
+std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
+    std::lock_guard lock(mFrameRateOverridesMutex);
+    {
+        const auto iter = mFrameRateOverridesFromBackdoor.find(uid);
+        if (iter != mFrameRateOverridesFromBackdoor.end()) {
+            return std::make_optional<Fps>(iter->second);
+        }
+    }
+
+    {
+        const auto iter = mFrameRateOverridesByContent.find(uid);
+        if (iter != mFrameRateOverridesByContent.end()) {
+            return std::make_optional<Fps>(iter->second);
+        }
+    }
+
+    return std::nullopt;
+}
+
 bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const {
-    const auto divider = mRefreshRateConfigs.getRefreshRateDividerForUid(uid);
+    if (!mRefreshRateConfigs.supportsFrameRateOverride()) {
+        return true;
+    }
+
+    const auto frameRate = getFrameRateOverride(uid);
+    if (!frameRate.has_value()) {
+        return true;
+    }
+
+    const auto divider = mRefreshRateConfigs.getRefreshRateDivider(*frameRate);
     if (divider <= 1) {
         return true;
     }
@@ -215,14 +243,22 @@
     return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, divider);
 }
 
+impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
+    if (!mRefreshRateConfigs.supportsFrameRateOverride()) {
+        return {};
+    }
+
+    return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
+        return !isVsyncValid(expectedVsyncTimestamp, uid);
+    };
+}
+
 Scheduler::ConnectionHandle Scheduler::createConnection(
         const char* connectionName, frametimeline::TokenManager* tokenManager,
         std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
         impl::EventThread::InterceptVSyncsCallback interceptCallback) {
     auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);
-    auto throttleVsync = [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
-        return !isVsyncValid(expectedVsyncTimestamp, uid);
-    };
+    auto throttleVsync = makeThrottleVsyncCallback();
     auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager,
                                                            std::move(interceptCallback),
                                                            std::move(throttleVsync));
@@ -290,11 +326,22 @@
     thread->onScreenReleased();
 }
 
-void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
-                                            std::vector<FrameRateOverride> overrides) {
+void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
+    std::vector<FrameRateOverride> overrides;
+    {
+        std::lock_guard lock(mFrameRateOverridesMutex);
+        for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+            overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+        }
+        for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
+            if (mFrameRateOverridesFromBackdoor.count(uid) == 0) {
+                overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+            }
+        }
+    }
     android::EventThread* thread;
     {
-        std::lock_guard<std::mutex> lock(mConnectionsLock);
+        std::lock_guard lock(mConnectionsLock);
         RETURN_IF_INVALID_HANDLE(handle);
         thread = mConnections[handle].thread.get();
     }
@@ -303,9 +350,11 @@
 
 void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
                                               HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
-    std::lock_guard<std::mutex> lock(mFeatureStateLock);
-    // Cache the last reported config for primary display.
-    mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod};
+    {
+        std::lock_guard<std::mutex> lock(mFeatureStateLock);
+        // Cache the last reported config for primary display.
+        mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod};
+    }
     onNonPrimaryDisplayConfigChanged(handle, displayId, configId, vsyncPeriod);
 }
 
@@ -377,9 +426,10 @@
     thread->setDuration(workDuration, readyDuration);
 }
 
-void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats, nsecs_t now) {
-    stats->vsyncTime = mVsyncSchedule.tracker->nextAnticipatedVSyncTimeFrom(now);
-    stats->vsyncPeriod = mVsyncSchedule.tracker->currentPeriod();
+DisplayStatInfo Scheduler::getDisplayStatInfo(nsecs_t now) {
+    const auto vsyncTime = mVsyncSchedule.tracker->nextAnticipatedVSyncTimeFrom(now);
+    const auto vsyncPeriod = mVsyncSchedule.tracker->currentPeriod();
+    return DisplayStatInfo{.vsyncTime = vsyncTime, .vsyncPeriod = vsyncPeriod};
 }
 
 Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
@@ -399,6 +449,10 @@
                                                     impl::EventThread::InterceptVSyncsCallback(),
                                                     impl::EventThread::ThrottleVsyncCallback());
 
+        // EventThread does not dispatch VSYNC unless the display is connected and powered on.
+        eventThread->onHotplugReceived(PhysicalDisplayId::fromPort(0), true);
+        eventThread->onScreenAcquired();
+
         mInjectorConnectionHandle = createConnection(std::move(eventThread));
     }
 
@@ -510,23 +564,19 @@
 void Scheduler::registerLayer(Layer* layer) {
     if (!mLayerHistory) return;
 
-    const auto maxFps = mRefreshRateConfigs.getMaxRefreshRate().getFps();
-
     if (layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
-        mLayerHistory->registerLayer(layer, maxFps, scheduler::LayerHistory::LayerVoteType::NoVote);
+        mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::NoVote);
     } else if (!mOptions.useContentDetection) {
         // If the content detection feature is off, all layers are registered at Max. We still keep
         // the layer history, since we use it for other features (like Frame Rate API), so layers
         // still need to be registered.
-        mLayerHistory->registerLayer(layer, maxFps, scheduler::LayerHistory::LayerVoteType::Max);
+        mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::Max);
     } else {
         if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
             // Running Wallpaper at Min is considered as part of content detection.
-            mLayerHistory->registerLayer(layer, maxFps,
-                                         scheduler::LayerHistory::LayerVoteType::Min);
+            mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::Min);
         } else {
-            mLayerHistory->registerLayer(layer, maxFps,
-                                         scheduler::LayerHistory::LayerVoteType::Heuristic);
+            mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::Heuristic);
         }
     }
 }
@@ -550,7 +600,10 @@
     ATRACE_CALL();
 
     scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime());
+    scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
     HwcConfigIndexType newConfigId;
+    bool frameRateChanged;
+    bool frameRateOverridesChanged;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
         if (mFeatures.contentRequirements == summary) {
@@ -558,22 +611,32 @@
         }
         mFeatures.contentRequirements = summary;
 
-        scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
         newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
+        auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+        frameRateOverridesChanged =
+                updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
+
         if (mFeatures.configId == newConfigId) {
             // We don't need to change the config, but we might need to send an event
             // about a config change, since it was suppressed due to a previous idleConsidered
             if (!consideredSignals.idle) {
                 dispatchCachedReportedConfig();
             }
-            return;
+            frameRateChanged = false;
+        } else {
+            mFeatures.configId = newConfigId;
+            frameRateChanged = true;
         }
-        mFeatures.configId = newConfigId;
+    }
+    if (frameRateChanged) {
         auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
         mSchedulerCallback.changeRefreshRate(newRefreshRate,
                                              consideredSignals.idle ? ConfigEvent::None
                                                                     : ConfigEvent::Changed);
     }
+    if (frameRateOverridesChanged) {
+        mSchedulerCallback.triggerOnFrameRateOverridesChanged();
+    }
 }
 
 void Scheduler::resetIdleTimer() {
@@ -666,6 +729,21 @@
     StringAppendF(&result, "+  Content detection: %s %s\n\n",
                   toContentDetectionString(mOptions.useContentDetection),
                   mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
+
+    {
+        std::lock_guard lock(mFrameRateOverridesMutex);
+        StringAppendF(&result, "Frame Rate Overrides (backdoor): {");
+        for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+            StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+        }
+        StringAppendF(&result, "}\n");
+
+        StringAppendF(&result, "Frame Rate Overrides (setFrameRate): {");
+        for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
+            StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+        }
+        StringAppendF(&result, "}\n");
+    }
 }
 
 void Scheduler::dumpVsync(std::string& s) const {
@@ -677,9 +755,41 @@
     mVsyncSchedule.dispatch->dump(s);
 }
 
+bool Scheduler::updateFrameRateOverrides(
+        scheduler::RefreshRateConfigs::GlobalSignals consideredSignals, Fps displayRefreshRate) {
+    if (!mRefreshRateConfigs.supportsFrameRateOverride()) {
+        return false;
+    }
+
+    if (consideredSignals.touch) {
+        std::lock_guard lock(mFrameRateOverridesMutex);
+        const bool changed = !mFrameRateOverridesByContent.empty();
+        mFrameRateOverridesByContent.clear();
+        return changed;
+    }
+
+    if (!consideredSignals.idle) {
+        const auto frameRateOverrides =
+                mRefreshRateConfigs.getFrameRateOverrides(mFeatures.contentRequirements,
+                                                          displayRefreshRate);
+        std::lock_guard lock(mFrameRateOverridesMutex);
+        if (!std::equal(mFrameRateOverridesByContent.begin(), mFrameRateOverridesByContent.end(),
+                        frameRateOverrides.begin(), frameRateOverrides.end(),
+                        [](const std::pair<uid_t, Fps>& a, const std::pair<uid_t, Fps>& b) {
+                            return a.first == b.first && a.second.equalsWithMargin(b.second);
+                        })) {
+            mFrameRateOverridesByContent = frameRateOverrides;
+            return true;
+        }
+    }
+    return false;
+}
+
 template <class T>
 bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
     HwcConfigIndexType newConfigId;
+    bool refreshRateChanged = false;
+    bool frameRateOverridesChanged;
     scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
@@ -688,20 +798,32 @@
         }
         *currentState = newState;
         newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
+        const RefreshRate& newRefreshRate =
+                mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+        frameRateOverridesChanged =
+                updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
         if (mFeatures.configId == newConfigId) {
             // We don't need to change the config, but we might need to send an event
             // about a config change, since it was suppressed due to a previous idleConsidered
             if (!consideredSignals.idle) {
                 dispatchCachedReportedConfig();
             }
-            return consideredSignals.touch;
+        } else {
+            mFeatures.configId = newConfigId;
+            refreshRateChanged = true;
         }
-        mFeatures.configId = newConfigId;
     }
-    const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
-    mSchedulerCallback.changeRefreshRate(newRefreshRate,
-                                         consideredSignals.idle ? ConfigEvent::None
-                                                                : ConfigEvent::Changed);
+    if (refreshRateChanged) {
+        const RefreshRate& newRefreshRate =
+                mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+
+        mSchedulerCallback.changeRefreshRate(newRefreshRate,
+                                             consideredSignals.idle ? ConfigEvent::None
+                                                                    : ConfigEvent::Changed);
+    }
+    if (frameRateOverridesChanged) {
+        mSchedulerCallback.triggerOnFrameRateOverridesChanged();
+    }
     return consideredSignals.touch;
 }
 
@@ -775,4 +897,17 @@
     }
 }
 
+void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
+    if (frameRateOverride.frameRateHz > 0.f && frameRateOverride.frameRateHz < 1.f) {
+        return;
+    }
+
+    std::lock_guard lock(mFrameRateOverridesMutex);
+    if (frameRateOverride.frameRateHz != 0.f) {
+        mFrameRateOverridesFromBackdoor[frameRateOverride.uid] = Fps(frameRateOverride.frameRateHz);
+    } else {
+        mFrameRateOverridesFromBackdoor.erase(frameRateOverride.uid);
+    }
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index f16e1f9..cae3fe7 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -26,8 +26,9 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 #include <ui/GraphicTypes.h>
-#pragma clang diagnostic pop
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 #include "EventThread.h"
 #include "LayerHistory.h"
@@ -60,6 +61,7 @@
                                    scheduler::RefreshRateConfigEvent) = 0;
     virtual void repaintEverythingForHWC() = 0;
     virtual void kernelTimerChanged(bool expired) = 0;
+    virtual void triggerOnFrameRateOverridesChanged() = 0;
 
 protected:
     ~ISchedulerCallback() = default;
@@ -93,14 +95,14 @@
     void onScreenAcquired(ConnectionHandle);
     void onScreenReleased(ConnectionHandle);
 
-    void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId,
-                                     std::vector<FrameRateOverride>);
+    void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId)
+            EXCLUDES(mFrameRateOverridesMutex) EXCLUDES(mConnectionsLock);
 
     // Modifies work duration in the event thread.
     void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
                      std::chrono::nanoseconds readyDuration);
 
-    void getDisplayStatInfo(DisplayStatInfo* stats, nsecs_t now);
+    DisplayStatInfo getDisplayStatInfo(nsecs_t now);
 
     // Returns injector handle if injection has toggled, or an invalid handle otherwise.
     ConnectionHandle enableVSyncInjection(bool enable);
@@ -144,7 +146,8 @@
 
     // Returns true if a given vsync timestamp is considered valid vsync
     // for a given uid
-    bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const;
+    bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const
+            EXCLUDES(mFrameRateOverridesMutex);
 
     void dump(std::string&) const;
     void dump(ConnectionHandle, std::string&) const;
@@ -169,6 +172,10 @@
                                                            std::chrono::nanoseconds readyDuration,
                                                            bool traceVsync = true);
 
+    // Stores the preferred refresh rate that an app should run at.
+    // FrameRateOverride.refreshRateHz == 0 means no preference.
+    void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mFrameRateOverridesMutex);
+
 private:
     friend class TestableScheduler;
 
@@ -226,6 +233,12 @@
             REQUIRES(mFeatureStateLock);
 
     void dispatchCachedReportedConfig() REQUIRES(mFeatureStateLock);
+    bool updateFrameRateOverrides(scheduler::RefreshRateConfigs::GlobalSignals consideredSignals,
+                                  Fps displayRefreshRate) REQUIRES(mFeatureStateLock)
+            EXCLUDES(mFrameRateOverridesMutex);
+
+    std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mFrameRateOverridesMutex);
+    impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const;
 
     // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
     struct Connection {
@@ -264,7 +277,7 @@
 
     // In order to make sure that the features don't override themselves, we need a state machine
     // to keep track which feature requested the config change.
-    std::mutex mFeatureStateLock;
+    mutable std::mutex mFeatureStateLock;
 
     struct {
         TimerState idleTimer = TimerState::Reset;
@@ -295,6 +308,17 @@
     static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
 
     const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
+
+    // The frame rate override lists need their own mutex as they are being read
+    // by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks
+    mutable std::mutex mFrameRateOverridesMutex;
+
+    // mappings between a UID and a preferred refresh rate that this app would
+    // run at.
+    scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesByContent
+            GUARDED_BY(mFrameRateOverridesMutex);
+    scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor
+            GUARDED_BY(mFrameRateOverridesMutex);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index a6f9372..7cca206 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 //#define LOG_NDEBUG 0
 #include "VSyncPredictor.h"
@@ -339,3 +343,5 @@
 
 } // namespace android::scheduler
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
index 8431323..cb57aea 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -16,14 +16,19 @@
 
 #include "VsyncConfiguration.h"
 
-#include <cutils/properties.h>
-
+#include <chrono>
+#include <cinttypes>
 #include <optional>
 
+#include <cutils/properties.h>
+#include <log/log.h>
+
 #include "SurfaceFlingerProperties.h"
 
 namespace {
 
+using namespace std::chrono_literals;
+
 std::optional<nsecs_t> getProperty(const char* name) {
     char value[PROPERTY_VALUE_MAX];
     property_get(name, value, "-1");
@@ -31,19 +36,6 @@
     return std::nullopt;
 }
 
-std::vector<android::Fps> getRefreshRatesFromConfigs(
-        const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
-    const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
-    std::vector<android::Fps> refreshRates;
-    refreshRates.reserve(allRefreshRates.size());
-
-    for (const auto& [ignored, refreshRate] : allRefreshRates) {
-        refreshRates.emplace_back(refreshRate->getFps());
-    }
-
-    return refreshRates;
-}
-
 } // namespace
 
 namespace android::scheduler::impl {
@@ -51,25 +43,19 @@
 VsyncConfiguration::VsyncConfiguration(Fps currentFps) : mRefreshRateFps(currentFps) {}
 
 PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(Fps fps) const {
-    const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
-                                   [&fps](const std::pair<Fps, VsyncConfigSet>& candidateFps) {
-                                       return fps.equalsWithMargin(candidateFps.first);
-                                   });
+    std::lock_guard lock(mLock);
+    return getConfigsForRefreshRateLocked(fps);
+}
 
-    if (iter != mOffsets.end()) {
+PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRateLocked(Fps fps) const {
+    const auto iter = mOffsetsCache.find(fps);
+    if (iter != mOffsetsCache.end()) {
         return iter->second;
     }
 
-    // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
-    // In this case just construct the offset.
-    ALOGW("Can't find offset for %s", to_string(fps).c_str());
-    return constructOffsets(fps.getPeriodNsecs());
-}
-
-void VsyncConfiguration::initializeOffsets(const std::vector<Fps>& refreshRates) {
-    for (const auto fps : refreshRates) {
-        mOffsets.emplace(fps, constructOffsets(fps.getPeriodNsecs()));
-    }
+    const auto offset = constructOffsets(fps.getPeriodNsecs());
+    mOffsetsCache[fps] = offset;
+    return offset;
 }
 
 void VsyncConfiguration::dump(std::string& result) const {
@@ -98,10 +84,8 @@
                   earlyGpu.appWorkDuration.count(), earlyGpu.sfWorkDuration.count());
 }
 
-PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
-      : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
-                     refreshRateConfigs.getCurrentRefreshRate().getFps(),
-                     sysprop::vsync_event_phase_offset_ns(1000000),
+PhaseOffsets::PhaseOffsets(Fps currentRefreshRate)
+      : PhaseOffsets(currentRefreshRate, sysprop::vsync_event_phase_offset_ns(1000000),
                      sysprop::vsync_sf_event_phase_offset_ns(1000000),
                      getProperty("debug.sf.early_phase_offset_ns"),
                      getProperty("debug.sf.early_gl_phase_offset_ns"),
@@ -121,15 +105,17 @@
                      getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
                              .value_or(std::numeric_limits<nsecs_t>::max())) {}
 
-PhaseOffsets::PhaseOffsets(
-        const std::vector<Fps>& refreshRates, Fps currentFps, nsecs_t vsyncPhaseOffsetNs,
-        nsecs_t sfVSyncPhaseOffsetNs, std::optional<nsecs_t> earlySfOffsetNs,
-        std::optional<nsecs_t> earlyGpuSfOffsetNs, std::optional<nsecs_t> earlyAppOffsetNs,
-        std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
-        nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
-        std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
-        std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
-        std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, nsecs_t thresholdForNextVsync)
+PhaseOffsets::PhaseOffsets(Fps currentFps, nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+                           std::optional<nsecs_t> earlySfOffsetNs,
+                           std::optional<nsecs_t> earlyGpuSfOffsetNs,
+                           std::optional<nsecs_t> earlyAppOffsetNs,
+                           std::optional<nsecs_t> earlyGpuAppOffsetNs,
+                           nsecs_t highFpsVsyncPhaseOffsetNs, nsecs_t highFpsSfVSyncPhaseOffsetNs,
+                           std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+                           std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+                           std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+                           std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs,
+                           nsecs_t thresholdForNextVsync)
       : VsyncConfiguration(currentFps),
         mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
         mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
@@ -143,9 +129,7 @@
         mHighFpsEarlyGpuSfOffsetNs(highFpsEarlyGpuSfOffsetNs),
         mHighFpsEarlyAppOffsetNs(highFpsEarlyAppOffsetNs),
         mHighFpsEarlyGpuAppOffsetNs(highFpsEarlyGpuAppOffsetNs),
-        mThresholdForNextVsync(thresholdForNextVsync) {
-    initializeOffsets(refreshRates);
-}
+        mThresholdForNextVsync(thresholdForNextVsync) {}
 
 PhaseOffsets::VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const {
     if (vsyncDuration < std::chrono::nanoseconds(15ms).count()) {
@@ -361,10 +345,8 @@
     };
 }
 
-WorkDuration::WorkDuration(const scheduler::RefreshRateConfigs& refreshRateConfigs)
-      : WorkDuration(getRefreshRatesFromConfigs(refreshRateConfigs),
-                     refreshRateConfigs.getCurrentRefreshRate().getFps(),
-                     getProperty("debug.sf.late.sf.duration").value_or(-1),
+WorkDuration::WorkDuration(Fps currentRefreshRate)
+      : WorkDuration(currentRefreshRate, getProperty("debug.sf.late.sf.duration").value_or(-1),
                      getProperty("debug.sf.late.app.duration").value_or(-1),
                      getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
                      getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
@@ -373,17 +355,15 @@
     validateSysprops();
 }
 
-WorkDuration::WorkDuration(const std::vector<Fps>& refreshRates, Fps currentFps, nsecs_t sfDuration,
-                           nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+WorkDuration::WorkDuration(Fps currentRefreshRate, nsecs_t sfDuration, nsecs_t appDuration,
+                           nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
                            nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration)
-      : VsyncConfiguration(currentFps),
+      : VsyncConfiguration(currentRefreshRate),
         mSfDuration(sfDuration),
         mAppDuration(appDuration),
         mSfEarlyDuration(sfEarlyDuration),
         mAppEarlyDuration(appEarlyDuration),
         mSfEarlyGpuDuration(sfEarlyGpuDuration),
-        mAppEarlyGpuDuration(appEarlyGpuDuration) {
-    initializeOffsets(refreshRates);
-}
+        mAppEarlyGpuDuration(appEarlyGpuDuration) {}
 
 } // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
index a120e97..d9d206d 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.h
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -16,12 +16,14 @@
 
 #pragma once
 
+#include <mutex>
+#include <type_traits>
 #include <unordered_map>
+#include <vector>
 
 #include <utils/Timers.h>
 
 #include "Fps.h"
-#include "RefreshRateConfigs.h"
 #include "VsyncModulator.h"
 
 namespace android::scheduler {
@@ -39,9 +41,9 @@
     virtual ~VsyncConfiguration() = default;
     virtual VsyncConfigSet getCurrentConfigs() const = 0;
     virtual VsyncConfigSet getConfigsForRefreshRate(Fps fps) const = 0;
+    virtual void reset() = 0;
 
     virtual void setRefreshRateFps(Fps fps) = 0;
-
     virtual void dump(std::string& result) const = 0;
 };
 
@@ -57,26 +59,39 @@
     explicit VsyncConfiguration(Fps currentFps);
 
     // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
-    VsyncConfigSet getConfigsForRefreshRate(Fps fps) const override;
+    VsyncConfigSet getConfigsForRefreshRate(Fps fps) const override EXCLUDES(mLock);
 
     // Returns early, early GL, and late offsets for Apps and SF.
-    VsyncConfigSet getCurrentConfigs() const override {
-        return getConfigsForRefreshRate(mRefreshRateFps);
+    VsyncConfigSet getCurrentConfigs() const override EXCLUDES(mLock) {
+        std::lock_guard lock(mLock);
+        return getConfigsForRefreshRateLocked(mRefreshRateFps);
+    }
+
+    // Cleans the internal cache.
+    void reset() override EXCLUDES(mLock) {
+        std::lock_guard lock(mLock);
+        mOffsetsCache.clear();
     }
 
     // This function should be called when the device is switching between different
     // refresh rates, to properly update the offsets.
-    void setRefreshRateFps(Fps fps) override { mRefreshRateFps = fps; }
+    void setRefreshRateFps(Fps fps) override EXCLUDES(mLock) {
+        std::lock_guard lock(mLock);
+        mRefreshRateFps = fps;
+    }
 
     // Returns current offsets in human friendly format.
     void dump(std::string& result) const override;
 
 protected:
-    void initializeOffsets(const std::vector<Fps>& refreshRates);
     virtual VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const = 0;
 
-    std::unordered_map<Fps, VsyncConfigSet, std::hash<Fps>, Fps::EqualsInBuckets> mOffsets;
-    std::atomic<Fps> mRefreshRateFps;
+    VsyncConfigSet getConfigsForRefreshRateLocked(Fps fps) const REQUIRES(mLock);
+
+    mutable std::unordered_map<Fps, VsyncConfigSet, std::hash<Fps>, Fps::EqualsInBuckets>
+            mOffsetsCache GUARDED_BY(mLock);
+    std::atomic<Fps> mRefreshRateFps GUARDED_BY(mLock);
+    mutable std::mutex mLock;
 };
 
 /*
@@ -85,13 +100,13 @@
  */
 class PhaseOffsets : public VsyncConfiguration {
 public:
-    explicit PhaseOffsets(const scheduler::RefreshRateConfigs&);
+    explicit PhaseOffsets(Fps currentRefreshRate);
 
 protected:
     // Used for unit tests
-    PhaseOffsets(const std::vector<Fps>& refreshRates, Fps currentFps, nsecs_t vsyncPhaseOffsetNs,
-                 nsecs_t sfVSyncPhaseOffsetNs, std::optional<nsecs_t> earlySfOffsetNs,
-                 std::optional<nsecs_t> earlyGpuSfOffsetNs, std::optional<nsecs_t> earlyAppOffsetNs,
+    PhaseOffsets(Fps currentRefreshRate, nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+                 std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGpuSfOffsetNs,
+                 std::optional<nsecs_t> earlyAppOffsetNs,
                  std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
                  nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
                  std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
@@ -128,13 +143,12 @@
  */
 class WorkDuration : public VsyncConfiguration {
 public:
-    explicit WorkDuration(const scheduler::RefreshRateConfigs&);
+    explicit WorkDuration(Fps currentRefrshRate);
 
 protected:
     // Used for unit tests
-    WorkDuration(const std::vector<Fps>& refreshRates, Fps currentFps, nsecs_t sfDuration,
-                 nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
-                 nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration);
+    WorkDuration(Fps currentFps, nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
+                 nsecs_t appEarlyDuration, nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration);
 
 private:
     VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e9416d6..6967f69 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -17,6 +17,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 //#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -47,8 +48,7 @@
 #include <configstore/Utils.h>
 #include <cutils/compiler.h>
 #include <cutils/properties.h>
-#include <dlfcn.h>
-#include <errno.h>
+#include <ftl/future.h>
 #include <gui/BufferQueue.h>
 #include <gui/DebugEGLImageTracker.h>
 #include <gui/IDisplayEventConnection.h>
@@ -81,6 +81,7 @@
 #include <utils/misc.h>
 
 #include <algorithm>
+#include <cerrno>
 #include <cinttypes>
 #include <cmath>
 #include <cstdint>
@@ -112,7 +113,6 @@
 #include "LayerVector.h"
 #include "MonitoredProducer.h"
 #include "NativeWindowSurface.h"
-#include "Promise.h"
 #include "RefreshRateOverlay.h"
 #include "RegionSamplingThread.h"
 #include "Scheduler/DispSyncSource.h"
@@ -340,7 +340,7 @@
         mInterceptor(mFactory.createSurfaceInterceptor()),
         mTimeStats(std::make_shared<impl::TimeStats>()),
         mFrameTracer(mFactory.createFrameTracer()),
-        mFrameTimeline(mFactory.createFrameTimeline(mTimeStats)),
+        mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, getpid())),
         mEventQueue(mFactory.createMessageQueue()),
         mCompositionEngine(mFactory.createCompositionEngine()),
         mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
@@ -711,16 +711,17 @@
     // Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display.
     mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(
             renderengine::RenderEngineCreationArgs::Builder()
-                .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
-                .setImageCacheSize(maxFrameBufferAcquiredBuffers)
-                .setUseColorManagerment(useColorManagement)
-                .setEnableProtectedContext(enable_protected_contents(false))
-                .setPrecacheToneMapperShaderOnly(false)
-                .setSupportsBackgroundBlur(mSupportsBlur)
-                .setContextPriority(useContextPriority
-                        ? renderengine::RenderEngine::ContextPriority::HIGH
-                        : renderengine::RenderEngine::ContextPriority::MEDIUM)
-                .build()));
+                    .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
+                    .setImageCacheSize(maxFrameBufferAcquiredBuffers)
+                    .setUseColorManagerment(useColorManagement)
+                    .setEnableProtectedContext(enable_protected_contents(false))
+                    .setPrecacheToneMapperShaderOnly(false)
+                    .setSupportsBackgroundBlur(mSupportsBlur)
+                    .setContextPriority(
+                            useContextPriority
+                                    ? renderengine::RenderEngine::ContextPriority::REALTIME
+                                    : renderengine::RenderEngine::ContextPriority::MEDIUM)
+                    .build()));
     mCompositionEngine->setTimeStats(mTimeStats);
     mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
     mCompositionEngine->getHwComposer().setConfiguration(this, getBE().mComposerSequenceId);
@@ -963,7 +964,7 @@
         return BAD_VALUE;
     }
 
-    mScheduler->getDisplayStatInfo(stats, systemTime());
+    *stats = mScheduler->getDisplayStatInfo(systemTime());
     return NO_ERROR;
 }
 
@@ -1431,8 +1432,7 @@
         Mutex::Autolock lock(mStateLock);
 
         if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
-            mEventQueue->setEventConnection(enable ? mScheduler->getEventConnection(handle)
-                                                   : nullptr);
+            mEventQueue->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr);
         }
     }).wait();
 
@@ -1441,7 +1441,8 @@
 
 status_t SurfaceFlinger::injectVSync(nsecs_t when) {
     Mutex::Autolock lock(mStateLock);
-    const auto expectedPresent = calculateExpectedPresentTime(when);
+    const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(when);
+    const auto expectedPresent = calculateExpectedPresentTime(stats);
     return mScheduler->injectVSync(when, /*expectedVSyncTime=*/expectedPresent,
                                    /*deadlineTimestamp=*/expectedPresent)
             ? NO_ERROR
@@ -1512,12 +1513,12 @@
         return BAD_VALUE;
     }
 
-    return promise::chain(schedule([=]() MAIN_THREAD {
+    return ftl::chain(schedule([=]() MAIN_THREAD {
                if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
                    return getHwComposer().setDisplayBrightness(*displayId, brightness);
                } else {
                    ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
-                   return promise::yield<status_t>(NAME_NOT_FOUND);
+                   return ftl::yield<status_t>(NAME_NOT_FOUND);
                }
            }))
             .then([](std::future<status_t> task) { return task; })
@@ -1720,9 +1721,7 @@
     return fence->getSignalTime();
 }
 
-nsecs_t SurfaceFlinger::calculateExpectedPresentTime(nsecs_t now) const {
-    DisplayStatInfo stats;
-    mScheduler->getDisplayStatInfo(&stats, now);
+nsecs_t SurfaceFlinger::calculateExpectedPresentTime(DisplayStatInfo stats) const {
     // Inflate the expected present time if we're targetting the next vsync.
     return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? stats.vsyncTime
                                                           : stats.vsyncTime + stats.vsyncPeriod;
@@ -1770,8 +1769,7 @@
     // Add some slop to correct for drift. This should generally be
     // smaller than a typical frame duration, but should not be so small
     // that it reports reasonable drift as a missed frame.
-    DisplayStatInfo stats;
-    mScheduler->getDisplayStatInfo(&stats, systemTime());
+    const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(systemTime());
     const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
     const nsecs_t previousPresentTime = previousFramePresentTime();
     const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
@@ -1872,7 +1870,7 @@
         const bool tracePreComposition = mTracingEnabled && !mTracePostComposition;
         ConditionalLockGuard<std::mutex> lock(mTracingLock, tracePreComposition);
 
-        mFrameTimeline->setSfWakeUp(vsyncId, frameStart);
+        mFrameTimeline->setSfWakeUp(vsyncId, frameStart, stats.vsyncPeriod);
 
         refreshNeeded = handleMessageTransaction();
         refreshNeeded |= handleMessageInvalidate();
@@ -2148,8 +2146,7 @@
     auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFences[0]);
     getBE().mDisplayTimeline.push(presentFenceTime);
 
-    DisplayStatInfo stats;
-    mScheduler->getDisplayStatInfo(&stats, systemTime());
+    const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(systemTime());
 
     // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
     // be sampled a little later than when we started doing work for this frame,
@@ -2619,6 +2616,17 @@
         if (currentState.physical) {
             const auto display = getDisplayDeviceLocked(displayToken);
             setPowerModeInternal(display, hal::PowerMode::ON);
+
+            // TODO(b/175678251) Call a listener instead.
+            if (currentState.physical->hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
+                const auto displayId = currentState.physical->id;
+                const auto configs = getHwComposer().getConfigs(displayId);
+                mVsyncConfiguration->reset();
+                updatePhaseConfiguration(mRefreshRateConfigs->getCurrentRefreshRate());
+                if (mRefreshRateOverlay) {
+                    mRefreshRateOverlay->reset();
+                }
+            }
         }
         return;
     }
@@ -2880,12 +2888,21 @@
                                        Scheduler::ConfigEvent event) {
     // If this is called from the main thread mStateLock must be locked before
     // Currently the only way to call this function from the main thread is from
-    // Sheduler::chooseRefreshRateForContent
+    // Scheduler::chooseRefreshRateForContent
 
     ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
     changeRefreshRateLocked(refreshRate, event);
 }
 
+void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
+    PhysicalDisplayId displayId = [&]() {
+        ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
+        return getDefaultDisplayDeviceLocked()->getPhysicalId();
+    }();
+
+    mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
+}
+
 void SurfaceFlinger::initScheduler(PhysicalDisplayId primaryDisplayId) {
     if (mScheduler) {
         // In practice it's not allowed to hotplug in/out the primary display once it's been
@@ -2904,7 +2921,7 @@
             std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate.getFps(),
                                                           hal::PowerMode::OFF);
 
-    mVsyncConfiguration = getFactory().createVsyncConfiguration(*mRefreshRateConfigs);
+    mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate.getFps());
     mVsyncModulator.emplace(mVsyncConfiguration->getCurrentConfigs());
 
     // start the EventThread
@@ -3243,8 +3260,9 @@
                 applyTransactionState(transaction.frameTimelineVsyncId, transaction.states,
                                       transaction.displays, transaction.flags,
                                       mPendingInputWindowCommands, transaction.desiredPresentTime,
-                                      transaction.buffer, transaction.postTime,
-                                      transaction.privileged, transaction.hasListenerCallbacks,
+                                      transaction.isAutoTimestamp, transaction.buffer,
+                                      transaction.postTime, transaction.privileged,
+                                      transaction.hasListenerCallbacks,
                                       transaction.listenerCallbacks, transaction.originPid,
                                       transaction.originUid, transaction.id, /*isMainThread*/ true);
                 transactionQueue.pop();
@@ -3268,14 +3286,16 @@
 
 
 bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
-                                                   const Vector<ComposerState>& states) {
+                                                   const Vector<ComposerState>& states,
+                                                   bool updateTransactionCounters) {
 
     const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
+    bool ready = true;
     // Do not present if the desiredPresentTime has not passed unless it is more than one second
     // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
-    if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime &&
+    if (desiredPresentTime > 0 && desiredPresentTime >= expectedPresentTime &&
         desiredPresentTime < expectedPresentTime + s2ns(1)) {
-        return false;
+        ready = false;
     }
 
     for (const ComposerState& state : states) {
@@ -3284,17 +3304,33 @@
             continue;
         }
         if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
-            return false;
+          ready = false;
+        }
+        sp<Layer> layer = nullptr;
+        if (s.surface) {
+            layer = fromHandleLocked(s.surface).promote();
+        } else {
+            ALOGW("Transaction with buffer, but no Layer?");
+            continue;
+        }
+        if (layer && !mScheduler->isVsyncValid(expectedPresentTime, layer->getOwnerUid())) {
+            ATRACE_NAME("!isVsyncValidForUid");
+            ready = false;
+        }
+        if (updateTransactionCounters) {
+              // See BufferStateLayer::mPendingBufferTransactions
+              if (layer) layer->incrementPendingBufferCount();
+
         }
     }
-    return true;
+    return ready;
 }
 
 status_t SurfaceFlinger::setTransactionState(
         int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
         const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
         const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
-        const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+        bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
         const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
     ATRACE_CALL();
 
@@ -3324,24 +3360,35 @@
     const bool pendingTransactions = itr != mTransactionQueues.end();
     // Expected present time is computed and cached on invalidate, so it may be stale.
     if (!pendingTransactions) {
-        mExpectedPresentTime = calculateExpectedPresentTime(systemTime());
+        const auto now = systemTime();
+        const bool nextVsyncPending = now < mExpectedPresentTime.load();
+        const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(now);
+        mExpectedPresentTime = calculateExpectedPresentTime(stats);
+        // The transaction might arrive just before the next vsync but after
+        // invalidate was called. In that case we need to get the next vsync
+        // afterwards.
+        if (nextVsyncPending) {
+            mExpectedPresentTime += stats.vsyncPeriod;
+        }
     }
 
     IPCThreadState* ipc = IPCThreadState::self();
     const int originPid = ipc->getCallingPid();
     const int originUid = ipc->getCallingUid();
 
-    if (pendingTransactions || !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
+    if (pendingTransactions ||
+        !transactionIsReadyToBeApplied(isAutoTimestamp ? 0 : desiredPresentTime, states, true)) {
         mTransactionQueues[applyToken].emplace(frameTimelineVsyncId, states, displays, flags,
-                                               desiredPresentTime, uncacheBuffer, postTime,
-                                               privileged, hasListenerCallbacks, listenerCallbacks,
-                                               originPid, originUid, transactionId);
+                                               desiredPresentTime, isAutoTimestamp, uncacheBuffer,
+                                               postTime, privileged, hasListenerCallbacks,
+                                               listenerCallbacks, originPid, originUid,
+                                               transactionId);
         setTransactionFlags(eTransactionFlushNeeded);
         return NO_ERROR;
     }
 
     applyTransactionState(frameTimelineVsyncId, states, displays, flags, inputWindowCommands,
-                          desiredPresentTime, uncacheBuffer, postTime, privileged,
+                          desiredPresentTime, isAutoTimestamp, uncacheBuffer, postTime, privileged,
                           hasListenerCallbacks, listenerCallbacks, originPid, originUid,
                           transactionId, /*isMainThread*/ false);
     return NO_ERROR;
@@ -3351,9 +3398,10 @@
         int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
         const Vector<DisplayState>& displays, uint32_t flags,
         const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
-        const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged,
-        bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
-        int originPid, int originUid, uint64_t transactionId, bool isMainThread) {
+        bool isAutoTimestamp, const client_cache_t& uncacheBuffer, const int64_t postTime,
+        bool privileged, bool hasListenerCallbacks,
+        const std::vector<ListenerCallbacks>& listenerCallbacks, int originPid, int originUid,
+        uint64_t transactionId, bool isMainThread) {
     uint32_t transactionFlags = 0;
 
     if (flags & eAnimation) {
@@ -3387,12 +3435,13 @@
     std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces;
     uint32_t clientStateFlags = 0;
     for (const ComposerState& state : states) {
-        clientStateFlags |=
-                setClientStateLocked(frameTimelineVsyncId, state, desiredPresentTime, postTime,
-                                     privileged, listenerCallbacksWithSurfaces);
+        clientStateFlags |= setClientStateLocked(frameTimelineVsyncId, state, desiredPresentTime,
+                                                 isAutoTimestamp, postTime, privileged,
+                                                 listenerCallbacksWithSurfaces);
         if ((flags & eAnimation) && state.state.surface) {
             if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
-                mScheduler->recordLayerHistory(layer.get(), desiredPresentTime,
+                mScheduler->recordLayerHistory(layer.get(),
+                                               isAutoTimestamp ? 0 : desiredPresentTime,
                                                LayerHistory::LayerUpdateType::AnimationTX);
             }
         }
@@ -3567,7 +3616,7 @@
 
 uint32_t SurfaceFlinger::setClientStateLocked(
         int64_t frameTimelineVsyncId, const ComposerState& composerState,
-        int64_t desiredPresentTime, int64_t postTime, bool privileged,
+        int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, bool privileged,
         std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
     const layer_state_t& s = composerState.state;
 
@@ -3878,8 +3927,8 @@
                 ? s.frameNumber
                 : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1;
 
-        if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, s.cachedBuffer,
-                             frameNumber)) {
+        if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp,
+                             s.cachedBuffer, frameNumber)) {
             flags |= eTraversalNeeded;
         }
     }
@@ -4157,7 +4206,7 @@
     d.height = 0;
     displays.add(d);
     setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, state, displays, 0, nullptr,
-                        mPendingInputWindowCommands, -1, {}, false, {},
+                        mPendingInputWindowCommands, systemTime(), true, {}, false, {},
                         0 /* Undefined transactionId */);
 
     setPowerModeInternal(display, hal::PowerMode::ON);
@@ -4910,10 +4959,11 @@
         case CAPTURE_LAYERS:
         case CAPTURE_DISPLAY:
         case SET_DISPLAY_BRIGHTNESS:
-        case SET_FRAME_TIMELINE_VSYNC: {
+        case SET_FRAME_TIMELINE_VSYNC:
+        // This is not sensitive information, so should not require permission control.
+        case GET_GPU_CONTEXT_PRIORITY: {
             return OK;
         }
-
         case ADD_REGION_SAMPLING_LISTENER:
         case REMOVE_REGION_SAMPLING_LISTENER: {
             // codes that require permission check
@@ -5262,12 +5312,17 @@
                 return NO_ERROR;
             }
             case 1035: {
-                n = data.readInt32();
+                const int newConfigId = data.readInt32();
                 mDebugDisplayConfigSetByBackdoor = false;
-                const auto numConfigs = mRefreshRateConfigs->getAllRefreshRates().size();
-                if (n >= 0 && n < numConfigs) {
+                const auto displayId = getInternalDisplayId();
+                if (!displayId) {
+                    ALOGE("No internal display found.");
+                    return NO_ERROR;
+                }
+                const auto numConfigs = getHwComposer().getConfigs(*displayId).size();
+                if (newConfigId >= 0 && newConfigId < numConfigs) {
                     const auto displayToken = getInternalDisplayToken();
-                    status_t result = setActiveConfig(displayToken, n);
+                    status_t result = setActiveConfig(displayToken, newConfigId);
                     if (result != NO_ERROR) {
                         return result;
                     }
@@ -5321,13 +5376,10 @@
 
                 auto inUid = static_cast<uid_t>(data.readInt32());
                 const auto refreshRate = data.readFloat();
-                mRefreshRateConfigs->setPreferredRefreshRateForUid(
-                        FrameRateOverride{inUid, refreshRate});
-                const auto mappings = mRefreshRateConfigs->getFrameRateOverrides();
-                mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId,
-                                                        std::move(mappings));
-            }
+                mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{inUid, refreshRate});
+                mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
                 return NO_ERROR;
+            }
         }
     }
     return err;
@@ -5519,7 +5571,7 @@
         }
     }
 
-    RenderAreaFuture renderAreaFuture = promise::defer([=] {
+    RenderAreaFuture renderAreaFuture = ftl::defer([=] {
         return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize, dataspace,
                                          args.useIdentityTransform, args.captureSecureLayers);
     });
@@ -5553,7 +5605,7 @@
                 pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode);
     }
 
-    RenderAreaFuture renderAreaFuture = promise::defer([=] {
+    RenderAreaFuture renderAreaFuture = ftl::defer([=] {
         return DisplayRenderArea::create(displayWeak, Rect(), size, dataspace,
                                          false /* useIdentityTransform */,
                                          false /* captureSecureLayers */);
@@ -5657,7 +5709,7 @@
     }
 
     bool childrenOnly = args.childrenOnly;
-    RenderAreaFuture renderAreaFuture = promise::defer([=]() -> std::unique_ptr<RenderArea> {
+    RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> {
         return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
                                                  childrenOnly, layerStackSpaceRect,
                                                  captureSecureLayers);
@@ -6335,6 +6387,10 @@
     return NO_ERROR;
 }
 
+int SurfaceFlinger::getGPUContextPriority() {
+    return getRenderEngine().getContextPriority();
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
@@ -6346,4 +6402,4 @@
 #endif
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 0509247..9f0f2ea 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -341,7 +341,7 @@
 
     virtual uint32_t setClientStateLocked(
             int64_t frameTimelineVsyncId, const ComposerState& composerState,
-            int64_t desiredPresentTime, int64_t postTime, bool privileged,
+            int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, bool privileged,
             std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
             REQUIRES(mStateLock);
     virtual void commitTransactionLocked();
@@ -434,8 +434,9 @@
     struct TransactionState {
         TransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& composerStates,
                          const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
-                         int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
-                         int64_t postTime, bool privileged, bool hasListenerCallbacks,
+                         int64_t desiredPresentTime, bool isAutoTimestamp,
+                         const client_cache_t& uncacheBuffer, int64_t postTime, bool privileged,
+                         bool hasListenerCallbacks,
                          std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
                          int originUid, uint64_t transactionId)
               : frameTimelineVsyncId(frameTimelineVsyncId),
@@ -443,6 +444,7 @@
                 displays(displayStates),
                 flags(transactionFlags),
                 desiredPresentTime(desiredPresentTime),
+                isAutoTimestamp(isAutoTimestamp),
                 buffer(uncacheBuffer),
                 postTime(postTime),
                 privileged(privileged),
@@ -457,6 +459,7 @@
         Vector<DisplayState> displays;
         uint32_t flags;
         const int64_t desiredPresentTime;
+        const bool isAutoTimestamp;
         client_cache_t buffer;
         const int64_t postTime;
         bool privileged;
@@ -520,8 +523,8 @@
                                  const Vector<DisplayState>& displays, uint32_t flags,
                                  const sp<IBinder>& applyToken,
                                  const InputWindowCommands& inputWindowCommands,
-                                 int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
-                                 bool hasListenerCallbacks,
+                                 int64_t desiredPresentTime, bool isAutoTimestamp,
+                                 const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
                                  const std::vector<ListenerCallbacks>& listenerCallbacks,
                                  uint64_t transactionId) override;
     void bootFinished() override;
@@ -608,6 +611,8 @@
     status_t addTransactionTraceListener(
             const sp<gui::ITransactionTraceListener>& listener) override;
 
+    int getGPUContextPriority() override;
+
     // Implements IBinder::DeathRecipient.
     void binderDied(const wp<IBinder>& who) override;
 
@@ -639,6 +644,8 @@
     void repaintEverythingForHWC() override;
     // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
     void kernelTimerChanged(bool expired) override;
+    // Called when the frame rate override list changed to trigger an event.
+    void triggerOnFrameRateOverridesChanged() override;
     // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
     void toggleKernelIdleTimer();
     // Keeps track of whether the kernel idle timer is currently enabled, so we don't have to
@@ -719,7 +726,7 @@
     void applyTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
                                const Vector<DisplayState>& displays, uint32_t flags,
                                const InputWindowCommands& inputWindowCommands,
-                               const int64_t desiredPresentTime,
+                               const int64_t desiredPresentTime, bool isAutoTimestamp,
                                const client_cache_t& uncacheBuffer, const int64_t postTime,
                                bool privileged, bool hasListenerCallbacks,
                                const std::vector<ListenerCallbacks>& listenerCallbacks,
@@ -743,7 +750,8 @@
     void commitTransaction() REQUIRES(mStateLock);
     void commitOffscreenLayers();
     bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
-                                       const Vector<ComposerState>& states);
+                                       const Vector<ComposerState>& states,
+                                       bool updateTransactionCounters = false) REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
             REQUIRES(mStateLock);
@@ -857,26 +865,12 @@
     /*
      * H/W composer
      */
-
-    // The current hardware composer interface.
-    //
-    // The following thread safety rules apply when accessing mHwc, either
-    // directly or via getHwComposer():
-    //
-    // 1. When recreating mHwc, acquire mStateLock. Recreating mHwc must only be
-    //    done on the main thread.
-    //
-    // 2. When accessing mHwc on the main thread, it's not necessary to acquire
-    //    mStateLock.
-    //
-    // 3. When accessing mHwc on a thread other than the main thread, we always
+    // The following thread safety rules apply when accessing HWComposer:
+    // 1. When reading display state from HWComposer on the main thread, it's not necessary to
+    //    acquire mStateLock.
+    // 2. When accessing HWComposer on a thread other than the main thread, we always
     //    need to acquire mStateLock. This is because the main thread could be
-    //    in the process of destroying the current mHwc instance.
-    //
-    // The above thread safety rules only apply to SurfaceFlinger.cpp. In
-    // SurfaceFlinger_hwc1.cpp we create mHwc at surface flinger init and never
-    // destroy it, so it's always safe to access mHwc from any thread without
-    // acquiring mStateLock.
+    //    in the process of writing display state, e.g. creating or destroying a display.
     HWComposer& getHwComposer() const;
 
     /*
@@ -942,7 +936,7 @@
 
     // Calculates the expected present time for this frame. For negative offsets, performs a
     // correction using the predicted vsync for the next frame instead.
-    nsecs_t calculateExpectedPresentTime(nsecs_t now) const;
+    nsecs_t calculateExpectedPresentTime(DisplayStatInfo) const;
 
     /*
      * Display identification
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index bc487ac..4a75180 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -56,11 +56,11 @@
 }
 
 std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfiguration(
-        const scheduler::RefreshRateConfigs& refreshRateConfigs) {
+        Fps currentRefreshRate) {
     if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
-        return std::make_unique<scheduler::impl::WorkDuration>(refreshRateConfigs);
+        return std::make_unique<scheduler::impl::WorkDuration>(currentRefreshRate);
     } else {
-        return std::make_unique<scheduler::impl::PhaseOffsets>(refreshRateConfigs);
+        return std::make_unique<scheduler::impl::PhaseOffsets>(currentRefreshRate);
     }
 }
 
@@ -136,8 +136,8 @@
 }
 
 std::unique_ptr<frametimeline::FrameTimeline> DefaultFactory::createFrameTimeline(
-        std::shared_ptr<TimeStats> timeStats) {
-    return std::make_unique<frametimeline::impl::FrameTimeline>(timeStats);
+        std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) {
+    return std::make_unique<frametimeline::impl::FrameTimeline>(timeStats, surfaceFlingerPid);
 }
 
 } // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index fc45fa9..24148dd 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -29,7 +29,7 @@
     std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
     std::unique_ptr<MessageQueue> createMessageQueue() override;
     std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
-            const scheduler::RefreshRateConfigs&) override;
+            Fps currentRefreshRate) override;
     std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
                                                ISchedulerCallback&) override;
     sp<SurfaceInterceptor> createSurfaceInterceptor() override;
@@ -56,7 +56,7 @@
     sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override;
     std::unique_ptr<FrameTracer> createFrameTracer() override;
     std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
-            std::shared_ptr<TimeStats> timeStats) override;
+            std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) override;
 };
 
 } // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index deb1b52..885297f 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include "Fps.h"
+
 #include <cutils/compiler.h>
 #include <utils/StrongPointer.h>
 
@@ -76,7 +78,7 @@
     virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
     virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
     virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
-            const scheduler::RefreshRateConfigs&) = 0;
+            Fps currentRefreshRate) = 0;
     virtual std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
                                                        ISchedulerCallback&) = 0;
     virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0;
@@ -108,7 +110,7 @@
     virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
     virtual std::unique_ptr<FrameTracer> createFrameTracer() = 0;
     virtual std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
-            std::shared_ptr<TimeStats> timeStats) = 0;
+            std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) = 0;
 
 protected:
     ~Factory() = default;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 2405884..f4a0319 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -680,19 +680,18 @@
 static void updateJankPayload(T& t, int32_t reasons) {
     t.jankPayload.totalFrames++;
 
-    static const constexpr int32_t kValidJankyReason =
-            JankType::SurfaceFlingerDeadlineMissed |
-            JankType::SurfaceFlingerGpuDeadlineMissed |
-            JankType::AppDeadlineMissed | JankType::Display;
+    static const constexpr int32_t kValidJankyReason = JankType::SurfaceFlingerCpuDeadlineMissed |
+            JankType::SurfaceFlingerGpuDeadlineMissed | JankType::AppDeadlineMissed |
+            JankType::DisplayHAL;
     if (reasons & kValidJankyReason) {
         t.jankPayload.totalJankyFrames++;
-        if ((reasons & JankType::SurfaceFlingerDeadlineMissed) != 0) {
+        if ((reasons & JankType::SurfaceFlingerCpuDeadlineMissed) != 0) {
             t.jankPayload.totalSFLongCpu++;
         }
         if ((reasons & JankType::SurfaceFlingerGpuDeadlineMissed) != 0) {
             t.jankPayload.totalSFLongGpu++;
         }
-        if ((reasons & JankType::Display) != 0) {
+        if ((reasons & JankType::DisplayHAL) != 0) {
             t.jankPayload.totalSFUnattributed++;
         }
         if ((reasons & JankType::AppDeadlineMissed) != 0) {
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index a83b2bf..8fac8e9 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -20,11 +20,12 @@
 #include <cstdint>
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include <android/hardware/graphics/composer/2.4/IComposerClient.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 #include <stats_event.h>
 #include <stats_pull_atom_callback.h>
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
index fae4e94..9481cac 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
@@ -42,3 +42,16 @@
         type: "full",
     },
 }
+
+// ====  java device library for timestats proto  ===========================
+// Note timestats is deprecated and is only used for legacy tests
+java_library {
+    name: "timestats-proto",
+    srcs: [
+        "timestats.proto",
+    ],
+    proto: {
+        type: "lite",
+    },
+    sdk_version: "current",
+}
diff --git a/services/surfaceflinger/tests/DetachChildren_test.cpp b/services/surfaceflinger/tests/DetachChildren_test.cpp
index 9c7b1fc..abf8b1a 100644
--- a/services/surfaceflinger/tests/DetachChildren_test.cpp
+++ b/services/surfaceflinger/tests/DetachChildren_test.cpp
@@ -371,4 +371,7 @@
     }
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 3a8b40f..55b3173 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
 #include <gtest/gtest.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
@@ -143,3 +147,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp
index 152d2d2..58b039e 100644
--- a/services/surfaceflinger/tests/InvalidHandles_test.cpp
+++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp
@@ -70,3 +70,6 @@
 
 } // namespace
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index b87c734..6758518 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -19,6 +19,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include <gtest/gtest.h>
 #include <gui/ISurfaceComposer.h>
@@ -306,4 +307,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index c57ad43..782a364 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -377,6 +377,72 @@
                       40 /* tolerance */);
 }
 
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurAffectedByParentAlpha) {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.surface_flinger.supports_background_blur", value, "0");
+    if (!atoi(value)) {
+        // This device doesn't support blurs, no-op.
+        return;
+    }
+
+    property_get("debug.renderengine.backend", value, "");
+    if (strcmp(value, "skiagl") != 0) {
+        // This device isn't using Skia render engine, no-op.
+        return;
+    }
+
+    sp<SurfaceControl> left;
+    sp<SurfaceControl> right;
+    sp<SurfaceControl> blur;
+    sp<SurfaceControl> blurParent;
+
+    const auto size = 32;
+    ASSERT_NO_FATAL_FAILURE(left = createLayer("Left", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(left, Color::BLUE, size, size));
+    ASSERT_NO_FATAL_FAILURE(right = createLayer("Right", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(right, Color::RED, size, size));
+
+    Transaction()
+            .setLayer(left, mLayerZBase + 1)
+            .setFrame(left, {0, 0, size, size})
+            .setLayer(right, mLayerZBase + 2)
+            .setPosition(right, size, 0)
+            .setFrame(right, {size, 0, size * 2, size})
+            .apply();
+
+    {
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, size, size), Color::BLUE);
+        shot->expectColor(Rect(size, 0, size * 2, size), Color::RED);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(blur = createLayer("BackgroundBlur", size * 2, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(blur, Color::TRANSPARENT, size * 2, size));
+    ASSERT_NO_FATAL_FAILURE(blurParent = createLayer("BackgroundBlurParent", size * 2, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(blurParent, Color::TRANSPARENT, size * 2, size));
+
+    Transaction()
+            .setLayer(blurParent, mLayerZBase + 3)
+            .setAlpha(blurParent, 0.5)
+            .setLayer(blur, mLayerZBase + 4)
+            .setBackgroundBlurRadius(blur, size) // set the blur radius to the size of one rect
+            .setFrame(blur, {0, 0, size * 2, size})
+            .reparent(blur, blurParent)
+            .apply();
+
+    {
+        auto shot = getScreenCapture();
+        // assert that outer sides of the red and blue rects are not blended with the other color;
+        // if the blur didn't take into account parent alpha, the outer sides would have traces of
+        // the other color
+        shot->expectColor(Rect(0, 0, size / 2, size), Color::BLUE);
+        shot->expectColor(Rect(size + size / 2, 0, size * 2, size), Color::RED);
+        // assert that middle line has blended red and blur color; adding a tolerance of 10 to
+        // account for future blur algorithm changes
+        shot->expectColor(Rect(size, 0, size + 1, size), {136, 0, 119, 255}, 10);
+    }
+}
+
 TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
     sp<SurfaceControl> bufferLayer;
     ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 7df3711..214a0cd 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -837,3 +837,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 81e648a..8dc9a12 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -17,6 +17,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
 #include <google/protobuf/io/zero_copy_stream_impl.h>
@@ -1043,4 +1044,4 @@
 }
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
index 5cbf2ef..b38032d 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -907,6 +907,7 @@
 }
 
 void FakeComposerClient::onSurfaceFlingerStop() {
+    mSurfaceComposer->enableVSyncInjections(false);
     mSurfaceComposer->dispose();
     mSurfaceComposer.clear();
 }
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 538b10d..bd49728 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -17,6 +17,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 // #define LOG_NDEBUG 0
 #undef LOG_TAG
@@ -27,6 +28,7 @@
 #include "FakeComposerUtils.h"
 #include "MockComposerHal.h"
 
+#include <binder/Parcel.h>
 #include <gui/DisplayEventReceiver.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerDebugInfo.h>
@@ -870,6 +872,25 @@
 
 using DisplayTest_2_1 = DisplayTest<FakeComposerService_2_1>;
 
+// Tests that VSYNC injection can be safely toggled while invalidating.
+TEST_F(DisplayTest_2_1, VsyncInjection) {
+    const auto flinger = ComposerService::getComposerService();
+    bool enable = true;
+
+    for (int i = 0; i < 100; i++) {
+        flinger->enableVSyncInjections(enable);
+        enable = !enable;
+
+        constexpr uint32_t kForceInvalidate = 1004;
+        android::Parcel data, reply;
+        data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+        EXPECT_EQ(NO_ERROR,
+                  android::IInterface::asBinder(flinger)->transact(kForceInvalidate, data, &reply));
+
+        std::this_thread::sleep_for(5ms);
+    }
+}
+
 TEST_F(DisplayTest_2_1, HotplugOneConfig) {
     Test_HotplugOneConfig();
 }
@@ -1997,4 +2018,4 @@
 }
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index a00e959..13c7c8b 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -51,7 +51,6 @@
         "LayerHistoryTest.cpp",
         "LayerMetadataTest.cpp",
         "MessageQueueTest.cpp",
-        "PromiseTest.cpp",
         "SurfaceFlinger_CreateDisplayTest.cpp",
         "SurfaceFlinger_DestroyDisplayTest.cpp",
         "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
index 1b8c76d..6bc2318 100644
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CachingTest.cpp
@@ -17,6 +17,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #undef LOG_TAG
 #define LOG_TAG "CachingTest"
@@ -97,4 +98,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index be9d336..83e3ba4 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -17,6 +17,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #undef LOG_TAG
 #define LOG_TAG "CompositionTest"
@@ -1533,4 +1534,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp
index be7609a..77a3e14 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
 #include <gtest/gtest.h>
 
 #include <vector>
@@ -80,3 +84,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
index 02ce079..dc04b6d 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
 #include <functional>
 #include <string_view>
 
@@ -409,3 +413,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 01cdb28..9ec2d16 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -19,6 +19,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include <type_traits>
 #include "DisplayIdentificationTest.h"
@@ -364,7 +365,7 @@
     static constexpr DisplayType HWC_DISPLAY_TYPE = hwcDisplayType;
 
     // The HWC active configuration id
-    static constexpr int HWC_ACTIVE_CONFIG_ID = 2001;
+    static constexpr hal::HWConfigId HWC_ACTIVE_CONFIG_ID = 2001;
     static constexpr PowerMode INIT_POWER_MODE = hal::PowerMode::ON;
 
     static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) {
@@ -415,6 +416,45 @@
                                                       ceDisplayArgs);
     }
 
+    static void setupHwcGetConfigsCallExpectations(DisplayTransactionTest* test) {
+        if (HWC_DISPLAY_TYPE == DisplayType::PHYSICAL) {
+            EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _))
+                    .WillRepeatedly(DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{
+                                                  HWC_ACTIVE_CONFIG_ID}),
+                                          Return(Error::NONE)));
+            EXPECT_CALL(*test->mComposer,
+                        getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                            IComposerClient::Attribute::WIDTH, _))
+                    .WillRepeatedly(
+                            DoAll(SetArgPointee<3>(DisplayVariant::WIDTH), Return(Error::NONE)));
+            EXPECT_CALL(*test->mComposer,
+                        getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                            IComposerClient::Attribute::HEIGHT, _))
+                    .WillRepeatedly(
+                            DoAll(SetArgPointee<3>(DisplayVariant::HEIGHT), Return(Error::NONE)));
+            EXPECT_CALL(*test->mComposer,
+                        getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                            IComposerClient::Attribute::VSYNC_PERIOD, _))
+                    .WillRepeatedly(
+                            DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE)));
+            EXPECT_CALL(*test->mComposer,
+                        getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                            IComposerClient::Attribute::DPI_X, _))
+                    .WillRepeatedly(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+            EXPECT_CALL(*test->mComposer,
+                        getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                            IComposerClient::Attribute::DPI_Y, _))
+                    .WillRepeatedly(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+            EXPECT_CALL(*test->mComposer,
+                        getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                            IComposerClient::Attribute::CONFIG_GROUP, _))
+                    .WillRepeatedly(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
+        } else {
+            EXPECT_CALL(*test->mComposer, getDisplayConfigs(_, _)).Times(0);
+            EXPECT_CALL(*test->mComposer, getDisplayAttribute(_, _, _, _)).Times(0);
+        }
+    }
+
     static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
         constexpr auto CONNECTION_TYPE =
                 PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal
@@ -426,33 +466,8 @@
 
         EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_))
                 .WillOnce(Return(hal::Error::NONE));
-        EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{HWC_ACTIVE_CONFIG_ID}),
-                                Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::WIDTH, _))
-                .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::WIDTH), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::HEIGHT, _))
-                .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::HEIGHT), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::VSYNC_PERIOD, _))
-                .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::DPI_X, _))
-                .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::DPI_Y, _))
-                .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
-                                        IComposerClient::Attribute::CONFIG_GROUP, _))
-                .WillOnce(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
+
+        setupHwcGetConfigsCallExpectations(test);
 
         if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
             EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
@@ -559,6 +574,11 @@
                                                       ceDisplayArgs);
     }
 
+    static void setupHwcGetConfigsCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mComposer, getDisplayConfigs(_, _)).Times(0);
+        EXPECT_CALL(*test->mComposer, getDisplayAttribute(_, _, _, _)).Times(0);
+    }
+
     static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
     }
@@ -753,4 +773,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index ee56178..0cd50ce 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
@@ -623,3 +627,6 @@
 
 } // namespace
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
index 36e24d2..e890a62 100644
--- a/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
+++ b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
@@ -37,6 +37,7 @@
                  FAKE_DURATION_OFFSET_NS}};
     }
 
+    void reset() override {}
     void setRefreshRateFps(Fps) override {}
     void dump(std::string&) const override {}
 };
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 43b5afe..169698b 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
 #include "gmock/gmock-spec-builders.h"
 #include "mock/MockTimeStats.h"
 #undef LOG_TAG
@@ -26,6 +30,7 @@
 #include <cinttypes>
 
 using namespace std::chrono_literals;
+using testing::AtLeast;
 using testing::Contains;
 using FrameTimelineEvent = perfetto::protos::FrameTimelineEvent;
 using ProtoDisplayFrame = perfetto::protos::FrameTimelineEvent_DisplayFrame;
@@ -62,7 +67,8 @@
 
     void SetUp() override {
         mTimeStats = std::make_shared<mock::TimeStats>();
-        mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats);
+        mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, mSurfaceFlingerPid,
+                                                               kTestThresholds);
         mFrameTimeline->registerDataSource();
         mTokenManager = &mFrameTimeline->mTokenManager;
         maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
@@ -110,7 +116,8 @@
 
     SurfaceFrame& getSurfaceFrame(size_t displayFrameIdx, size_t surfaceFrameIdx) {
         std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
-        return *(mFrameTimeline->mDisplayFrames[displayFrameIdx]->surfaceFrames[surfaceFrameIdx]);
+        return *(mFrameTimeline->mDisplayFrames[displayFrameIdx]
+                         ->getSurfaceFrames()[surfaceFrameIdx]);
     }
 
     std::shared_ptr<impl::FrameTimeline::DisplayFrame> getDisplayFrame(size_t idx) {
@@ -123,7 +130,7 @@
                 a.presentTime == b.presentTime;
     }
 
-    const std::unordered_map<int64_t, TimelineItem>& getPredictions() {
+    const std::map<int64_t, TokenManagerPrediction>& getPredictions() {
         return mTokenManager->mPredictions;
     }
 
@@ -138,6 +145,16 @@
     FenceToFenceTimeMap fenceFactory;
     uint32_t* maxDisplayFrames;
     nsecs_t maxTokenRetentionTime;
+    pid_t mSurfaceFlingerPid = 666;
+    static constexpr nsecs_t kPresentThreshold =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count();
+    static constexpr nsecs_t kDeadlineThreshold =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count();
+    static constexpr nsecs_t kStartThreshold =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count();
+    static constexpr JankClassificationThresholds kTestThresholds{kPresentThreshold,
+                                                                  kDeadlineThreshold,
+                                                                  kStartThreshold};
 };
 
 static const std::string sLayerNameOne = "layer1";
@@ -162,55 +179,58 @@
 }
 
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
-    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                    sLayerNameOne, std::nullopt);
-    auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken(sPidTwo, sUidOne, sLayerNameOne,
-                                                                    sLayerNameOne, std::nullopt);
+    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+                                                                    sLayerNameOne, sLayerNameOne);
+    auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidTwo, sUidOne,
+                                                                    sLayerNameOne, sLayerNameOne);
     EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
     EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
 }
 
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
-    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                   sLayerNameOne, std::nullopt);
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+                                                                   sLayerNameOne, sLayerNameOne);
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
 }
 
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
     flushTokens(systemTime() + maxTokenRetentionTime);
-    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                   sLayerNameOne, token1);
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+                                                                   sLayerNameOne, sLayerNameOne);
 
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
 }
 
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
-    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                   sLayerNameOne, token1);
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+                                                                   sLayerNameOne, sLayerNameOne);
 
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
     EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
 }
 
 TEST_F(FrameTimelineTest, presentFenceSignaled_droppedFramesNotUpdated) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
-    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                    sLayerNameOne, token1);
+    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+                                                                    sLayerNameOne, sLayerNameOne);
 
     // Set up the display frame
-    mFrameTimeline->setSfWakeUp(token1, 20);
-    mFrameTimeline->addSurfaceFrame(surfaceFrame1, SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->setSfWakeUp(token1, 20, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     mFrameTimeline->setSfPresent(25, presentFence1);
     presentFence1->signalForTest(30);
 
     // Trigger a flush by calling setSfPresent for the next frame
-    mFrameTimeline->setSfWakeUp(token2, 50);
+    mFrameTimeline->setSfWakeUp(token2, 50, 11);
     mFrameTimeline->setSfPresent(55, presentFence2);
 
     auto& droppedSurfaceFrame = getSurfaceFrame(0, 0);
@@ -219,48 +239,50 @@
 }
 
 TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
     int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 60});
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                       sLayerNameOne, surfaceFrameToken1);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
     auto surfaceFrame2 =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameTwo,
-                                                       sLayerNameTwo, surfaceFrameToken2);
-    mFrameTimeline->setSfWakeUp(sfToken1, 22);
-    mFrameTimeline->addSurfaceFrame(surfaceFrame1,
-                                    SurfaceFrame::PresentState::Presented);
-    mFrameTimeline->addSurfaceFrame(surfaceFrame2,
-                                    SurfaceFrame::PresentState::Presented);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameTwo, sLayerNameTwo);
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame2);
     mFrameTimeline->setSfPresent(26, presentFence1);
     auto displayFrame = getDisplayFrame(0);
-    SurfaceFrame& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
-    SurfaceFrame& presentedSurfaceFrame2 = getSurfaceFrame(0, 1);
+    auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+    auto& presentedSurfaceFrame2 = getSurfaceFrame(0, 1);
     presentFence1->signalForTest(42);
 
     // Fences haven't been flushed yet, so it should be 0
-    EXPECT_EQ(displayFrame->surfaceFlingerActuals.presentTime, 0);
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
     EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 0);
     EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 0);
 
-    EXPECT_EQ(surfaceFrame1->getToken(), surfaceFrameToken1);
-    EXPECT_EQ(surfaceFrame2->getToken(), surfaceFrameToken2);
-
     // Trigger a flush by finalizing the next DisplayFrame
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto surfaceFrame3 =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                       sLayerNameOne, surfaceFrameToken2);
-    mFrameTimeline->setSfWakeUp(sfToken2, 52);
-    mFrameTimeline->addSurfaceFrame(surfaceFrame3, SurfaceFrame::PresentState::Dropped);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+    surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame3);
     mFrameTimeline->setSfPresent(56, presentFence2);
     displayFrame = getDisplayFrame(0);
 
     // Fences have flushed, so the present timestamps should be updated
-    EXPECT_EQ(displayFrame->surfaceFlingerActuals.presentTime, 42);
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 42);
     EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 42);
     EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 42);
     EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
@@ -270,6 +292,12 @@
 TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) {
     // Insert kMaxDisplayFrames' count of DisplayFrames to fill the deque
     int frameTimeFactor = 0;
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+            .Times(static_cast<int32_t>(*maxDisplayFrames));
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_))
+            .Times(static_cast<int32_t>(*maxDisplayFrames));
     for (size_t i = 0; i < *maxDisplayFrames; i++) {
         auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
         int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions(
@@ -277,11 +305,11 @@
         int64_t sfToken = mTokenManager->generateTokenForPredictions(
                 {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
         auto surfaceFrame =
-                mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                           sLayerNameOne, surfaceFrameToken);
-        mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
-        mFrameTimeline->addSurfaceFrame(surfaceFrame,
-                                        SurfaceFrame::PresentState::Presented);
+                mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+                                                           sLayerNameOne, sLayerNameOne);
+        mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, 11);
+        surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->addSurfaceFrame(surfaceFrame);
         mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
         presentFence->signalForTest(32 + frameTimeFactor);
         frameTimeFactor += 30;
@@ -289,8 +317,7 @@
     auto displayFrame0 = getDisplayFrame(0);
 
     // The 0th Display Frame should have actuals 22, 27, 32
-    EXPECT_EQ(compareTimelineItems(displayFrame0->surfaceFlingerActuals, TimelineItem(22, 27, 32)),
-              true);
+    EXPECT_EQ(compareTimelineItems(displayFrame0->getActuals(), TimelineItem(22, 27, 32)), true);
 
     // Add one more display frame
     auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -299,51 +326,54 @@
     int64_t sfToken = mTokenManager->generateTokenForPredictions(
             {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
     auto surfaceFrame =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                       sLayerNameOne, surfaceFrameToken);
-    mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
-    mFrameTimeline->addSurfaceFrame(surfaceFrame, SurfaceFrame::PresentState::Presented);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, 11);
+    surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame);
     mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
     presentFence->signalForTest(32 + frameTimeFactor);
     displayFrame0 = getDisplayFrame(0);
 
     // The window should have slided by 1 now and the previous 0th display frame
     // should have been removed from the deque
-    EXPECT_EQ(compareTimelineItems(displayFrame0->surfaceFlingerActuals, TimelineItem(52, 57, 62)),
-              true);
+    EXPECT_EQ(compareTimelineItems(displayFrame0->getActuals(), TimelineItem(52, 57, 62)), true);
 }
 
 TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
-    auto surfaceFrame =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue",
-                                                       "acquireFenceAfterQueue", std::nullopt);
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, 0,
+                                                                   "acquireFenceAfterQueue",
+                                                                   "acquireFenceAfterQueue");
     surfaceFrame->setActualQueueTime(123);
     surfaceFrame->setAcquireFenceTime(456);
     EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
 }
 
 TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
-    auto surfaceFrame =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue",
-                                                       "acquireFenceAfterQueue", std::nullopt);
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, 0,
+                                                                   "acquireFenceAfterQueue",
+                                                                   "acquireFenceAfterQueue");
     surfaceFrame->setActualQueueTime(456);
     surfaceFrame->setAcquireFenceTime(123);
     EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
 }
 
 TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+            .Times(static_cast<int32_t>(*maxDisplayFrames + 10));
     auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     presentFence->signalForTest(2);
 
     // Size shouldn't exceed maxDisplayFrames - 64
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
         auto surfaceFrame =
-                mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                           sLayerNameOne, std::nullopt);
+                mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+                                                           sLayerNameOne, sLayerNameOne);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
-        mFrameTimeline->setSfWakeUp(sfToken, 22);
-        mFrameTimeline->addSurfaceFrame(surfaceFrame,
-                                        SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->setSfWakeUp(sfToken, 22, 11);
+        surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->addSurfaceFrame(surfaceFrame);
         mFrameTimeline->setSfPresent(27, presentFence);
     }
     EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
@@ -351,15 +381,18 @@
     // Increase the size to 256
     mFrameTimeline->setMaxDisplayFrames(256);
     EXPECT_EQ(*maxDisplayFrames, 256);
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+            .Times(static_cast<int32_t>(*maxDisplayFrames + 10));
 
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
         auto surfaceFrame =
-                mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                           sLayerNameOne, std::nullopt);
+                mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+                                                           sLayerNameOne, sLayerNameOne);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
-        mFrameTimeline->setSfWakeUp(sfToken, 22);
-        mFrameTimeline->addSurfaceFrame(surfaceFrame,
-                                        SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->setSfWakeUp(sfToken, 22, 11);
+        surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->addSurfaceFrame(surfaceFrame);
         mFrameTimeline->setSfPresent(27, presentFence);
     }
     EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
@@ -367,26 +400,30 @@
     // Shrink the size to 128
     mFrameTimeline->setMaxDisplayFrames(128);
     EXPECT_EQ(*maxDisplayFrames, 128);
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+            .Times(static_cast<int32_t>(*maxDisplayFrames + 10));
 
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
         auto surfaceFrame =
-                mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                           sLayerNameOne, std::nullopt);
+                mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+                                                           sLayerNameOne, sLayerNameOne);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
-        mFrameTimeline->setSfWakeUp(sfToken, 22);
-        mFrameTimeline->addSurfaceFrame(surfaceFrame,
-                                        SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->setSfWakeUp(sfToken, 22, 11);
+        surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+        mFrameTimeline->addSurfaceFrame(surfaceFrame);
         mFrameTimeline->setSfPresent(27, presentFence);
     }
     EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
 }
 
+// Tests related to TimeStats
 TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
     EXPECT_CALL(*mTimeStats,
                 incrementJankyFrames(sUidOne, sLayerNameOne,
-                                     HasBit(JankType::SurfaceFlingerDeadlineMissed)));
+                                     HasBit(JankType::SurfaceFlingerCpuDeadlineMissed)));
     EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(HasBit(JankType::SurfaceFlingerDeadlineMissed)));
+                incrementJankyFrames(HasBit(JankType::SurfaceFlingerCpuDeadlineMissed)));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
             {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
@@ -397,12 +434,13 @@
              std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
              std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                       sLayerNameOne, surfaceFrameToken1);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
     mFrameTimeline->setSfWakeUp(sfToken1,
-                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
-    mFrameTimeline->addSurfaceFrame(surfaceFrame1,
-                                    SurfaceFrame::PresentState::Presented);
+                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+                                11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     presentFence1->signalForTest(
             std::chrono::duration_cast<std::chrono::nanoseconds>(70ms).count());
 
@@ -412,8 +450,9 @@
 
 TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
     EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(sUidOne, sLayerNameOne, HasBit(JankType::Display)));
-    EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(JankType::Display)));
+                incrementJankyFrames(sUidOne, sLayerNameOne, HasBit(JankType::DisplayHAL)));
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(JankType::DisplayHAL)));
+
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
             {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
@@ -424,18 +463,18 @@
              std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
              std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                       sLayerNameOne, surfaceFrameToken1);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
     mFrameTimeline->setSfWakeUp(sfToken1,
-                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
-    mFrameTimeline->addSurfaceFrame(surfaceFrame1,
-                                    SurfaceFrame::PresentState::Presented);
+                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+                                30);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     presentFence1->signalForTest(
             std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
-    mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(),
+    mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
                                  presentFence1);
-    EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
-    EXPECT_TRUE((surfaceFrame1->getJankType().value() & JankType::Display) != 0);
+    EXPECT_EQ(surfaceFrame1->getJankType(), JankType::DisplayHAL);
 }
 
 TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
@@ -449,26 +488,26 @@
              std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
              std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
-            {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
-             std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
-             std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(82ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(86ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count()});
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                       sLayerNameOne, surfaceFrameToken1);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
     surfaceFrame1->setAcquireFenceTime(
             std::chrono::duration_cast<std::chrono::nanoseconds>(45ms).count());
     mFrameTimeline->setSfWakeUp(sfToken1,
-                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+                                11);
 
-    mFrameTimeline->addSurfaceFrame(surfaceFrame1,
-                                    SurfaceFrame::PresentState::Presented);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     presentFence1->signalForTest(
             std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
-    mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+    mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(86ms).count(),
                                  presentFence1);
 
-    EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
-    EXPECT_TRUE((surfaceFrame1->getJankType().value() & JankType::AppDeadlineMissed) != 0);
+    EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
 }
 
 /*
@@ -481,23 +520,26 @@
  */
 TEST_F(FrameTimelineTest, tracing_noPacketsSentWithoutTraceStart) {
     auto tracingSession = getTracingSessionForTest();
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
-    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                    sLayerNameOne, token1);
+    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+                                                                    sLayerNameOne, sLayerNameOne);
 
     // Set up the display frame
-    mFrameTimeline->setSfWakeUp(token1, 20);
-    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->setSfWakeUp(token1, 20, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     mFrameTimeline->setSfPresent(25, presentFence1);
     presentFence1->signalForTest(30);
 
     // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
     // next frame
-    mFrameTimeline->setSfWakeUp(token2, 50);
+    mFrameTimeline->setSfWakeUp(token2, 50, 11);
     mFrameTimeline->setSfPresent(55, presentFence2);
 
     auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
@@ -506,25 +548,29 @@
 
 TEST_F(FrameTimelineTest, tracing_sanityTest) {
     auto tracingSession = getTracingSessionForTest();
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
     tracingSession->StartBlocking();
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
-    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                    sLayerNameOne, token1);
+    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+                                                                    sLayerNameOne, sLayerNameOne);
 
     // Set up the display frame
-    mFrameTimeline->setSfWakeUp(token2, 20);
-    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
-                                    SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->setSfWakeUp(token2, 20, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     mFrameTimeline->setSfPresent(25, presentFence1);
     presentFence1->signalForTest(30);
 
     // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
     // next frame
-    mFrameTimeline->setSfWakeUp(token2, 50);
+    mFrameTimeline->setSfWakeUp(token2, 50, 11);
     mFrameTimeline->setSfPresent(55, presentFence2);
     presentFence2->signalForTest(55);
 
@@ -543,6 +589,8 @@
 
 TEST_F(FrameTimelineTest, traceDisplayFrame_invalidTokenDoesNotEmitTracePacket) {
     auto tracingSession = getTracingSessionForTest();
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
@@ -550,13 +598,13 @@
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
 
     // Set up the display frame
-    mFrameTimeline->setSfWakeUp(-1, 20);
+    mFrameTimeline->setSfWakeUp(-1, 20, 11);
     mFrameTimeline->setSfPresent(25, presentFence1);
     presentFence1->signalForTest(30);
 
     // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
     // next frame
-    mFrameTimeline->setSfWakeUp(token1, 50);
+    mFrameTimeline->setSfWakeUp(token1, 50, 11);
     mFrameTimeline->setSfPresent(55, presentFence2);
     presentFence2->signalForTest(60);
 
@@ -572,24 +620,27 @@
 
 TEST_F(FrameTimelineTest, traceSurfaceFrame_invalidTokenDoesNotEmitTracePacket) {
     auto tracingSession = getTracingSessionForTest();
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
     tracingSession->StartBlocking();
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
-    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                                    sLayerNameOne, std::nullopt);
+    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+                                                                    sLayerNameOne, sLayerNameOne);
 
     // Set up the display frame
-    mFrameTimeline->setSfWakeUp(token1, 20);
-    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->setSfWakeUp(token1, 20, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     mFrameTimeline->setSfPresent(25, presentFence1);
     presentFence1->signalForTest(30);
 
     // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
     // next frame
-    mFrameTimeline->setSfWakeUp(token2, 50);
+    mFrameTimeline->setSfWakeUp(token2, 50, 11);
     mFrameTimeline->setSfPresent(55, presentFence2);
     presentFence2->signalForTest(60);
 
@@ -662,6 +713,8 @@
 
 TEST_F(FrameTimelineTest, traceDisplayFrame_emitsValidTracePacket) {
     auto tracingSession = getTracingSessionForTest();
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
@@ -670,7 +723,7 @@
     int64_t displayFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
 
     // Set up the display frame
-    mFrameTimeline->setSfWakeUp(displayFrameToken1, 20);
+    mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, 11);
     mFrameTimeline->setSfPresent(26, presentFence1);
     presentFence1->signalForTest(31);
 
@@ -687,7 +740,7 @@
 
     // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
     // next frame
-    mFrameTimeline->setSfWakeUp(displayFrameToken2, 50);
+    mFrameTimeline->setSfWakeUp(displayFrameToken2, 50, 11);
     mFrameTimeline->setSfPresent(55, presentFence2);
     presentFence2->signalForTest(55);
 
@@ -713,6 +766,10 @@
 
 TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) {
     auto tracingSession = getTracingSessionForTest();
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
@@ -722,8 +779,8 @@
     int64_t displayFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
 
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
-                                                       sLayerNameOne, surfaceFrameToken);
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
     surfaceFrame1->setActualStartTime(0);
     surfaceFrame1->setActualQueueTime(15);
     surfaceFrame1->setAcquireFenceTime(20);
@@ -743,15 +800,15 @@
     protoSurfaceFrame.set_pid(sPidOne);
 
     // Set up the display frame
-    mFrameTimeline->setSfWakeUp(displayFrameToken1, 20);
-    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
-                                    SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     mFrameTimeline->setSfPresent(26, presentFence1);
-    presentFence1->signalForTest(31);
+    presentFence1->signalForTest(40);
 
     // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
     // next frame
-    mFrameTimeline->setSfWakeUp(displayFrameToken2, 50);
+    mFrameTimeline->setSfWakeUp(displayFrameToken2, 50, 11);
     mFrameTimeline->setSfPresent(55, presentFence2);
     presentFence2->signalForTest(55);
 
@@ -775,4 +832,511 @@
     validateSurfaceFrameEvent(surfaceFrameEvent, protoSurfaceFrame);
 }
 
+// Tests for Jank classification
+TEST_F(FrameTimelineTest, jankClassification_presentOnTimeDoesNotClassify) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions({10, 20, 30});
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
+    auto surfaceFrame =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+    surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame);
+    mFrameTimeline->setSfPresent(26, presentFence1);
+    auto displayFrame = getDisplayFrame(0);
+    auto& presentedSurfaceFrame = getSurfaceFrame(0, 0);
+    presentFence1->signalForTest(29);
+
+    // Fences haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+    EXPECT_EQ(presentedSurfaceFrame.getActuals().presentTime, 0);
+
+    addEmptyDisplayFrame();
+    displayFrame = getDisplayFrame(0);
+
+    // Fences have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 29);
+    EXPECT_EQ(presentedSurfaceFrame.getActuals().presentTime, 29);
+    EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+    EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame->getJankType(), JankType::None);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresent) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+    mFrameTimeline->setSfPresent(26, presentFence1);
+    auto displayFrame = getDisplayFrame(0);
+    presentFence1->signalForTest(30);
+
+    // Fences for the first frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+    // Trigger a flush by finalizing the next DisplayFrame
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+    mFrameTimeline->setSfPresent(56, presentFence2);
+    displayFrame = getDisplayFrame(0);
+
+    // Fences for the first frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 30);
+    EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+
+    // Fences for the second frame haven't been flushed yet, so it should be 0
+    auto displayFrame2 = getDisplayFrame(1);
+    presentFence2->signalForTest(65);
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+
+    addEmptyDisplayFrame();
+    displayFrame2 = getDisplayFrame(1);
+
+    // Fences for the second frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 65);
+    EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+    mFrameTimeline->setSfPresent(26, presentFence1);
+    auto displayFrame = getDisplayFrame(0);
+    presentFence1->signalForTest(50);
+
+    // Fences for the first frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+    // Trigger a flush by finalizing the next DisplayFrame
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+    mFrameTimeline->setSfPresent(56, presentFence2);
+    displayFrame = getDisplayFrame(0);
+
+    // Fences for the first frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 50);
+    EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame->getJankType(), JankType::DisplayHAL);
+
+    // Fences for the second frame haven't been flushed yet, so it should be 0
+    auto displayFrame2 = getDisplayFrame(1);
+    presentFence2->signalForTest(75);
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+
+    addEmptyDisplayFrame();
+    displayFrame2 = getDisplayFrame(1);
+
+    // Fences for the second frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 75);
+    EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishEarlyPresent) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({12, 18, 40});
+    mFrameTimeline->setSfWakeUp(sfToken1, 12, 11);
+
+    mFrameTimeline->setSfPresent(22, presentFence1);
+    auto displayFrame = getDisplayFrame(0);
+    presentFence1->signalForTest(28);
+
+    // Fences haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+    addEmptyDisplayFrame();
+    displayFrame = getDisplayFrame(0);
+
+    // Fences have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 28);
+    EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+    mFrameTimeline->setSfWakeUp(sfToken1, 12, 11);
+    mFrameTimeline->setSfPresent(36, presentFence1);
+    auto displayFrame = getDisplayFrame(0);
+    presentFence1->signalForTest(52);
+
+    // Fences haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+    addEmptyDisplayFrame();
+    displayFrame = getDisplayFrame(0);
+
+    // Fences have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 52);
+    EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
+    int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame1->setAcquireFenceTime(16);
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    mFrameTimeline->setSfPresent(26, presentFence1);
+    auto displayFrame1 = getDisplayFrame(0);
+    auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+    presentFence1->signalForTest(30);
+
+    // Fences for the first frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+    auto actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 0);
+
+    // Trigger a flush by finalizing the next DisplayFrame
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto surfaceFrame2 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame2->setAcquireFenceTime(36);
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+    surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+    mFrameTimeline->setSfPresent(56, presentFence2);
+    auto displayFrame2 = getDisplayFrame(1);
+    auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+
+    // Fences for the first frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 30);
+    EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerScheduling);
+
+    actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 30);
+    EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::SurfaceFlingerScheduling);
+
+    // Fences for the second frame haven't been flushed yet, so it should be 0
+    presentFence2->signalForTest(65);
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+    auto actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 0);
+
+    addEmptyDisplayFrame();
+
+    // Fences for the second frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 65);
+    EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+
+    actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 65);
+    EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
+    int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame1->setAcquireFenceTime(16);
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    mFrameTimeline->setSfPresent(26, presentFence1);
+    auto displayFrame1 = getDisplayFrame(0);
+    auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+    presentFence1->signalForTest(50);
+
+    // Fences for the first frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+    auto actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 0);
+
+    // Trigger a flush by finalizing the next DisplayFrame
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto surfaceFrame2 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame2->setAcquireFenceTime(36);
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+    surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+    mFrameTimeline->setSfPresent(56, presentFence2);
+    auto displayFrame2 = getDisplayFrame(1);
+    auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+
+    // Fences for the first frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 50);
+    EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame1->getJankType(), JankType::DisplayHAL);
+
+    actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 50);
+    EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::DisplayHAL);
+
+    // Fences for the second frame haven't been flushed yet, so it should be 0
+    presentFence2->signalForTest(86);
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+    auto actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 0);
+
+    addEmptyDisplayFrame();
+
+    // Fences for the second frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 86);
+    EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+
+    actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 86);
+    EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishEarlyPresent) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({42, 46, 50});
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 26, 60});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame1->setAcquireFenceTime(40);
+    mFrameTimeline->setSfWakeUp(sfToken1, 42, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    mFrameTimeline->setSfPresent(46, presentFence1);
+    auto displayFrame1 = getDisplayFrame(0);
+    auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+    presentFence1->signalForTest(50);
+
+    // Fences for the first frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+    auto actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 0);
+
+    addEmptyDisplayFrame();
+
+    // Fences for the first frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 50);
+    EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+    EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+    actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 50);
+    EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+    EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::Unknown);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) {
+    // First frame - DisplayFrame is not janky. This should classify the SurfaceFrame as
+    // AppDeadlineMissed. Second frame - DisplayFrame is janky. This should propagate DisplayFrame's
+    // jank to the SurfaceFrame.
+
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({32, 36, 40});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({42, 46, 50});
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 30});
+    int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 50});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame1->setAcquireFenceTime(26);
+    mFrameTimeline->setSfWakeUp(sfToken1, 32, 11);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    mFrameTimeline->setSfPresent(36, presentFence1);
+    auto displayFrame1 = getDisplayFrame(0);
+    auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+    presentFence1->signalForTest(40);
+
+    // Fences for the first frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+    auto actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 0);
+
+    // Trigger a flush by finalizing the next DisplayFrame
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto surfaceFrame2 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame2->setAcquireFenceTime(40);
+    mFrameTimeline->setSfWakeUp(sfToken2, 43, 11);
+    surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+    mFrameTimeline->setSfPresent(56, presentFence2);
+    auto displayFrame2 = getDisplayFrame(1);
+    auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+
+    // Fences for the first frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 40);
+    EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+    EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+    actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 40);
+    EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed);
+
+    // Fences for the second frame haven't been flushed yet, so it should be 0
+    presentFence2->signalForTest(60);
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+    auto actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 0);
+
+    addEmptyDisplayFrame();
+
+    // Fences for the second frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 60);
+    EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+
+    actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 60);
+    EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadlineMissed) {
+    // Global increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+    // Layer specific increment
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+    int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
+
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 56, 60});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({112, 116, 120});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame1->setAcquireFenceTime(50);
+    mFrameTimeline->setSfWakeUp(sfToken1, 52, 30);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    mFrameTimeline->setSfPresent(56, presentFence1);
+    auto displayFrame1 = getDisplayFrame(0);
+    auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+    presentFence1->signalForTest(60);
+
+    // Fences for the first frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+    auto actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.presentTime, 0);
+
+    // Trigger a flush by finalizing the next DisplayFrame
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto surfaceFrame2 =
+            mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    surfaceFrame2->setAcquireFenceTime(84);
+    mFrameTimeline->setSfWakeUp(sfToken2, 112, 30);
+    surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+    mFrameTimeline->setSfPresent(116, presentFence2);
+    auto displayFrame2 = getDisplayFrame(1);
+    auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+    presentFence2->signalForTest(120);
+
+    // Fences for the first frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame1->getActuals().presentTime, 60);
+    actuals1 = presentedSurfaceFrame1.getActuals();
+    EXPECT_EQ(actuals1.endTime, 50);
+    EXPECT_EQ(actuals1.presentTime, 60);
+
+    EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+    EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+    EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed);
+
+    // Fences for the second frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+    auto actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 0);
+
+    addEmptyDisplayFrame();
+
+    // Fences for the second frame have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 120);
+    actuals2 = presentedSurfaceFrame2.getActuals();
+    EXPECT_EQ(actuals2.presentTime, 120);
+
+    EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+    EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+    EXPECT_EQ(displayFrame2->getJankType(), JankType::None);
+
+    EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
+              JankType::AppDeadlineMissed | JankType::BufferStuffing);
+}
 } // namespace android::frametimeline
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
index a119e27..2c71a2e 100644
--- a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
@@ -17,6 +17,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
@@ -381,4 +382,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index fa12315..bc1e88a 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -42,7 +42,10 @@
 namespace android {
 namespace {
 
-namespace hal = android::hardware::graphics::composer::hal;
+namespace V2_1 = hardware::graphics::composer::V2_1;
+namespace V2_4 = hardware::graphics::composer::V2_4;
+
+using Hwc2::Config;
 
 using ::testing::_;
 using ::testing::DoAll;
@@ -170,5 +173,88 @@
     EXPECT_EQ(hal::Error::UNSUPPORTED, result);
 }
 
+class HWComposerConfigsTest : public testing::Test {
+public:
+    Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>();
+    MockHWC2ComposerCallback mCallback;
+
+    void setActiveConfig(Config config) {
+        EXPECT_CALL(*mHal, getActiveConfig(_, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(config), Return(V2_1::Error::NONE)));
+    }
+
+    void setDisplayConfigs(std::vector<Config> configs) {
+        EXPECT_CALL(*mHal, getDisplayConfigs(_, _))
+                .WillOnce(DoAll(SetArgPointee<1>(configs), Return(V2_1::Error::NONE)));
+        EXPECT_CALL(*mHal, getDisplayAttribute(_, _, _, _))
+                .WillRepeatedly(DoAll(SetArgPointee<3>(1), Return(V2_1::Error::NONE)));
+    }
+
+    void testSetActiveConfigWithConstraintsCommon(bool isVsyncPeriodSwitchSupported);
+};
+
+void HWComposerConfigsTest::testSetActiveConfigWithConstraintsCommon(
+        bool isVsyncPeriodSwitchSupported) {
+    EXPECT_CALL(*mHal, getMaxVirtualDisplayCount()).WillOnce(Return(0));
+    EXPECT_CALL(*mHal, getCapabilities()).WillOnce(Return(std::vector<hal::Capability>{}));
+    EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_)).WillOnce(Return(V2_4::Error::UNSUPPORTED));
+    EXPECT_CALL(*mHal, registerCallback(_));
+    EXPECT_CALL(*mHal, setVsyncEnabled(_, _)).WillRepeatedly(Return(V2_1::Error::NONE));
+    EXPECT_CALL(*mHal, getDisplayIdentificationData(_, _, _))
+            .WillRepeatedly(Return(V2_1::Error::UNSUPPORTED));
+    EXPECT_CALL(*mHal, setClientTargetSlotCount(_)).WillRepeatedly(Return(V2_1::Error::NONE));
+
+    EXPECT_CALL(*mHal, isVsyncPeriodSwitchSupported())
+            .WillRepeatedly(Return(isVsyncPeriodSwitchSupported));
+
+    if (isVsyncPeriodSwitchSupported) {
+        EXPECT_CALL(*mHal, setActiveConfigWithConstraints(_, _, _, _))
+                .WillRepeatedly(Return(V2_4::Error::NONE));
+    } else {
+        EXPECT_CALL(*mHal, setActiveConfig(_, _)).WillRepeatedly(Return(V2_1::Error::NONE));
+    }
+
+    impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
+    hwc.setConfiguration(&mCallback, 123);
+
+    setDisplayConfigs({15});
+    setActiveConfig(15);
+
+    const auto physicalId = PhysicalDisplayId::fromPort(0);
+    const hal::HWDisplayId hwcId = 0;
+    hwc.allocatePhysicalDisplay(hwcId, physicalId);
+
+    hal::VsyncPeriodChangeConstraints constraints;
+    constraints.desiredTimeNanos = systemTime();
+    constraints.seamlessRequired = false;
+
+    hal::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
+    constexpr size_t kConfigIndex = 0;
+    const auto status =
+            hwc.setActiveConfigWithConstraints(physicalId, kConfigIndex, constraints, &timeline);
+    EXPECT_EQ(NO_ERROR, status);
+
+    const std::vector<Config> kConfigs{7, 8, 9, 10, 11};
+    // Change the set of supported modes.
+    setDisplayConfigs(kConfigs);
+    setActiveConfig(11);
+    hwc.onHotplug(hwcId, hal::Connection::CONNECTED);
+    hwc.allocatePhysicalDisplay(hwcId, physicalId);
+
+    for (size_t configIndex = 0; configIndex < kConfigs.size(); configIndex++) {
+        const auto status =
+                hwc.setActiveConfigWithConstraints(physicalId, configIndex, constraints, &timeline);
+        EXPECT_EQ(NO_ERROR, status) << "Error when switching to config " << configIndex;
+    }
+}
+
+TEST_F(HWComposerConfigsTest, setActiveConfigWithConstraintsWithVsyncSwitchingSupported) {
+    testSetActiveConfigWithConstraintsCommon(/*supported=*/true);
+}
+
+TEST_F(HWComposerConfigsTest, setActiveConfigWithConstraintsWithVsyncSwitchingNotSupported) {
+    testSetActiveConfigWithConstraintsCommon(/*supported=*/false);
+}
+
 } // namespace
-} // namespace android
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index cbc1e02..2ee9c64 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
 #undef LOG_TAG
 #define LOG_TAG "LayerHistoryTest"
 
@@ -754,3 +758,6 @@
 } // namespace
 } // namespace scheduler
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
index 75a061b..373fd74 100644
--- a/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
@@ -107,3 +111,6 @@
 
 } // namespace
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 53dfe3f..8208b3f 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -69,6 +69,7 @@
     ~MockTokenManager() override = default;
 
     MOCK_METHOD1(generateTokenForPredictions, int64_t(frametimeline::TimelineItem&& prediction));
+    MOCK_CONST_METHOD1(getPredictionsForToken, std::optional<frametimeline::TimelineItem>(int64_t));
 };
 
 class MessageQueueTest : public testing::Test {
diff --git a/services/surfaceflinger/tests/unittests/PromiseTest.cpp b/services/surfaceflinger/tests/unittests/PromiseTest.cpp
deleted file mode 100644
index e4dc1fe..0000000
--- a/services/surfaceflinger/tests/unittests/PromiseTest.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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 <algorithm>
-#include <future>
-#include <string>
-#include <thread>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-#include "Promise.h"
-
-namespace android {
-namespace {
-
-using Bytes = std::vector<uint8_t>;
-
-Bytes decrement(Bytes bytes) {
-    std::transform(bytes.begin(), bytes.end(), bytes.begin(), [](auto b) { return b - 1; });
-    return bytes;
-}
-
-} // namespace
-
-TEST(PromiseTest, yield) {
-    EXPECT_EQ(42, promise::yield(42).get());
-
-    auto ptr = std::make_unique<char>('!');
-    auto future = promise::yield(std::move(ptr));
-    EXPECT_EQ('!', *future.get());
-}
-
-TEST(PromiseTest, chain) {
-    std::packaged_task<const char*()> fetchString([] { return "ifmmp-"; });
-
-    std::packaged_task<Bytes(std::string)> appendString([](std::string str) {
-        str += "!xpsme";
-        return Bytes{str.begin(), str.end()};
-    });
-
-    std::packaged_task<std::future<Bytes>(Bytes)> decrementBytes(
-            [](Bytes bytes) { return promise::defer(decrement, std::move(bytes)); });
-
-    auto fetch = fetchString.get_future();
-    std::thread fetchThread(std::move(fetchString));
-
-    std::thread appendThread, decrementThread;
-
-    EXPECT_EQ("hello, world",
-              promise::chain(std::move(fetch))
-                      .then([](const char* str) { return std::string(str); })
-                      .then([&](std::string str) {
-                          auto append = appendString.get_future();
-                          appendThread = std::thread(std::move(appendString), std::move(str));
-                          return append;
-                      })
-                      .then([&](Bytes bytes) {
-                          auto decrement = decrementBytes.get_future();
-                          decrementThread = std::thread(std::move(decrementBytes),
-                                                        std::move(bytes));
-                          return decrement;
-                      })
-                      .then([](std::future<Bytes> bytes) { return bytes; })
-                      .then([](const Bytes& bytes) {
-                          return std::string(bytes.begin(), bytes.end());
-                      })
-                      .get());
-
-    fetchThread.join();
-    appendThread.join();
-    decrementThread.join();
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 83ad737..45f0ee6 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
 #undef LOG_TAG
 #define LOG_TAG "SchedulerUnittests"
 
@@ -1545,40 +1549,117 @@
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
 }
 
-TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUnknownUid) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_30);
-    EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(1234));
-}
-
 TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUid) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_30);
-    const uid_t uid = 1234;
-    refreshRateConfigs->setPreferredRefreshRateForUid({uid, 30});
-    EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+    const auto frameRate = Fps(30.f);
+    EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDivider(frameRate));
 
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
-    EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+    EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDivider(frameRate));
 
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_72);
-    EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+    EXPECT_EQ(0, refreshRateConfigs->getRefreshRateDivider(frameRate));
 
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
-    EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+    EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDivider(frameRate));
 
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120);
-    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(frameRate));
 
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
-    refreshRateConfigs->setPreferredRefreshRateForUid({uid, 22.5f});
-    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
-    refreshRateConfigs->setPreferredRefreshRateForUid({uid, 22.6f});
-    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(Fps(22.5f)));
+    EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(Fps(22.6f)));
+}
+
+TEST_F(RefreshRateConfigsTest, populatePreferredFrameRate_noLayers) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
+                                                 HWC_CONFIG_ID_120);
+
+    auto layers = std::vector<LayerRequirement>{};
+    ASSERT_TRUE(refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f)).empty());
+}
+
+TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
+                                                 HWC_CONFIG_ID_120);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    layers[0].name = "Test layer";
+    layers[0].ownerUid = 1234;
+    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+    auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_EQ(1, frameRateOverrides.size());
+    ASSERT_EQ(1, frameRateOverrides.count(1234));
+    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+    layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_EQ(1, frameRateOverrides.size());
+    ASSERT_EQ(1, frameRateOverrides.count(1234));
+    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+    layers[0].vote = LayerVoteType::NoVote;
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_TRUE(frameRateOverrides.empty());
+
+    layers[0].vote = LayerVoteType::Min;
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_TRUE(frameRateOverrides.empty());
+
+    layers[0].vote = LayerVoteType::Max;
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_TRUE(frameRateOverrides.empty());
+
+    layers[0].vote = LayerVoteType::Heuristic;
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_TRUE(frameRateOverrides.empty());
+}
+
+TEST_F(RefreshRateConfigsTest, populatePreferredFrameRate_twoUids) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
+                                                 HWC_CONFIG_ID_120);
+
+    auto layers = std::vector<LayerRequirement>{
+            LayerRequirement{.ownerUid = 1234, .weight = 1.0f},
+            LayerRequirement{.ownerUid = 5678, .weight = 1.0f},
+    };
+
+    layers[0].name = "Test layer 1234";
+    layers[0].desiredRefreshRate = Fps(60.0f);
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+
+    layers[1].name = "Test layer 5678";
+    layers[1].desiredRefreshRate = Fps(30.0f);
+    layers[1].vote = LayerVoteType::ExplicitDefault;
+    auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+
+    ASSERT_EQ(2, frameRateOverrides.size());
+    ASSERT_EQ(1, frameRateOverrides.count(1234));
+    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+    ASSERT_EQ(1, frameRateOverrides.count(5678));
+    ASSERT_EQ(30.0f, frameRateOverrides.at(5678).getValue());
+
+    layers[1].vote = LayerVoteType::Heuristic;
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_EQ(1, frameRateOverrides.size());
+    ASSERT_EQ(1, frameRateOverrides.count(1234));
+    ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+    layers[1].ownerUid = 1234;
+    frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+    ASSERT_TRUE(frameRateOverrides.empty());
 }
 
 } // namespace
 } // namespace scheduler
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index e93d0d0..2188402 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
 #undef LOG_TAG
 #define LOG_TAG "SchedulerUnittests"
 
@@ -216,3 +220,6 @@
 } // namespace
 } // namespace scheduler
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index c47b141..1bbe8e2 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -432,7 +432,7 @@
     EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
 }
 
-TEST_P(SetFrameRateTest, SetAndGetRearentChildren) {
+TEST_P(SetFrameRateTest, SetAndGetReparentChildren) {
     EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
 
     const auto& layerFactory = GetParam();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
index cd3f6ab..65efc85 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
@@ -144,7 +144,7 @@
     // SF should have a display token.
     const auto displayId = Case::Display::DISPLAY_ID::get();
     ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-    ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
+    ASSERT_EQ(mFlinger.mutablePhysicalDisplayTokens().count(displayId), 1);
     auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[displayId];
 
     verifyDisplayIsConnected<Case>(displayToken);
@@ -259,14 +259,6 @@
     processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
 }
 
-TEST_F(HandleTransactionLockedTest,
-       processesHotplugConnectPrimaryDisplayWithExternalAlreadyConnected) {
-    // Inject an external display.
-    ExternalDisplayVariant::injectHwcDisplay(this);
-
-    processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
-}
-
 TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) {
     // Inject a primary display.
     PrimaryDisplayVariant::injectHwcDisplay(this);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 61f0788..cedb404 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -223,6 +223,7 @@
     // Various native window calls will be made.
     Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
     Case::Display::setupHwcGetActiveConfigCallExpectations(this);
+    Case::Display::setupHwcGetConfigsCallExpectations(this);
     Case::WideColorSupport::setupComposerCallExpectations(this);
     Case::HdrSupport::setupComposerCallExpectations(this);
     Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
@@ -236,6 +237,7 @@
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
+        mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId);
         state.physical = {.id = *displayId, .type = *connectionType, .hwcDisplayId = *hwcDisplayId};
     }
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 030073c..b57d473 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -78,7 +78,7 @@
     }
 
     std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
-            const scheduler::RefreshRateConfigs& /*refreshRateConfigs*/) override {
+            Fps /*currentRefreshRate*/) override {
         return std::make_unique<scheduler::FakePhaseOffsets>();
     }
 
@@ -156,8 +156,8 @@
     }
 
     std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
-            std::shared_ptr<TimeStats> timeStats) override {
-        return std::make_unique<mock::FrameTimeline>(timeStats);
+            std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid = 0) override {
+        return std::make_unique<mock::FrameTimeline>(timeStats, surfaceFlingerPid);
     }
 
     using CreateBufferQueueFunction =
@@ -231,8 +231,7 @@
         mFlinger->mRefreshRateStats =
                 std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
                                                               /*powerMode=*/hal::PowerMode::OFF);
-        mFlinger->mVsyncConfiguration =
-                mFactory.createVsyncConfiguration(*mFlinger->mRefreshRateConfigs);
+        mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(currFps);
         mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs());
 
         mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker),
@@ -372,14 +371,14 @@
                              const Vector<DisplayState>& displays, uint32_t flags,
                              const sp<IBinder>& applyToken,
                              const InputWindowCommands& inputWindowCommands,
-                             int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
-                             bool hasListenerCallbacks,
+                             int64_t desiredPresentTime, bool isAutoTimestamp,
+                             const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
                              std::vector<ListenerCallbacks>& listenerCallbacks,
                              uint64_t transactionId) {
         return mFlinger->setTransactionState(frameTimelineVsyncId, states, displays, flags,
                                              applyToken, inputWindowCommands, desiredPresentTime,
-                                             uncacheBuffer, hasListenerCallbacks, listenerCallbacks,
-                                             transactionId);
+                                             isAutoTimestamp, uncacheBuffer, hasListenerCallbacks,
+                                             listenerCallbacks, transactionId);
     }
 
     auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); };
@@ -388,6 +387,8 @@
         return mFlinger->onTransact(code, data, reply, flags);
     }
 
+    auto getGPUContextPriority() { return mFlinger->getGPUContextPriority(); }
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
@@ -560,8 +561,15 @@
                 const auto physicalId = PhysicalDisplayId::tryCast(mDisplayId);
                 LOG_ALWAYS_FATAL_IF(!physicalId);
                 flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId);
-                (mIsPrimary ? flinger->mutableInternalHwcDisplayId()
-                            : flinger->mutableExternalHwcDisplayId()) = mHwcDisplayId;
+                if (mIsPrimary) {
+                    flinger->mutableInternalHwcDisplayId() = mHwcDisplayId;
+                } else {
+                    // If there is an external HWC display there should always be an internal ID
+                    // as well. Set it to some arbitrary value.
+                    auto& internalId = flinger->mutableInternalHwcDisplayId();
+                    if (!internalId) internalId = mHwcDisplayId - 1;
+                    flinger->mutableExternalHwcDisplayId() = mHwcDisplayId;
+                }
             }
         }
 
@@ -690,6 +698,7 @@
     void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override {}
     void repaintEverythingForHWC() override {}
     void kernelTimerChanged(bool) override {}
+    void triggerOnFrameRateOverridesChanged() {}
 
     surfaceflinger::test::Factory mFactory;
     sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index bbcc0c9..fb9afd4 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -17,6 +17,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
@@ -356,9 +357,9 @@
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
-    mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerDeadlineMissed);
+    mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerCpuDeadlineMissed);
     mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerGpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(JankType::Display);
+    mTimeStats->incrementJankyFrames(JankType::DisplayHAL);
     mTimeStats->incrementJankyFrames(JankType::AppDeadlineMissed);
     mTimeStats->incrementJankyFrames(JankType::None);
 
@@ -383,10 +384,10 @@
 
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::SurfaceFlingerDeadlineMissed);
+                                     JankType::SurfaceFlingerCpuDeadlineMissed);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
                                      JankType::SurfaceFlingerGpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::Display);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::DisplayHAL);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
                                      JankType::AppDeadlineMissed);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::None);
@@ -848,10 +849,10 @@
             std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
 
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::SurfaceFlingerDeadlineMissed);
+                                     JankType::SurfaceFlingerCpuDeadlineMissed);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
                                      JankType::SurfaceFlingerGpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::Display);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::DisplayHAL);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
                                      JankType::AppDeadlineMissed);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::None);
@@ -987,9 +988,9 @@
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
 
-    mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerDeadlineMissed);
+    mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerCpuDeadlineMissed);
     mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerGpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(JankType::Display);
+    mTimeStats->incrementJankyFrames(JankType::DisplayHAL);
     mTimeStats->incrementJankyFrames(JankType::AppDeadlineMissed);
     mTimeStats->incrementJankyFrames(JankType::None);
 
@@ -1062,10 +1063,10 @@
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
 
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::SurfaceFlingerDeadlineMissed);
+                                     JankType::SurfaceFlingerCpuDeadlineMissed);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
                                      JankType::SurfaceFlingerGpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::Display);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::DisplayHAL);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
                                      JankType::AppDeadlineMissed);
     mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::None);
@@ -1320,4 +1321,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index c36d994..06275c6 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -17,6 +17,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #undef LOG_TAG
 #define LOG_TAG "CompositionTest"
@@ -97,7 +98,8 @@
         uint32_t flags = 0;
         sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
         InputWindowCommands inputWindowCommands;
-        int64_t desiredPresentTime = -1;
+        int64_t desiredPresentTime = 0;
+        bool isAutoTimestamp = true;
         int64_t frameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
         client_cache_t uncacheBuffer;
         int64_t id = -1;
@@ -114,11 +116,13 @@
     }
 
     void setupSingle(TransactionInfo& transaction, uint32_t flags, bool syncInputWindows,
-                     int64_t desiredPresentTime, int64_t frameTimelineVsyncId) {
+                     int64_t desiredPresentTime, bool isAutoTimestamp,
+                     int64_t frameTimelineVsyncId) {
         mTransactionNumber++;
         transaction.flags |= flags; // ISurfaceComposer::eSynchronous;
         transaction.inputWindowCommands.syncInputWindows = syncInputWindows;
         transaction.desiredPresentTime = desiredPresentTime;
+        transaction.isAutoTimestamp = isAutoTimestamp;
         transaction.frameTimelineVsyncId = frameTimelineVsyncId;
     }
 
@@ -129,13 +133,15 @@
         EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillOnce(Return(systemTime()));
         TransactionInfo transaction;
         setupSingle(transaction, flags, syncInputWindows,
-                    /*desiredPresentTime*/ -1, ISurfaceComposer::INVALID_VSYNC_ID);
+                    /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+                    ISurfaceComposer::INVALID_VSYNC_ID);
         nsecs_t applicationTime = systemTime();
         mFlinger.setTransactionState(transaction.frameTimelineVsyncId, transaction.states,
                                      transaction.displays, transaction.flags,
                                      transaction.applyToken, transaction.inputWindowCommands,
-                                     transaction.desiredPresentTime, transaction.uncacheBuffer,
-                                     mHasListenerCallbacks, mCallbacks, transaction.id);
+                                     transaction.desiredPresentTime, transaction.isAutoTimestamp,
+                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transaction.id);
 
         // This transaction should not have been placed on the transaction queue.
         // If transaction is synchronous or syncs input windows, SF
@@ -164,13 +170,15 @@
                 .WillOnce(Return(time + nsecs_t(5 * 1e8)));
         TransactionInfo transaction;
         setupSingle(transaction, flags, syncInputWindows,
-                    /*desiredPresentTime*/ time + s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
+                    /*desiredPresentTime*/ time + s2ns(1), false,
+                    ISurfaceComposer::INVALID_VSYNC_ID);
         nsecs_t applicationSentTime = systemTime();
         mFlinger.setTransactionState(transaction.frameTimelineVsyncId, transaction.states,
                                      transaction.displays, transaction.flags,
                                      transaction.applyToken, transaction.inputWindowCommands,
-                                     transaction.desiredPresentTime, transaction.uncacheBuffer,
-                                     mHasListenerCallbacks, mCallbacks, transaction.id);
+                                     transaction.desiredPresentTime, transaction.isAutoTimestamp,
+                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transaction.id);
 
         nsecs_t returnedTime = systemTime();
         EXPECT_LE(returnedTime, applicationSentTime + s2ns(5));
@@ -189,20 +197,23 @@
         // transaction that should go on the pending thread
         TransactionInfo transactionA;
         setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
-                    /*desiredPresentTime*/ time + s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
+                    /*desiredPresentTime*/ time + s2ns(1), false,
+                    ISurfaceComposer::INVALID_VSYNC_ID);
 
         // transaction that would not have gone on the pending thread if not
         // blocked
         TransactionInfo transactionB;
         setupSingle(transactionB, flags, syncInputWindows,
-                    /*desiredPresentTime*/ -1, ISurfaceComposer::INVALID_VSYNC_ID);
+                    /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+                    ISurfaceComposer::INVALID_VSYNC_ID);
 
         nsecs_t applicationSentTime = systemTime();
         mFlinger.setTransactionState(transactionA.frameTimelineVsyncId, transactionA.states,
                                      transactionA.displays, transactionA.flags,
                                      transactionA.applyToken, transactionA.inputWindowCommands,
-                                     transactionA.desiredPresentTime, transactionA.uncacheBuffer,
-                                     mHasListenerCallbacks, mCallbacks, transactionA.id);
+                                     transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+                                     transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionA.id);
 
         // This thread should not have been blocked by the above transaction
         // (5s is the timeout period that applyTransactionState waits for SF to
@@ -213,8 +224,9 @@
         mFlinger.setTransactionState(transactionB.frameTimelineVsyncId, transactionB.states,
                                      transactionB.displays, transactionB.flags,
                                      transactionB.applyToken, transactionB.inputWindowCommands,
-                                     transactionB.desiredPresentTime, transactionB.uncacheBuffer,
-                                     mHasListenerCallbacks, mCallbacks, transactionB.id);
+                                     transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+                                     transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+                                     transactionB.id);
 
         // this thread should have been blocked by the above transaction
         // if this is an animation, this thread should be blocked for 5s
@@ -256,12 +268,12 @@
             .WillOnce(Return(s2ns(2)));
     TransactionInfo transactionA; // transaction to go on pending queue
     setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
-                /*desiredPresentTime*/ s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
+                /*desiredPresentTime*/ s2ns(1), false, ISurfaceComposer::INVALID_VSYNC_ID);
     mFlinger.setTransactionState(transactionA.frameTimelineVsyncId, transactionA.states,
                                  transactionA.displays, transactionA.flags, transactionA.applyToken,
                                  transactionA.inputWindowCommands, transactionA.desiredPresentTime,
-                                 transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
-                                 transactionA.id);
+                                 transactionA.isAutoTimestamp, transactionA.uncacheBuffer,
+                                 mHasListenerCallbacks, mCallbacks, transactionA.id);
 
     auto& transactionQueue = mFlinger.getTransactionQueue();
     ASSERT_EQ(1, transactionQueue.size());
@@ -279,8 +291,8 @@
     empty.applyToken = sp<IBinder>();
     mFlinger.setTransactionState(empty.frameTimelineVsyncId, empty.states, empty.displays,
                                  empty.flags, empty.applyToken, empty.inputWindowCommands,
-                                 empty.desiredPresentTime, empty.uncacheBuffer,
-                                 mHasListenerCallbacks, mCallbacks, empty.id);
+                                 empty.desiredPresentTime, empty.isAutoTimestamp,
+                                 empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks, empty.id);
 
     // flush transaction queue should flush as desiredPresentTime has
     // passed
@@ -333,4 +345,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 72b5396..00cf574 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -17,6 +17,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
@@ -1152,4 +1153,4 @@
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index a142022..a4ddbf4 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -17,6 +17,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
@@ -492,4 +493,4 @@
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index a7568e4..b9651ea 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 #define LOG_NDEBUG 0
@@ -491,3 +495,6 @@
 }
 
 } // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
index 2a35f69..bb7578d 100644
--- a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
@@ -19,6 +19,7 @@
 
 #include <gmock/gmock.h>
 #include <log/log.h>
+#include <chrono>
 #include <thread>
 
 #include "Scheduler/VsyncConfiguration.h"
@@ -27,14 +28,15 @@
 
 namespace android::scheduler {
 
+using namespace std::chrono_literals;
+
 class TestableWorkDuration : public impl::WorkDuration {
 public:
     TestableWorkDuration(Fps currentFps, nsecs_t sfDuration, nsecs_t appDuration,
                          nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
                          nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
-          : impl::WorkDuration({Fps(60.0f), Fps(90.0f)}, currentFps, sfDuration, appDuration,
-                               sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration,
-                               appEarlyGlDuration) {}
+          : impl::WorkDuration(currentFps, sfDuration, appDuration, sfEarlyDuration,
+                               appEarlyDuration, sfEarlyGlDuration, appEarlyGlDuration) {}
 };
 
 class WorkDurationTest : public testing::Test {
@@ -171,9 +173,9 @@
                          std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
                          std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs,
                          nsecs_t thresholdForNextVsync)
-          : impl::PhaseOffsets({Fps(60.0f), Fps(90.0f)}, Fps(60.0f), vsyncPhaseOffsetNs,
-                               sfVSyncPhaseOffsetNs, earlySfOffsetNs, earlyGpuSfOffsetNs,
-                               earlyAppOffsetNs, earlyGpuAppOffsetNs, highFpsVsyncPhaseOffsetNs,
+          : impl::PhaseOffsets(Fps(60.0f), vsyncPhaseOffsetNs, sfVSyncPhaseOffsetNs,
+                               earlySfOffsetNs, earlyGpuSfOffsetNs, earlyAppOffsetNs,
+                               earlyGpuAppOffsetNs, highFpsVsyncPhaseOffsetNs,
                                highFpsSfVSyncPhaseOffsetNs, highFpsEarlySfOffsetNs,
                                highFpsEarlyGpuSfOffsetNs, highFpsEarlyAppOffsetNs,
                                highFpsEarlyGpuAppOffsetNs, thresholdForNextVsync) {}
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp
index f784df3..ff005a0 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp
@@ -19,8 +19,8 @@
 namespace android::mock {
 
 // Explicit default instantiation is recommended.
-FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats)
-      : android::frametimeline::impl::FrameTimeline(timeStats) {}
+FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid)
+      : android::frametimeline::impl::FrameTimeline(timeStats, surfaceFlingerPid) {}
 FrameTimeline::~FrameTimeline() = default;
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
index 6b12536..0a6a9f4 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
@@ -26,14 +26,12 @@
     // No need to create mocks for SurfaceFrame and TokenManager yet. They are very small components
     // and do not have external dependencies like perfetto.
 public:
-    FrameTimeline(std::shared_ptr<TimeStats> timeStats);
+    FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid);
     ~FrameTimeline();
 
     MOCK_METHOD0(onBootFinished, void());
-    MOCK_METHOD2(addSurfaceFrame,
-                 void(std::shared_ptr<frametimeline::SurfaceFrame>,
-                      frametimeline::SurfaceFrame::PresentState));
-    MOCK_METHOD2(setSfWakeUp, void(int64_t, nsecs_t));
+    MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>));
+    MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, nsecs_t));
     MOCK_METHOD2(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&));
 };
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 078d8e0..ba2e4db 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -33,6 +33,7 @@
     MOCK_CONST_METHOD0(isVisible, bool());
     MOCK_METHOD0(createClone, sp<Layer>());
     MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
+    MOCK_CONST_METHOD0(getOwnerUid, uid_t());
 };
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index efaa9fa..453c93a 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -30,7 +30,7 @@
     ~MessageQueue() override;
 
     MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
-    MOCK_METHOD1(setEventConnection, void(const sp<EventThreadConnection>& connection));
+    MOCK_METHOD1(setInjector, void(sp<EventThreadConnection>));
     MOCK_METHOD0(waitMessage, void());
     MOCK_METHOD1(postMessage, void(sp<MessageHandler>&&));
     MOCK_METHOD0(invalidate, void());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 72bc89c..ab19886 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -29,6 +29,7 @@
                       scheduler::RefreshRateConfigEvent));
     MOCK_METHOD0(repaintEverythingForHWC, void());
     MOCK_METHOD1(kernelTimerChanged, void(bool));
+    MOCK_METHOD0(triggerOnFrameRateOverridesChanged, void());
 };
 
 struct NoOpSchedulerCallback final : ISchedulerCallback {
@@ -37,6 +38,7 @@
                            scheduler::RefreshRateConfigEvent) override {}
     void repaintEverythingForHWC() override {}
     void kernelTimerChanged(bool) override {}
+    void triggerOnFrameRateOverridesChanged() {}
 };
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h
index 5c5b18e..3cbfed9 100644
--- a/services/surfaceflinger/tests/utils/TransactionUtils.h
+++ b/services/surfaceflinger/tests/utils/TransactionUtils.h
@@ -18,6 +18,7 @@
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
 
 #include <chrono>
 
@@ -186,4 +187,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
\ No newline at end of file