Merge "vkjson: correctly handle std::numeric_limits<float>::infinity()"
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 1416629..fe55fe7 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1989,12 +1989,12 @@
     }
 
     if (ds.options_->do_fb) {
-        ds.screenshot_path_ = ds.GetPath(".png");
+        ds.screenshot_path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.png" : ".png");
     }
     ds.tmp_path_ = ds.GetPath(".tmp");
     ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt");
 
-    std::string destination = ds.options_->bugreport_fd.get() != -1
+    std::string destination = ds.CalledByApi()
                                   ? StringPrintf("[fd:%d]", ds.options_->bugreport_fd.get())
                                   : ds.bugreport_internal_dir_.c_str();
     MYLOGD(
@@ -2008,7 +2008,7 @@
         ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
 
     if (ds.options_->do_zip_file) {
-        ds.path_ = ds.GetPath(".zip");
+        ds.path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.zip" : ".zip");
         MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
         create_parent_dirs(ds.path_.c_str());
         ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
@@ -2043,7 +2043,7 @@
         MYLOGI("changing suffix from %s to %s\n", ds.name_.c_str(), name.c_str());
         ds.name_ = name;
         if (!ds.screenshot_path_.empty()) {
-            std::string new_screenshot_path = ds.GetPath(".png");
+            std::string new_screenshot_path = ds.GetPath(ds.CalledByApi() ? "-tmp.png" : ".png");
             if (rename(ds.screenshot_path_.c_str(), new_screenshot_path.c_str())) {
                 MYLOGE("rename(%s, %s): %s\n", ds.screenshot_path_.c_str(),
                        new_screenshot_path.c_str(), strerror(errno));
@@ -2061,7 +2061,7 @@
         } else {
             do_text_file = false;
             // If the user has changed the suffix, we need to change the zip file name.
-            std::string new_path = ds.GetPath(".zip");
+            std::string new_path = ds.GetPath(ds.CalledByApi() ? "-tmp.zip" : ".zip");
             if (ds.path_ != new_path) {
                 MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str());
                 if (rename(ds.path_.c_str(), new_path.c_str())) {
@@ -2608,7 +2608,7 @@
         // Dump state for the default case. This also drops root.
         RunStatus s = DumpstateDefault();
         if (s != RunStatus::OK) {
-            if (s == RunStatus::USER_CONSENT_TIMED_OUT) {
+            if (s == RunStatus::USER_CONSENT_DENIED) {
                 HandleUserConsentDenied();
             }
             return s;
@@ -2716,6 +2716,10 @@
            ds.consent_callback_->getResult() == UserConsentResult::DENIED;
 }
 
+bool Dumpstate::CalledByApi() const {
+    return ds.options_->bugreport_fd.get() != -1 ? true : false;
+}
+
 void Dumpstate::CleanupFiles() {
     android::os::UnlinkAndLogOnError(tmp_path_);
     android::os::UnlinkAndLogOnError(screenshot_path_);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index ae6a721..fe330df 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -341,6 +341,11 @@
     bool IsUserConsentDenied() const;
 
     /*
+     * Returns true if dumpstate is called by bugreporting API
+     */
+    bool CalledByApi() const;
+
+    /*
      * Structure to hold options that determine the behavior of dumpstate.
      */
     struct DumpOptions {
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 5bde7db..f7acaf1 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -14,20 +14,21 @@
  * limitations under the License.
  */
 
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <fcntl.h>
-#include <libgen.h>
-
 #include <android-base/file.h>
 #include <android/os/BnDumpstate.h>
 #include <android/os/BnDumpstateListener.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 #include <cutils/properties.h>
+#include <fcntl.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <libgen.h>
 #include <ziparchive/zip_archive.h>
 
+#include <fstream>
+#include <regex>
+
 #include "dumpstate.h"
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
@@ -44,6 +45,11 @@
 
 namespace {
 
+struct SectionInfo {
+    std::string name;
+    int32_t size_bytes;
+};
+
 sp<IDumpstate> GetDumpstateService() {
     return android::interface_cast<IDumpstate>(
         android::defaultServiceManager()->getService(String16("dumpstate")));
@@ -55,14 +61,79 @@
                                    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
 }
 
-}  // namespace
+void GetEntry(const ZipArchiveHandle archive, const std::string_view entry_name, ZipEntry* data) {
+    int32_t e = FindEntry(archive, entry_name, data);
+    EXPECT_EQ(e, 0) << ErrorCodeString(e) << " entry name: " << entry_name;
+}
 
-struct SectionInfo {
-    std::string name;
-    status_t status;
-    int32_t size_bytes;
-    int32_t duration_ms;
-};
+// Extracts the main bugreport txt from the given archive and writes into output_fd.
+void ExtractBugreport(const ZipArchiveHandle* handle, int output_fd) {
+    // Read contents of main_entry.txt which is a single line indicating the name of the zip entry
+    // that contains the main bugreport txt.
+    ZipEntry main_entry;
+    GetEntry(*handle, "main_entry.txt", &main_entry);
+    std::string bugreport_txt_name;
+    bugreport_txt_name.resize(main_entry.uncompressed_length);
+    ExtractToMemory(*handle, &main_entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()),
+                    main_entry.uncompressed_length);
+
+    // Read the main bugreport txt and extract to output_fd.
+    ZipEntry entry;
+    GetEntry(*handle, bugreport_txt_name, &entry);
+    ExtractEntryToFile(*handle, &entry, output_fd);
+}
+
+bool IsSectionStart(const std::string& line, std::string* section_name) {
+    static const std::regex kSectionStart = std::regex{"DUMP OF SERVICE (.*):"};
+    std::smatch match;
+    if (std::regex_match(line, match, kSectionStart)) {
+        *section_name = match.str(1);
+        return true;
+    }
+    return false;
+}
+
+bool IsSectionEnd(const std::string& line) {
+    // Not all lines that contain "was the duration of" is a section end, but all section ends do
+    // contain "was the duration of". The disambiguation can be done by the caller.
+    return (line.find("was the duration of") != std::string::npos);
+}
+
+// Extracts the zipped bugreport and identifies the sections.
+void ParseSections(const std::string& zip_path, std::vector<SectionInfo>* sections) {
+    // Open the archive
+    ZipArchiveHandle handle;
+    ASSERT_EQ(OpenArchive(zip_path.c_str(), &handle), 0);
+
+    // Extract the main entry to a temp file
+    TemporaryFile tmp_binary;
+    ASSERT_NE(-1, tmp_binary.fd);
+    ExtractBugreport(&handle, tmp_binary.fd);
+
+    // Read line by line and identify sections
+    std::ifstream ifs(tmp_binary.path, std::ifstream::in);
+    std::string line;
+    int section_bytes = 0;
+    std::string current_section_name;
+    while (std::getline(ifs, line)) {
+        std::string section_name;
+        if (IsSectionStart(line, &section_name)) {
+            section_bytes = 0;
+            current_section_name = section_name;
+        } else if (IsSectionEnd(line)) {
+            if (!current_section_name.empty()) {
+                sections->push_back({current_section_name, section_bytes});
+            }
+            current_section_name = "";
+        } else if (!current_section_name.empty()) {
+            section_bytes += line.length();
+        }
+    }
+
+    CloseArchive(handle);
+}
+
+}  // namespace
 
 /**
  * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the
@@ -107,11 +178,11 @@
         return binder::Status::ok();
     }
 
-    binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes,
-                                     int32_t duration_ms) override {
+    binder::Status onSectionComplete(const ::std::string& name, int32_t, int32_t size_bytes,
+                                     int32_t) override {
         std::lock_guard<std::mutex> lock(lock_);
         if (sections_.get() != nullptr) {
-            sections_->push_back({name, status, size_bytes, duration_ms});
+            sections_->push_back({name, size_bytes});
         }
         return binder::Status::ok();
     }
@@ -208,29 +279,30 @@
 
     void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) {
         ZipEntry entry;
-        EXPECT_EQ(FindEntry(handle, filename, &entry), 0);
+        GetEntry(handle, filename, &entry);
         EXPECT_GT(entry.uncompressed_length, minsize);
         EXPECT_LT(entry.uncompressed_length, maxsize);
     }
 };
 
 TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) {
-    ZipEntry mainEntryLoc;
+    ZipEntry main_entry;
     // contains main entry name file
-    EXPECT_EQ(FindEntry(handle, "main_entry.txt", &mainEntryLoc), 0);
+    GetEntry(handle, "main_entry.txt", &main_entry);
 
-    char* buf = new char[mainEntryLoc.uncompressed_length];
-    ExtractToMemory(handle, &mainEntryLoc, (uint8_t*)buf, mainEntryLoc.uncompressed_length);
-    delete[] buf;
+    std::string bugreport_txt_name;
+    bugreport_txt_name.resize(main_entry.uncompressed_length);
+    ExtractToMemory(handle, &main_entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()),
+                    main_entry.uncompressed_length);
 
     // contains main entry file
-    FileExists(buf, 1000000U, 50000000U);
+    FileExists(bugreport_txt_name.c_str(), 1000000U, 50000000U);
 }
 
 TEST_F(ZippedBugReportContentsTest, ContainsVersion) {
     ZipEntry entry;
     // contains main entry name file
-    EXPECT_EQ(FindEntry(handle, "version.txt", &entry), 0);
+    GetEntry(handle, "version.txt", &entry);
 
     char* buf = new char[entry.uncompressed_length + 1];
     ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length);
@@ -244,6 +316,10 @@
     FileExists("dumpstate_board.txt", 100000U, 1000000U);
 }
 
+TEST_F(ZippedBugReportContentsTest, ContainsProtoFile) {
+    FileExists("proto/activity.proto", 100000U, 1000000U);
+}
+
 // Spot check on some files pulled from the file system
 TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) {
     // FS/proc/*/mountinfo size > 0
@@ -258,6 +334,11 @@
  */
 class BugreportSectionTest : public Test {
   public:
+    static void SetUpTestCase() {
+        ParseSections(ZippedBugreportGenerationTest::getZipFilePath(),
+                      ZippedBugreportGenerationTest::sections.get());
+    }
+
     int numMatches(const std::string& substring) {
         int matches = 0;
         for (auto const& section : *ZippedBugreportGenerationTest::sections) {
@@ -267,10 +348,11 @@
         }
         return matches;
     }
+
     void SectionExists(const std::string& sectionName, int minsize) {
         for (auto const& section : *ZippedBugreportGenerationTest::sections) {
             if (sectionName == section.name) {
-                EXPECT_GE(section.size_bytes, minsize);
+                EXPECT_GE(section.size_bytes, minsize) << " for section:" << sectionName;
                 return;
             }
         }
@@ -278,71 +360,59 @@
     }
 };
 
-// Test all sections are generated without timeouts or errors
-TEST_F(BugreportSectionTest, GeneratedWithoutErrors) {
-    for (auto const& section : *ZippedBugreportGenerationTest::sections) {
-        EXPECT_EQ(section.status, 0) << section.name << " failed with status " << section.status;
-    }
-}
-
 TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) {
-    int numSections = numMatches("DUMPSYS CRITICAL");
+    int numSections = numMatches("CRITICAL");
     EXPECT_GE(numSections, 3);
 }
 
 TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) {
-    int numSections = numMatches("DUMPSYS HIGH");
+    int numSections = numMatches("HIGH");
     EXPECT_GE(numSections, 2);
 }
 
 TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) {
-    int allSections = numMatches("DUMPSYS");
-    int criticalSections = numMatches("DUMPSYS CRITICAL");
-    int highSections = numMatches("DUMPSYS HIGH");
+    int allSections = ZippedBugreportGenerationTest::sections->size();
+    int criticalSections = numMatches("CRITICAL");
+    int highSections = numMatches("HIGH");
     int normalSections = allSections - criticalSections - highSections;
 
     EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections
                                   << "High:" << highSections << "Normal:" << normalSections << ")";
 }
 
-TEST_F(BugreportSectionTest, Atleast1ProtoDumpsysSectionGenerated) {
-    int numSections = numMatches("proto/");
-    EXPECT_GE(numSections, 1);
-}
-
 // Test if some critical sections are being generated.
 TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) {
-    SectionExists("DUMPSYS CRITICAL - SurfaceFlinger", /* bytes= */ 10000);
+    SectionExists("CRITICAL SurfaceFlinger", /* bytes= */ 10000);
 }
 
 TEST_F(BugreportSectionTest, ActivitySectionsGenerated) {
-    SectionExists("DUMPSYS CRITICAL - activity", /* bytes= */ 5000);
-    SectionExists("DUMPSYS - activity", /* bytes= */ 10000);
+    SectionExists("CRITICAL activity", /* bytes= */ 5000);
+    SectionExists("activity", /* bytes= */ 10000);
 }
 
 TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) {
-    SectionExists("DUMPSYS CRITICAL - cpuinfo", /* bytes= */ 1000);
+    SectionExists("CRITICAL cpuinfo", /* bytes= */ 1000);
 }
 
 TEST_F(BugreportSectionTest, WindowSectionGenerated) {
-    SectionExists("DUMPSYS CRITICAL - window", /* bytes= */ 20000);
+    SectionExists("CRITICAL window", /* bytes= */ 20000);
 }
 
 TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
-    SectionExists("DUMPSYS HIGH - connectivity", /* bytes= */ 5000);
-    SectionExists("DUMPSYS - connectivity", /* bytes= */ 5000);
+    SectionExists("HIGH connectivity", /* bytes= */ 3000);
+    SectionExists("connectivity", /* bytes= */ 5000);
 }
 
 TEST_F(BugreportSectionTest, MeminfoSectionGenerated) {
-    SectionExists("DUMPSYS HIGH - meminfo", /* bytes= */ 100000);
+    SectionExists("HIGH meminfo", /* bytes= */ 100000);
 }
 
 TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) {
-    SectionExists("DUMPSYS - batterystats", /* bytes= */ 1000);
+    SectionExists("batterystats", /* bytes= */ 1000);
 }
 
 TEST_F(BugreportSectionTest, WifiSectionGenerated) {
-    SectionExists("DUMPSYS - wifi", /* bytes= */ 100000);
+    SectionExists("wifi", /* bytes= */ 100000);
 }
 
 class DumpstateBinderTest : public Test {
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index a738527..792ff91 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -1,5 +1,6 @@
 syntax = "proto2";
 option optimize_for = LITE_RUNTIME;
+package android.surfaceflinger;
 
 message Trace {
     repeated Increment increment = 1;
diff --git a/cmds/surfacereplayer/replayer/Event.cpp b/cmds/surfacereplayer/replayer/Event.cpp
index 390d398..64db5f0 100644
--- a/cmds/surfacereplayer/replayer/Event.cpp
+++ b/cmds/surfacereplayer/replayer/Event.cpp
@@ -17,6 +17,7 @@
 #include "Event.h"
 
 using namespace android;
+using Increment = surfaceflinger::Increment;
 
 Event::Event(Increment::IncrementCase type) : mIncrementType(type) {}
 
diff --git a/cmds/surfacereplayer/replayer/Event.h b/cmds/surfacereplayer/replayer/Event.h
index 44b60f5..09a7c24 100644
--- a/cmds/surfacereplayer/replayer/Event.h
+++ b/cmds/surfacereplayer/replayer/Event.h
@@ -24,6 +24,8 @@
 
 namespace android {
 
+using Increment = surfaceflinger::Increment;
+
 class Event {
   public:
     Event(Increment::IncrementCase);
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index d7709cc..3b94618 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -38,6 +38,8 @@
 #include <unordered_map>
 #include <utility>
 
+using namespace android::surfaceflinger;
+
 namespace android {
 
 const auto DEFAULT_PATH = "/data/local/tmp/SurfaceTrace.dat";
diff --git a/data/etc/android.hardware.se.omapi.ese.xml b/data/etc/android.hardware.se.omapi.ese.xml
new file mode 100644
index 0000000..3b1d81c
--- /dev/null
+++ b/data/etc/android.hardware.se.omapi.ese.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<!-- This feature indicates that the device supports The device supports
+     Open Mobile API capable ESE-based secure elements-->
+<permissions>
+    <feature name="android.hardware.se.omapi.ese" />
+</permissions>
diff --git a/data/etc/android.hardware.se.omapi.sd.xml b/data/etc/android.hardware.se.omapi.sd.xml
new file mode 100644
index 0000000..8fc2869
--- /dev/null
+++ b/data/etc/android.hardware.se.omapi.sd.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<!-- This feature indicates that the device supports The device supports
+     Open Mobile API capable SD-based secure elements-->
+<permissions>
+    <feature name="android.hardware.se.omapi.sd" />
+</permissions>
diff --git a/data/etc/android.hardware.se.omapi.uicc.xml b/data/etc/android.hardware.se.omapi.uicc.xml
new file mode 100644
index 0000000..9c6f143
--- /dev/null
+++ b/data/etc/android.hardware.se.omapi.uicc.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<!-- This feature indicates that the device supports The device supports
+     Open Mobile API capable UICC-based secure elements-->
+<permissions>
+    <feature name="android.hardware.se.omapi.uicc" />
+</permissions>
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index 44883cc..1b589bc 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -83,7 +83,7 @@
  * Power a callback to be run on the next frame.  The data pointer provided will
  * be passed to the callback function when it's called.
  */
-void AChoreographer_postFrameCallback64(AChoreographer* chroreographer,
+void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
                 AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29);
 
 /**
diff --git a/include/input/Input.h b/include/input/Input.h
index ad8c233..cbd1a41 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -469,14 +469,16 @@
 
     inline float getYPrecision() const { return mYPrecision; }
 
-    inline float getRawXCursorPosition() const { return mXCursorPosition; }
+    inline float getRawXCursorPosition() const { return mRawXCursorPosition; }
 
     float getXCursorPosition() const;
 
-    inline float getRawYCursorPosition() const { return mYCursorPosition; }
+    inline float getRawYCursorPosition() const { return mRawYCursorPosition; }
 
     float getYCursorPosition() const;
 
+    void setCursorPosition(float x, float y);
+
     static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); }
 
     inline nsecs_t getDownTime() const { return mDownTime; }
@@ -623,8 +625,8 @@
     void initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action,
                     int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState,
                     int32_t buttonState, MotionClassification classification, float xOffset,
-                    float yOffset, float xPrecision, float yPrecision, float mXCursorPosition,
-                    float mYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
+                    float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition,
+                    float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
                     size_t pointerCount, const PointerProperties* pointerProperties,
                     const PointerCoords* pointerCoords);
 
@@ -676,8 +678,8 @@
     float mYOffset;
     float mXPrecision;
     float mYPrecision;
-    float mXCursorPosition;
-    float mYCursorPosition;
+    float mRawXCursorPosition;
+    float mRawYCursorPosition;
     nsecs_t mDownTime;
     Vector<PointerProperties> mPointerProperties;
     Vector<nsecs_t> mSampleEventTimes;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index b5fbf42..6bd97d58 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -102,10 +102,6 @@
                 reinterpret_cast<IBinder*>(obj.cookie)->incStrong(who);
             }
             return;
-        case BINDER_TYPE_WEAK_BINDER:
-            if (obj.binder)
-                reinterpret_cast<RefBase::weakref_type*>(obj.binder)->incWeak(who);
-            return;
         case BINDER_TYPE_HANDLE: {
             const sp<IBinder> b = proc->getStrongProxyForHandle(obj.handle);
             if (b != nullptr) {
@@ -114,11 +110,6 @@
             }
             return;
         }
-        case BINDER_TYPE_WEAK_HANDLE: {
-            const wp<IBinder> b = proc->getWeakProxyForHandle(obj.handle);
-            if (b != nullptr) b.get_refs()->incWeak(who);
-            return;
-        }
         case BINDER_TYPE_FD: {
             if ((obj.cookie != 0) && (outAshmemSize != nullptr) && ashmem_valid(obj.handle)) {
                 // If we own an ashmem fd, keep track of how much memory it refers to.
@@ -144,10 +135,6 @@
                 reinterpret_cast<IBinder*>(obj.cookie)->decStrong(who);
             }
             return;
-        case BINDER_TYPE_WEAK_BINDER:
-            if (obj.binder)
-                reinterpret_cast<RefBase::weakref_type*>(obj.binder)->decWeak(who);
-            return;
         case BINDER_TYPE_HANDLE: {
             const sp<IBinder> b = proc->getStrongProxyForHandle(obj.handle);
             if (b != nullptr) {
@@ -156,11 +143,6 @@
             }
             return;
         }
-        case BINDER_TYPE_WEAK_HANDLE: {
-            const wp<IBinder> b = proc->getWeakProxyForHandle(obj.handle);
-            if (b != nullptr) b.get_refs()->decWeak(who);
-            return;
-        }
         case BINDER_TYPE_FD: {
             if (obj.cookie != 0) { // owned
                 if ((outAshmemSize != nullptr) && ashmem_valid(obj.handle)) {
@@ -230,55 +212,6 @@
     return finish_flatten_binder(binder, obj, out);
 }
 
-static status_t flatten_binder(const sp<ProcessState>& /*proc*/,
-    const wp<IBinder>& binder, Parcel* out)
-{
-    flat_binder_object obj;
-
-    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
-    if (binder != nullptr) {
-        sp<IBinder> real = binder.promote();
-        if (real != nullptr) {
-            IBinder *local = real->localBinder();
-            if (!local) {
-                BpBinder *proxy = real->remoteBinder();
-                if (proxy == nullptr) {
-                    ALOGE("null proxy");
-                }
-                const int32_t handle = proxy ? proxy->handle() : 0;
-                obj.hdr.type = BINDER_TYPE_WEAK_HANDLE;
-                obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
-                obj.handle = handle;
-                obj.cookie = 0;
-            } else {
-                obj.hdr.type = BINDER_TYPE_WEAK_BINDER;
-                obj.binder = reinterpret_cast<uintptr_t>(binder.get_refs());
-                obj.cookie = reinterpret_cast<uintptr_t>(binder.unsafe_get());
-            }
-            return finish_flatten_binder(real, obj, out);
-        }
-
-        // XXX How to deal?  In order to flatten the given binder,
-        // we need to probe it for information, which requires a primary
-        // reference...  but we don't have one.
-        //
-        // The OpenBinder implementation uses a dynamic_cast<> here,
-        // but we can't do that with the different reference counting
-        // implementation we are using.
-        ALOGE("Unable to unflatten Binder weak reference!");
-        obj.hdr.type = BINDER_TYPE_BINDER;
-        obj.binder = 0;
-        obj.cookie = 0;
-        return finish_flatten_binder(nullptr, obj, out);
-
-    } else {
-        obj.hdr.type = BINDER_TYPE_BINDER;
-        obj.binder = 0;
-        obj.cookie = 0;
-        return finish_flatten_binder(nullptr, obj, out);
-    }
-}
-
 inline static status_t finish_unflatten_binder(
     BpBinder* /*proxy*/, const flat_binder_object& /*flat*/,
     const Parcel& /*in*/)
@@ -305,35 +238,6 @@
     return BAD_TYPE;
 }
 
-static status_t unflatten_binder(const sp<ProcessState>& proc,
-    const Parcel& in, wp<IBinder>* out)
-{
-    const flat_binder_object* flat = in.readObject(false);
-
-    if (flat) {
-        switch (flat->hdr.type) {
-            case BINDER_TYPE_BINDER:
-                *out = reinterpret_cast<IBinder*>(flat->cookie);
-                return finish_unflatten_binder(nullptr, *flat, in);
-            case BINDER_TYPE_WEAK_BINDER:
-                if (flat->binder != 0) {
-                    out->set_object_and_refs(
-                        reinterpret_cast<IBinder*>(flat->cookie),
-                        reinterpret_cast<RefBase::weakref_type*>(flat->binder));
-                } else {
-                    *out = nullptr;
-                }
-                return finish_unflatten_binder(nullptr, *flat, in);
-            case BINDER_TYPE_HANDLE:
-            case BINDER_TYPE_WEAK_HANDLE:
-                *out = proc->getWeakProxyForHandle(flat->handle);
-                return finish_unflatten_binder(
-                    static_cast<BpBinder*>(out->unsafe_get()), *flat, in);
-        }
-    }
-    return BAD_TYPE;
-}
-
 // ---------------------------------------------------------------------------
 
 Parcel::Parcel()
@@ -590,6 +494,12 @@
     }
 }
 
+#ifdef __ANDROID_VNDK__
+constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R');
+#else
+constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T');
+#endif
+
 // Write RPC headers.  (previously just the interface token)
 status_t Parcel::writeInterfaceToken(const String16& interface)
 {
@@ -598,6 +508,7 @@
     updateWorkSourceRequestHeaderPosition();
     writeInt32(threadState->shouldPropagateWorkSource() ?
             threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource);
+    writeInt32(kHeader);
     // currently the interface identification token is just its name as a string
     return writeString16(interface);
 }
@@ -655,6 +566,12 @@
     updateWorkSourceRequestHeaderPosition();
     int32_t workSource = readInt32();
     threadState->setCallingWorkSourceUidWithoutPropagation(workSource);
+    // vendor header
+    int32_t header = readInt32();
+    if (header != kHeader) {
+        ALOGE("Expecting header 0x%x but found 0x%x. Mixing copies of libbinder?", kHeader, header);
+        return false;
+    }
     // Interface descriptor.
     const String16 str(readString16());
     if (str == interface) {
@@ -1136,11 +1053,6 @@
     return readTypedVector(val, &Parcel::readStrongBinder);
 }
 
-status_t Parcel::writeWeakBinder(const wp<IBinder>& val)
-{
-    return flatten_binder(ProcessState::self(), val, this);
-}
-
 status_t Parcel::writeRawNullableParcelable(const Parcelable* parcelable) {
     if (!parcelable) {
         return writeInt32(0);
@@ -2079,13 +1991,6 @@
     return val;
 }
 
-wp<IBinder> Parcel::readWeakBinder() const
-{
-    wp<IBinder> val;
-    unflatten_binder(ProcessState::self(), *this, &val);
-    return val;
-}
-
 status_t Parcel::readParcelable(Parcelable* parcelable) const {
     int32_t have_parcelable = 0;
     status_t status = readInt32(&have_parcelable);
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index a07b3a0..1e1bc3a 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -210,8 +210,12 @@
 
     if (e != nullptr) {
         // We need to create a new BpBinder if there isn't currently one, OR we
-        // are unable to acquire a weak reference on this current one.  See comment
-        // in getWeakProxyForHandle() for more info about this.
+        // are unable to acquire a weak reference on this current one.  The
+        // attemptIncWeak() is safe because we know the BpBinder destructor will always
+        // call expungeHandle(), which acquires the same lock we are holding now.
+        // We need to do this because there is a race condition between someone
+        // releasing a reference on this BpBinder, and a new reference on its handle
+        // arriving from the driver.
         IBinder* b = e->binder;
         if (b == nullptr || !e->refs->attemptIncWeak(this)) {
             if (handle == 0) {
@@ -257,37 +261,6 @@
     return result;
 }
 
-wp<IBinder> ProcessState::getWeakProxyForHandle(int32_t handle)
-{
-    wp<IBinder> result;
-
-    AutoMutex _l(mLock);
-
-    handle_entry* e = lookupHandleLocked(handle);
-
-    if (e != nullptr) {        
-        // We need to create a new BpBinder if there isn't currently one, OR we
-        // are unable to acquire a weak reference on this current one.  The
-        // attemptIncWeak() is safe because we know the BpBinder destructor will always
-        // call expungeHandle(), which acquires the same lock we are holding now.
-        // We need to do this because there is a race condition between someone
-        // releasing a reference on this BpBinder, and a new reference on its handle
-        // arriving from the driver.
-        IBinder* b = e->binder;
-        if (b == nullptr || !e->refs->attemptIncWeak(this)) {
-            b = BpBinder::create(handle);
-            result = b;
-            e->binder = b;
-            if (b) e->refs = b->getWeakRefs();
-        } else {
-            result = b;
-            e->refs->decWeak(this);
-        }
-    }
-
-    return result;
-}
-
 void ProcessState::expungeHandle(int32_t handle, IBinder* binder)
 {
     AutoMutex _l(mLock);
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index b65d456..28ab4d9 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -117,7 +117,6 @@
     status_t            writeString16(const std::unique_ptr<String16>& str);
     status_t            writeString16(const char16_t* str, size_t len);
     status_t            writeStrongBinder(const sp<IBinder>& val);
-    status_t            writeWeakBinder(const wp<IBinder>& val);
     status_t            writeInt32Array(size_t len, const int32_t *val);
     status_t            writeByteArray(size_t len, const uint8_t *val);
     status_t            writeBool(bool val);
@@ -273,7 +272,6 @@
     sp<IBinder>         readStrongBinder() const;
     status_t            readStrongBinder(sp<IBinder>* val) const;
     status_t            readNullableStrongBinder(sp<IBinder>* val) const;
-    wp<IBinder>         readWeakBinder() const;
 
     template<typename T>
     status_t            readParcelableVector(
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 3af9eed..8339976 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -58,7 +58,6 @@
                                     void* userData);
 
             sp<IBinder>         getStrongProxyForHandle(int32_t handle);
-            wp<IBinder>         getWeakProxyForHandle(int32_t handle);
             void                expungeHandle(int32_t handle, IBinder* binder);
 
             void                spawnPooledThread(bool isMain);
diff --git a/libs/binder/ndk/include_apex/android/binder_process.h b/libs/binder/ndk/include_apex/android/binder_process.h
index 69e6387..fdefbb4 100644
--- a/libs/binder/ndk/include_apex/android/binder_process.h
+++ b/libs/binder/ndk/include_apex/android/binder_process.h
@@ -27,7 +27,7 @@
 void ABinderProcess_startThreadPool();
 /**
  * This sets the maximum number of threads that can be started in the threadpool. By default, after
- * startThreadPool is called, this is one. If it is called additional times, it will only prevent
+ * startThreadPool is called, this is 15. If it is called additional times, it will only prevent
  * the kernel from starting new threads and will not delete already existing threads.
  */
 bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads);
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index e51bceb..9de3460 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -66,7 +66,6 @@
     BINDER_LIB_TEST_LINK_DEATH_TRANSACTION,
     BINDER_LIB_TEST_WRITE_FILE_TRANSACTION,
     BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION,
-    BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION,
     BINDER_LIB_TEST_EXIT_TRANSACTION,
     BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION,
     BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION,
@@ -770,22 +769,6 @@
     EXPECT_TRUE(strong_from_weak == nullptr);
 }
 
-TEST_F(BinderLibTest, PromoteRemote) {
-    int ret;
-    Parcel data, reply;
-    sp<IBinder> strong = new BBinder();
-    sp<IBinder> server = addServer();
-
-    ASSERT_TRUE(server != nullptr);
-    ASSERT_TRUE(strong != nullptr);
-
-    ret = data.writeWeakBinder(strong);
-    EXPECT_EQ(NO_ERROR, ret);
-
-    ret = server->transact(BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION, data, &reply);
-    EXPECT_GE(ret, 0);
-}
-
 TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) {
     status_t ret;
     Parcel data, reply;
@@ -811,7 +794,6 @@
     wp<IBinder> keepFreedBinder;
     {
         Parcel data, reply;
-        data.writeBool(false); /* request weak reference */
         ret = server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply);
         ASSERT_EQ(NO_ERROR, ret);
         struct flat_binder_object *freed = (struct flat_binder_object *)(reply.data());
@@ -820,8 +802,9 @@
          * delete its reference to it - otherwise the transaction
          * fails regardless of whether the driver is fixed.
          */
-        keepFreedBinder = reply.readWeakBinder();
+        keepFreedBinder = reply.readStrongBinder();
     }
+    IPCThreadState::self()->flushCommands();
     {
         Parcel data, reply;
         data.writeStrongBinder(server);
@@ -1280,29 +1263,6 @@
                 if (ret != size) return UNKNOWN_ERROR;
                 return NO_ERROR;
             }
-            case BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION: {
-                int ret;
-                wp<IBinder> weak;
-                sp<IBinder> strong;
-                Parcel data2, reply2;
-                sp<IServiceManager> sm = defaultServiceManager();
-                sp<IBinder> server = sm->getService(binderLibTestServiceName);
-
-                weak = data.readWeakBinder();
-                if (weak == nullptr) {
-                    return BAD_VALUE;
-                }
-                strong = weak.promote();
-
-                ret = server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data2, &reply2);
-                if (ret != NO_ERROR)
-                    exit(EXIT_FAILURE);
-
-                if (strong == nullptr) {
-                    reply->setError(1);
-                }
-                return NO_ERROR;
-            }
             case BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION:
                 alarm(10);
                 return NO_ERROR;
@@ -1311,13 +1271,8 @@
                     ;
                 exit(EXIT_SUCCESS);
             case BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION: {
-                bool strongRef = data.readBool();
                 sp<IBinder> binder = new BBinder();
-                if (strongRef) {
-                    reply->writeStrongBinder(binder);
-                } else {
-                    reply->writeWeakBinder(binder);
-                }
+                reply->writeStrongBinder(binder);
                 return NO_ERROR;
             }
             case BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION: {
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 8199c98..59f1bcd 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -46,7 +46,6 @@
 #include <utils/String8.h>
 #include <utils/Trace.h>
 
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 #define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content"
 #define EGL_PROTECTED_CONTENT_EXT 0x32C0
 
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index bf7991a..be58b85 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -169,12 +169,10 @@
 }
 
 status_t ComposerState::write(Parcel& output) const {
-    output.writeStrongBinder(IInterface::asBinder(client));
     return state.write(output);
 }
 
 status_t ComposerState::read(const Parcel& input) {
-    client = interface_cast<ISurfaceComposerClient>(input.readStrongBinder());
     return state.read(input);
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 16a4b35..e6b1beb 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -202,7 +202,8 @@
      * callbackIds to generate one super map that contains all the sp<IBinder> to sp<SurfaceControl>
      * that could possibly exist for the callbacks.
      */
-    std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls;
+    std::unordered_map<sp<IBinder>, sp<SurfaceControl>, SurfaceComposerClient::IBinderHash>
+            surfaceControls;
     for (const auto& transactionStats : listenerStats.transactionStats) {
         for (auto callbackId : transactionStats.callbackIds) {
             auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
@@ -365,16 +366,16 @@
     if (count > parcel->dataSize()) {
         return BAD_VALUE;
     }
-    std::unordered_map<sp<SurfaceControl>, ComposerState, SCHash> composerStates;
+    std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> composerStates;
     composerStates.reserve(count);
     for (size_t i = 0; i < count; i++) {
-        sp<SurfaceControl> surfaceControl = SurfaceControl::readFromParcel(parcel);
+        sp<IBinder> surfaceControlHandle = parcel->readStrongBinder();
 
         ComposerState composerState;
         if (composerState.read(*parcel) == BAD_VALUE) {
             return BAD_VALUE;
         }
-        composerStates[surfaceControl] = composerState;
+        composerStates[surfaceControlHandle] = composerState;
     }
 
     InputWindowCommands inputWindowCommands;
@@ -408,8 +409,8 @@
     }
 
     parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size()));
-    for (auto const& [surfaceControl, composerState] : mComposerStates) {
-        surfaceControl->writeToParcel(parcel);
+    for (auto const& [surfaceHandle, composerState] : mComposerStates) {
+        parcel->writeStrongBinder(surfaceHandle);
         composerState.write(*parcel);
     }
 
@@ -418,11 +419,11 @@
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
-    for (auto const& kv : other.mComposerStates) {
-        if (mComposerStates.count(kv.first) == 0) {
-            mComposerStates[kv.first] = kv.second;
+    for (auto const& [surfaceHandle, composerState] : other.mComposerStates) {
+        if (mComposerStates.count(surfaceHandle) == 0) {
+            mComposerStates[surfaceHandle] = composerState;
         } else {
-            mComposerStates[kv.first].state.merge(kv.second.state);
+            mComposerStates[surfaceHandle].state.merge(composerState.state);
         }
     }
 
@@ -466,14 +467,12 @@
     mDesiredPresentTime = -1;
 }
 
-void SurfaceComposerClient::doDropReferenceTransaction(const sp<IBinder>& handle,
-        const sp<ISurfaceComposerClient>& client) {
+void SurfaceComposerClient::doDropReferenceTransaction(const sp<IBinder>& handle) {
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
     Vector<ComposerState> composerStates;
     Vector<DisplayState> displayStates;
 
     ComposerState s;
-    s.client = client;
     s.state.surface = handle;
     s.state.what |= layer_state_t::eReparent;
     s.state.parentHandleForChild = nullptr;
@@ -500,8 +499,8 @@
     }
 
     size_t count = 0;
-    for (auto& [sc, cs] : mComposerStates) {
-        layer_state_t* s = getLayerState(sc);
+    for (auto& [handle, cs] : mComposerStates) {
+        layer_state_t* s = getLayerState(handle);
         if (!(s->what & layer_state_t::eBufferChanged)) {
             continue;
         }
@@ -640,16 +639,15 @@
     mEarlyWakeup = true;
 }
 
-layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
-    if (mComposerStates.count(sc) == 0) {
+layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<IBinder>& handle) {
+    if (mComposerStates.count(handle) == 0) {
         // we don't have it, add an initialized layer_state to our list
         ComposerState s;
-        s.client = sc->getClient()->mClient;
-        s.state.surface = sc->getHandle();
-        mComposerStates[sc] = s;
+        s.state.surface = handle;
+        mComposerStates[handle] = s;
     }
 
-    return &(mComposerStates[sc].state);
+    return &(mComposerStates[handle].state);
 }
 
 void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback(
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index b9defdd..071314f 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -65,8 +65,8 @@
 {
     // Avoid reparenting the server-side surface to null if we are not the owner of it,
     // meaning that we retrieved it from another process.
-    if (mClient != nullptr && mHandle != nullptr && mOwned) {
-        SurfaceComposerClient::doDropReferenceTransaction(mHandle, mClient->getClient());
+    if (mHandle != nullptr && mOwned) {
+        SurfaceComposerClient::doDropReferenceTransaction(mHandle);
     }
     release();
 }
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index f438eb3..cbd1c85 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -206,7 +206,6 @@
 };
 
 struct ComposerState {
-    sp<ISurfaceComposerClient> client;
     layer_state_t state;
     status_t write(Parcel& output) const;
     status_t read(const Parcel& input);
@@ -274,8 +273,6 @@
 };
 
 static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) {
-    if (lhs.client < rhs.client) return -1;
-    if (lhs.client > rhs.client) return 1;
     if (lhs.state.surface < rhs.state.surface) return -1;
     if (lhs.state.surface > rhs.state.surface) return 1;
     return 0;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 4dda97f..22ab62d 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -163,8 +163,7 @@
      * Called from SurfaceControl d'tor to 'destroy' the surface (or rather, reparent it
      * to null), but without needing an sp<SurfaceControl> to avoid infinite ressurection.
      */
-    static void doDropReferenceTransaction(const sp<IBinder>& handle,
-            const sp<ISurfaceComposerClient>& client);
+    static void doDropReferenceTransaction(const sp<IBinder>& handle);
 
     /**
      * Uncaches a buffer in ISurfaceComposer. It must be uncached via a transaction so that it is
@@ -270,6 +269,12 @@
         }
     };
 
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& iBinder) const {
+            return std::hash<IBinder*>{}(iBinder.get());
+        }
+    };
+
     struct TCLHash {
         std::size_t operator()(const sp<ITransactionCompletedListener>& tcl) const {
             return std::hash<IBinder*>{}((tcl) ? IInterface::asBinder(tcl).get() : nullptr);
@@ -286,7 +291,7 @@
     };
 
     class Transaction : Parcelable {
-        std::unordered_map<sp<SurfaceControl>, ComposerState, SCHash> mComposerStates;
+        std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
         SortedVector<DisplayState > mDisplayStates;
         std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
                 mListenerCallbacks;
@@ -314,7 +319,10 @@
         InputWindowCommands mInputWindowCommands;
         int mStatus = NO_ERROR;
 
-        layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
+        layer_state_t* getLayerState(const sp<IBinder>& surfaceHandle);
+        layer_state_t* getLayerState(const sp<SurfaceControl>& sc) {
+            return getLayerState(sc->getHandle());
+        }
         DisplayState& getDisplayState(const sp<IBinder>& token);
 
         void cacheBuffers();
@@ -560,15 +568,10 @@
 
     CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1;
 
-    struct IBinderHash {
-        std::size_t operator()(const sp<IBinder>& iBinder) const {
-            return std::hash<IBinder*>{}(iBinder.get());
-        }
-    };
-
     struct CallbackTranslation {
         TransactionCompletedCallback callbackFunction;
-        std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls;
+        std::unordered_map<sp<IBinder>, sp<SurfaceControl>, SurfaceComposerClient::IBinderHash>
+                surfaceControls;
     };
 
     std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex);
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 65e09f2..c85e844 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -28,8 +28,6 @@
 #include <utils/Log.h>
 #include <utils/Thread.h>
 
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-
 namespace android {
 
 class SurfaceTextureClientTest : public ::testing::Test {
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index dc4978b..34b305e 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -238,8 +238,8 @@
                              int32_t actionButton, int32_t flags, int32_t edgeFlags,
                              int32_t metaState, int32_t buttonState,
                              MotionClassification classification, float xOffset, float yOffset,
-                             float xPrecision, float yPrecision, float xCursorPosition,
-                             float yCursorPosition, nsecs_t downTime, nsecs_t eventTime,
+                             float xPrecision, float yPrecision, float rawXCursorPosition,
+                             float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
                              size_t pointerCount, const PointerProperties* pointerProperties,
                              const PointerCoords* pointerCoords) {
     InputEvent::initialize(deviceId, source, displayId);
@@ -254,8 +254,8 @@
     mYOffset = yOffset;
     mXPrecision = xPrecision;
     mYPrecision = yPrecision;
-    mXCursorPosition = xCursorPosition;
-    mYCursorPosition = yCursorPosition;
+    mRawXCursorPosition = rawXCursorPosition;
+    mRawYCursorPosition = rawYCursorPosition;
     mDownTime = downTime;
     mPointerProperties.clear();
     mPointerProperties.appendArray(pointerProperties, pointerCount);
@@ -277,8 +277,8 @@
     mYOffset = other->mYOffset;
     mXPrecision = other->mXPrecision;
     mYPrecision = other->mYPrecision;
-    mXCursorPosition = other->mXCursorPosition;
-    mYCursorPosition = other->mYCursorPosition;
+    mRawXCursorPosition = other->mRawXCursorPosition;
+    mRawYCursorPosition = other->mRawYCursorPosition;
     mDownTime = other->mDownTime;
     mPointerProperties = other->mPointerProperties;
 
@@ -313,6 +313,11 @@
     return rawY + mYOffset;
 }
 
+void MotionEvent::setCursorPosition(float x, float y) {
+    mRawXCursorPosition = x - mXOffset;
+    mRawYCursorPosition = y - mYOffset;
+}
+
 const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
     return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex];
 }
@@ -433,12 +438,12 @@
     transformPoint(matrix, 0, 0, &originX, &originY);
 
     // Apply the transformation to cursor position.
-    if (isValidCursorPosition(mXCursorPosition, mYCursorPosition)) {
-        float x = mXCursorPosition + oldXOffset;
-        float y = mYCursorPosition + oldYOffset;
+    if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
+        float x = mRawXCursorPosition + oldXOffset;
+        float y = mRawYCursorPosition + oldYOffset;
         transformPoint(matrix, x, y, &x, &y);
-        mXCursorPosition = x - mXOffset;
-        mYCursorPosition = y - mYOffset;
+        mRawXCursorPosition = x - mXOffset;
+        mRawYCursorPosition = y - mYOffset;
     }
 
     // Apply the transformation to all samples.
@@ -480,8 +485,8 @@
     mYOffset = parcel->readFloat();
     mXPrecision = parcel->readFloat();
     mYPrecision = parcel->readFloat();
-    mXCursorPosition = parcel->readFloat();
-    mYCursorPosition = parcel->readFloat();
+    mRawXCursorPosition = parcel->readFloat();
+    mRawYCursorPosition = parcel->readFloat();
     mDownTime = parcel->readInt64();
 
     mPointerProperties.clear();
@@ -533,8 +538,8 @@
     parcel->writeFloat(mYOffset);
     parcel->writeFloat(mXPrecision);
     parcel->writeFloat(mYPrecision);
-    parcel->writeFloat(mXCursorPosition);
-    parcel->writeFloat(mYCursorPosition);
+    parcel->writeFloat(mRawXCursorPosition);
+    parcel->writeFloat(mRawYCursorPosition);
     parcel->writeInt64(mDownTime);
 
     for (size_t i = 0; i < pointerCount; i++) {
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 5a60347..ec28757 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -99,7 +99,7 @@
     applicationInfo.write(output);
     output.write(touchableRegion);
     output.writeBool(replaceTouchableRegionWithCrop);
-    output.writeWeakBinder(touchableRegionCropHandle);
+    output.writeStrongBinder(touchableRegionCropHandle.promote());
     return OK;
 }
 
@@ -142,7 +142,7 @@
     ret.applicationInfo = InputApplicationInfo::read(from);
     from.read(ret.touchableRegion);
     ret.replaceTouchableRegionWithCrop = from.readBool();
-    ret.touchableRegionCropHandle = from.readWeakBinder();
+    ret.touchableRegionCropHandle = from.readStrongBinder();
 
     return ret;
 }
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index b879de6..b90857c 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -665,4 +665,14 @@
     ASSERT_EQ(600, event.getYCursorPosition());
 }
 
+TEST_F(MotionEventTest, SetCursorPosition) {
+    MotionEvent event;
+    initializeEventWithHistory(&event);
+    event.setSource(AINPUT_SOURCE_MOUSE);
+
+    event.setCursorPosition(3, 4);
+    ASSERT_EQ(3, event.getXCursorPosition());
+    ASSERT_EQ(4, event.getYCursorPosition());
+}
+
 } // namespace android
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 5200545..8ed09f8 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -199,6 +199,10 @@
             int32_t type = data.readInt32();
             int32_t format = data.readInt32();
             native_handle_t *resource = data.readNativeHandle();
+            // Avoid a crash in native_handle_close if resource is nullptr
+            if (resource == nullptr) {
+                return BAD_VALUE;
+            }
             sp<ISensorEventConnection> ch =
                     createSensorDirectConnection(opPackageName, size, type, format, resource);
             native_handle_close(resource);
diff --git a/libs/vr/libdisplay/include/private/dvr/display_protocol.h b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
index 3786d1d..861dc6c 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_protocol.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
@@ -191,7 +191,8 @@
 enum class ConfigFileType : uint32_t {
   kLensMetrics,
   kDeviceMetrics,
-  kDeviceConfiguration
+  kDeviceConfiguration,
+  kDeviceEdid
 };
 
 struct DisplayProtocol {
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
index e383bb2..b7abb99 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api.h
@@ -85,6 +85,8 @@
   DVR_CONFIGURATION_DATA_DEVICE_METRICS = 1,
   // Request the per device configuration data file.
   DVR_CONFIGURATION_DATA_DEVICE_CONFIG = 2,
+  // Request the edid data for the display.
+  DVR_CONFIGURATION_DATA_DEVICE_EDID = 3,
 };
 
 // dvr_display_manager.h
diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp
index 87162c0..8980a92 100644
--- a/libs/vr/libvrflinger/display_service.cpp
+++ b/libs/vr/libvrflinger/display_service.cpp
@@ -44,6 +44,18 @@
            Endpoint::Create(display::DisplayProtocol::kClientPath)) {
     hardware_composer_.Initialize(
         hidl, primary_display_id, request_display_callback);
+
+    uint8_t port;
+    const auto error = hidl->getDisplayIdentificationData(
+        primary_display_id, &port, &display_identification_data_);
+    if (error != android::hardware::graphics::composer::V2_1::Error::NONE) {
+      if (error !=
+          android::hardware::graphics::composer::V2_1::Error::UNSUPPORTED) {
+        ALOGI("DisplayService: identification data error\n");
+      } else {
+        ALOGI("DisplayService: identification data unsupported\n");
+      }
+    }
 }
 
 bool DisplayService::IsInitialized() const {
@@ -204,6 +216,12 @@
     case display::ConfigFileType::kDeviceConfiguration:
       property_name = kDvrDeviceConfigProperty;
       break;
+    case display::ConfigFileType::kDeviceEdid:
+      if (display_identification_data_.size() == 0) {
+        return ErrorStatus(ENOENT);
+      }
+      return std::string(display_identification_data_.begin(),
+                         display_identification_data_.end());
     default:
       return ErrorStatus(EINVAL);
   }
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
index e0f2edd..d45a61f 100644
--- a/libs/vr/libvrflinger/display_service.h
+++ b/libs/vr/libvrflinger/display_service.h
@@ -18,6 +18,8 @@
 #include "epoll_event_dispatcher.h"
 #include "hardware_composer.h"
 
+#include "DisplayHardware/DisplayIdentification.h"
+
 namespace android {
 namespace dvr {
 
@@ -117,6 +119,8 @@
 
   DisplayService(const DisplayService&) = delete;
   void operator=(const DisplayService&) = delete;
+
+  DisplayIdentificationData display_identification_data_;
 };
 
 }  // namespace dvr
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index fb28d1b..019815c 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -715,7 +715,7 @@
         CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();
 
         Command command = commandEntry->command;
-        (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'
+        command(*this, commandEntry); // commands are implicitly 'LockedInterruptible'
 
         commandEntry->connection.clear();
         delete commandEntry;
@@ -809,8 +809,8 @@
     resetKeyRepeatLocked();
 
     // Enqueue a command to run outside the lock to tell the policy that the configuration changed.
-    CommandEntry* commandEntry = postCommandLocked(
-            & InputDispatcher::doNotifyConfigurationChangedLockedInterruptible);
+    CommandEntry* commandEntry =
+            postCommandLocked(&InputDispatcher::doNotifyConfigurationChangedLockedInterruptible);
     commandEntry->eventTime = entry->eventTime;
     return true;
 }
@@ -884,7 +884,7 @@
     if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
         if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
             CommandEntry* commandEntry = postCommandLocked(
-                    & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
+                    &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
             sp<InputWindowHandle> focusedWindowHandle =
                     getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry));
             if (focusedWindowHandle != nullptr) {
@@ -1982,8 +1982,8 @@
     }
     }
 
-    CommandEntry* commandEntry = postCommandLocked(
-            & InputDispatcher::doPokeUserActivityLockedInterruptible);
+    CommandEntry* commandEntry =
+            postCommandLocked(&InputDispatcher::doPokeUserActivityLockedInterruptible);
     commandEntry->eventTime = eventEntry->eventTime;
     commandEntry->userActivityEventType = eventType;
 }
@@ -2200,8 +2200,8 @@
         return;
     }
 
-    CommandEntry* commandEntry = postCommandLocked(
-            & InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
+    CommandEntry* commandEntry =
+            postCommandLocked(&InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
     commandEntry->newToken = newToken;
 }
 
@@ -4087,8 +4087,8 @@
 
 void InputDispatcher::onDispatchCycleFinishedLocked(
         nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) {
-    CommandEntry* commandEntry = postCommandLocked(
-            & InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
+    CommandEntry* commandEntry =
+            postCommandLocked(&InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
     commandEntry->connection = connection;
     commandEntry->eventTime = currentTime;
     commandEntry->seq = seq;
@@ -4100,8 +4100,8 @@
     ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
             connection->getInputChannelName().c_str());
 
-    CommandEntry* commandEntry = postCommandLocked(
-            & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
+    CommandEntry* commandEntry =
+            postCommandLocked(&InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
     commandEntry->connection = connection;
 }
 
@@ -4109,8 +4109,8 @@
         const sp<InputWindowHandle>& newFocus) {
     sp<IBinder> oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr;
     sp<IBinder> newToken = newFocus != nullptr ? newFocus->getToken() : nullptr;
-    CommandEntry* commandEntry = postCommandLocked(
-            & InputDispatcher::doNotifyFocusChangedLockedInterruptible);
+    CommandEntry* commandEntry =
+            postCommandLocked(&InputDispatcher::doNotifyFocusChangedLockedInterruptible);
     commandEntry->oldToken = oldToken;
     commandEntry->newToken = newToken;
 }
@@ -4142,8 +4142,8 @@
     mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason);
     dumpDispatchStateLocked(mLastANRState);
 
-    CommandEntry* commandEntry = postCommandLocked(
-            & InputDispatcher::doNotifyANRLockedInterruptible);
+    CommandEntry* commandEntry =
+            postCommandLocked(&InputDispatcher::doNotifyANRLockedInterruptible);
     commandEntry->inputApplicationHandle = applicationHandle;
     commandEntry->inputChannel = windowHandle != nullptr ?
             getInputChannelLocked(windowHandle->getToken()) : nullptr;
@@ -4237,53 +4237,55 @@
 void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
         CommandEntry* commandEntry) {
     sp<Connection> connection = commandEntry->connection;
-    nsecs_t finishTime = commandEntry->eventTime;
+    const nsecs_t finishTime = commandEntry->eventTime;
     uint32_t seq = commandEntry->seq;
-    bool handled = commandEntry->handled;
+    const bool handled = commandEntry->handled;
 
     // Handle post-event policy actions.
     DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);
-    if (dispatchEntry) {
-        nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
-        if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
-            std::string msg =
-                    StringPrintf("Window '%s' spent %0.1fms processing the last input event: ",
-                    connection->getWindowName().c_str(), eventDuration * 0.000001f);
-            dispatchEntry->eventEntry->appendDescription(msg);
-            ALOGI("%s", msg.c_str());
-        }
-
-        bool restartEvent;
-        if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
-            KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
-            restartEvent = afterKeyEventLockedInterruptible(connection,
-                    dispatchEntry, keyEntry, handled);
-        } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {
-            MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
-            restartEvent = afterMotionEventLockedInterruptible(connection,
-                    dispatchEntry, motionEntry, handled);
-        } else {
-            restartEvent = false;
-        }
-
-        // Dequeue the event and start the next cycle.
-        // Note that because the lock might have been released, it is possible that the
-        // contents of the wait queue to have been drained, so we need to double-check
-        // a few things.
-        if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
-            connection->waitQueue.dequeue(dispatchEntry);
-            traceWaitQueueLength(connection);
-            if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
-                connection->outboundQueue.enqueueAtHead(dispatchEntry);
-                traceOutboundQueueLength(connection);
-            } else {
-                releaseDispatchEntry(dispatchEntry);
-            }
-        }
-
-        // Start the next dispatch cycle for this connection.
-        startDispatchCycleLocked(now(), connection);
+    if (!dispatchEntry) {
+        return;
     }
+
+    nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
+    if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
+        std::string msg =
+                StringPrintf("Window '%s' spent %0.1fms processing the last input event: ",
+                             connection->getWindowName().c_str(), eventDuration * 0.000001f);
+        dispatchEntry->eventEntry->appendDescription(msg);
+        ALOGI("%s", msg.c_str());
+    }
+
+    bool restartEvent;
+    if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
+        KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
+        restartEvent =
+                afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
+    } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {
+        MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
+        restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,
+                                                           handled);
+    } else {
+        restartEvent = false;
+    }
+
+    // Dequeue the event and start the next cycle.
+    // Note that because the lock might have been released, it is possible that the
+    // contents of the wait queue to have been drained, so we need to double-check
+    // a few things.
+    if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
+        connection->waitQueue.dequeue(dispatchEntry);
+        traceWaitQueueLength(connection);
+        if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
+            connection->outboundQueue.enqueueAtHead(dispatchEntry);
+            traceOutboundQueueLength(connection);
+        } else {
+            releaseDispatchEntry(dispatchEntry);
+        }
+    }
+
+    // Start the next dispatch cycle for this connection.
+    startDispatchCycleLocked(now(), connection);
 }
 
 bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index c30a8d6..147437c 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -644,7 +644,7 @@
     //
     // Commands are implicitly 'LockedInterruptible'.
     struct CommandEntry;
-    typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry);
+    typedef std::function<void(InputDispatcher&, CommandEntry*)> Command;
 
     class Connection;
     struct CommandEntry : Link<CommandEntry> {
diff --git a/services/nativeperms/.clang-format b/services/nativeperms/.clang-format
deleted file mode 100644
index 6006e6f..0000000
--- a/services/nativeperms/.clang-format
+++ /dev/null
@@ -1,13 +0,0 @@
-BasedOnStyle: Google
-AllowShortFunctionsOnASingleLine: Inline
-AllowShortIfStatementsOnASingleLine: true
-AllowShortLoopsOnASingleLine: true
-BinPackArguments: true
-BinPackParameters: true
-ColumnLimit: 80
-CommentPragmas: NOLINT:.*
-ContinuationIndentWidth: 8
-DerivePointerAlignment: false
-IndentWidth: 4
-PointerAlignment: Left
-TabWidth: 4
diff --git a/services/nativeperms/Android.bp b/services/nativeperms/Android.bp
deleted file mode 100644
index cbc7d66..0000000
--- a/services/nativeperms/Android.bp
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2016 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_binary {
-    name: "nativeperms",
-    srcs: [
-        "nativeperms.cpp",
-        "android/os/IPermissionController.aidl",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-    shared_libs: [
-        "libbinder",
-        "libbrillo",
-        "libbrillo-binder",
-        "libchrome",
-        "libutils",
-    ],
-    init_rc: ["nativeperms.rc"],
-}
diff --git a/services/nativeperms/android/os/IPermissionController.aidl b/services/nativeperms/android/os/IPermissionController.aidl
deleted file mode 100644
index 89db85c..0000000
--- a/services/nativeperms/android/os/IPermissionController.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-/* //device/java/android/android/os/IPowerManager.aidl
-**
-** Copyright 2007, 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.os;
-
-/** @hide */
-interface IPermissionController {
-    boolean checkPermission(String permission, int pid, int uid);
-    String[] getPackagesForUid(int uid);
-    boolean isRuntimePermission(String permission);
-}
diff --git a/services/nativeperms/android/os/README b/services/nativeperms/android/os/README
deleted file mode 100644
index e414499..0000000
--- a/services/nativeperms/android/os/README
+++ /dev/null
@@ -1,4 +0,0 @@
-IPermissionController.aidl in this directory is a verbatim copy of
-https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/IPermissionController.aidl,
-because some Brillo manifests do not currently include the frameworks/base repo.
-TODO(jorgelo): Figure out a way to use the .aidl file in frameworks/base.
diff --git a/services/nativeperms/nativeperms.cpp b/services/nativeperms/nativeperms.cpp
deleted file mode 100644
index 7f03bed..0000000
--- a/services/nativeperms/nativeperms.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2016 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 <base/at_exit.h>
-#include <base/logging.h>
-#include <base/message_loop/message_loop.h>
-#include <binder/IServiceManager.h>
-#include <binder/Status.h>
-#include <brillo/binder_watcher.h>
-#include <brillo/message_loops/base_message_loop.h>
-#include <brillo/syslog_logging.h>
-#include <utils/String16.h>
-
-#include "android/os/BnPermissionController.h"
-
-namespace {
-static android::String16 serviceName("permission");
-}
-
-namespace android {
-
-class PermissionService : public android::os::BnPermissionController {
-   public:
-    ::android::binder::Status checkPermission(
-            const ::android::String16& permission, int32_t pid, int32_t uid,
-            bool* _aidl_return) {
-        (void)permission;
-        (void)pid;
-        (void)uid;
-        *_aidl_return = true;
-        return binder::Status::ok();
-    }
-
-    ::android::binder::Status getPackagesForUid(
-            int32_t uid, ::std::vector<::android::String16>* _aidl_return) {
-        (void)uid;
-        // Brillo doesn't currently have installable packages.
-        if (_aidl_return) {
-            _aidl_return->clear();
-        }
-        return binder::Status::ok();
-    }
-
-    ::android::binder::Status isRuntimePermission(
-            const ::android::String16& permission, bool* _aidl_return) {
-        (void)permission;
-        // Brillo doesn't currently have runtime permissions.
-        *_aidl_return = false;
-        return binder::Status::ok();
-    }
-};
-
-}  // namespace android
-
-int main() {
-    base::AtExitManager atExitManager;
-    brillo::InitLog(brillo::kLogToSyslog);
-    // Register the service with servicemanager.
-    android::status_t status = android::defaultServiceManager()->addService(
-            serviceName, new android::PermissionService());
-    CHECK(status == android::OK) << "Failed to get IPermissionController "
-                                    "binder from servicemanager.";
-
-    // Create a message loop.
-    base::MessageLoopForIO messageLoopForIo;
-    brillo::BaseMessageLoop messageLoop{&messageLoopForIo};
-
-    // Initialize a binder watcher.
-    brillo::BinderWatcher watcher(&messageLoop);
-    watcher.Init();
-
-    // Run the message loop.
-    messageLoop.Run();
-}
diff --git a/services/nativeperms/nativeperms.rc b/services/nativeperms/nativeperms.rc
deleted file mode 100644
index 704c0a2..0000000
--- a/services/nativeperms/nativeperms.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-service nativeperms /system/bin/nativeperms
-    class main
-    user system
-    group system
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index f51fbb4..59ea9af 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -25,6 +25,7 @@
 #include <compositionengine/Display.h>
 #include <compositionengine/Layer.h>
 #include <compositionengine/LayerCreationArgs.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
@@ -253,90 +254,20 @@
             mActiveBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102);
 }
 
-void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice,
-                                  const ui::Transform& transform, const Rect& viewport,
-                                  int32_t supportedPerFrameMetadata,
-                                  const ui::Dataspace targetDataspace) {
-    RETURN_IF_NO_HWC_LAYER(displayDevice);
-
-    // Apply this display's projection's viewport to the visible region
-    // before giving it to the HWC HAL.
-    Region visible = transform.transform(visibleRegion.intersect(viewport));
-
-    const auto outputLayer = findOutputLayerForDisplay(displayDevice);
-    LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc);
-
-    auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
-    auto error = hwcLayer->setVisibleRegion(visible);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(),
-              to_string(error).c_str(), static_cast<int32_t>(error));
-        visible.dump(LOG_TAG);
-    }
-    outputLayer->editState().visibleRegion = visible;
-
-    auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-
-    error = hwcLayer->setSurfaceDamage(surfaceDamageRegion);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(),
-              to_string(error).c_str(), static_cast<int32_t>(error));
-        surfaceDamageRegion.dump(LOG_TAG);
-    }
-    layerCompositionState.surfaceDamage = surfaceDamageRegion;
+void BufferLayer::latchPerFrameState(
+        compositionengine::LayerFECompositionState& compositionState) const {
+    Layer::latchPerFrameState(compositionState);
 
     // Sideband layers
-    if (layerCompositionState.sidebandStream.get()) {
-        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::SIDEBAND);
-        ALOGV("[%s] Requesting Sideband composition", mName.string());
-        error = hwcLayer->setSidebandStream(layerCompositionState.sidebandStream->handle());
-        if (error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", mName.string(),
-                  layerCompositionState.sidebandStream->handle(), to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-        layerCompositionState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
-        return;
-    }
-
-    // Device or Cursor layers
-    if (mPotentialCursor) {
-        ALOGV("[%s] Requesting Cursor composition", mName.string());
-        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::CURSOR);
+    if (compositionState.sidebandStream.get()) {
+        compositionState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
     } else {
-        ALOGV("[%s] Requesting Device composition", mName.string());
-        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::DEVICE);
+        // Normal buffer layers
+        compositionState.hdrMetadata = getDrawingHdrMetadata();
+        compositionState.compositionType = mPotentialCursor
+                ? Hwc2::IComposerClient::Composition::CURSOR
+                : Hwc2::IComposerClient::Composition::DEVICE;
     }
-
-    ui::Dataspace dataspace = isColorSpaceAgnostic() && targetDataspace != ui::Dataspace::UNKNOWN
-            ? targetDataspace
-            : mCurrentDataSpace;
-    error = hwcLayer->setDataspace(dataspace);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace,
-              to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-
-    const HdrMetadata& metadata = getDrawingHdrMetadata();
-    error = hwcLayer->setPerFrameMetadata(supportedPerFrameMetadata, metadata);
-    if (error != HWC2::Error::None && error != HWC2::Error::Unsupported) {
-        ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", mName.string(),
-              to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-
-    error = hwcLayer->setColorTransform(getColorTransform());
-    if (error == HWC2::Error::Unsupported) {
-        // If per layer color transform is not supported, we use GPU composition.
-        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::CLIENT);
-    } else if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(),
-                to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-    layerCompositionState.dataspace = mCurrentDataSpace;
-    layerCompositionState.colorTransform = getColorTransform();
-    layerCompositionState.hdrMetadata = metadata;
-
-    setHwcLayerBuffer(displayDevice);
 }
 
 bool BufferLayer::onPreComposition(nsecs_t refreshStartTime) {
@@ -395,7 +326,8 @@
     return true;
 }
 
-bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
+bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                              nsecs_t expectedPresentTime) {
     ATRACE_CALL();
 
     bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
@@ -430,12 +362,12 @@
     const bool oldOpacity = isOpaque(s);
     sp<GraphicBuffer> oldBuffer = mActiveBuffer;
 
-    if (!allTransactionsSignaled()) {
+    if (!allTransactionsSignaled(expectedPresentTime)) {
         mFlinger->setTransactionFlags(eTraversalNeeded);
         return false;
     }
 
-    status_t err = updateTexImage(recomputeVisibleRegions, latchTime);
+    status_t err = updateTexImage(recomputeVisibleRegions, latchTime, expectedPresentTime);
     if (err != NO_ERROR) {
         return false;
     }
@@ -540,10 +472,10 @@
 }
 
 // transaction
-void BufferLayer::notifyAvailableFrames() {
-    const auto headFrameNumber = getHeadFrameNumber();
+void BufferLayer::notifyAvailableFrames(nsecs_t expectedPresentTime) {
+    const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime);
     const bool headFenceSignaled = fenceHasSignaled();
-    const bool presentTimeIsCurrent = framePresentTimeIsCurrent();
+    const bool presentTimeIsCurrent = framePresentTimeIsCurrent(expectedPresentTime);
     Mutex::Autolock lock(mLocalSyncPointMutex);
     for (auto& point : mLocalSyncPoints) {
         if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled &&
@@ -591,8 +523,8 @@
 }
 
 // h/w composer set-up
-bool BufferLayer::allTransactionsSignaled() {
-    auto headFrameNumber = getHeadFrameNumber();
+bool BufferLayer::allTransactionsSignaled(nsecs_t expectedPresentTime) {
+    const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime);
     bool matchingFramesFound = false;
     bool allTransactionsApplied = true;
     Mutex::Autolock lock(mLocalSyncPointMutex);
@@ -658,9 +590,9 @@
             sourceCrop.getWidth() != displayFrame.getWidth();
 }
 
-uint64_t BufferLayer::getHeadFrameNumber() const {
+uint64_t BufferLayer::getHeadFrameNumber(nsecs_t expectedPresentTime) const {
     if (hasFrameUpdate()) {
-        return getFrameNumber();
+        return getFrameNumber(expectedPresentTime);
     } else {
         return mCurrentFrameNumber;
     }
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index b679380..bb0205d 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -82,10 +82,6 @@
 
     bool isHdrY410() const override;
 
-    void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
-                         const Rect& viewport, int32_t supportedPerFrameMetadata,
-                         const ui::Dataspace targetDataspace) override;
-
     bool onPreComposition(nsecs_t refreshStartTime) override;
     bool onPostComposition(const std::optional<DisplayId>& displayId,
                            const std::shared_ptr<FenceTime>& glDoneFence,
@@ -96,11 +92,12 @@
     // the visible regions need to be recomputed (this is a fairly heavy
     // operation, so this should be set only if needed). Typically this is used
     // to figure out if the content or size of a surface has changed.
-    bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
+    bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                     nsecs_t expectedPresentTime) override;
 
     bool isBufferLatched() const override { return mRefreshPending; }
 
-    void notifyAvailableFrames() override;
+    void notifyAvailableFrames(nsecs_t expectedPresentTime) override;
 
     bool hasReadyFrame() const override;
 
@@ -114,7 +111,7 @@
     // -----------------------------------------------------------------------
 private:
     virtual bool fenceHasSignaled() const = 0;
-    virtual bool framePresentTimeIsCurrent() const = 0;
+    virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0;
 
     virtual nsecs_t getDesiredPresentTime() = 0;
     virtual std::shared_ptr<FenceTime> getCurrentFenceTime() const = 0;
@@ -129,7 +126,7 @@
     virtual int getDrawingApi() const = 0;
     virtual PixelFormat getPixelFormat() const = 0;
 
-    virtual uint64_t getFrameNumber() const = 0;
+    virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0;
 
     virtual bool getAutoRefresh() const = 0;
     virtual bool getSidebandStreamChanged() const = 0;
@@ -142,21 +139,22 @@
     virtual void setFilteringEnabled(bool enabled) = 0;
 
     virtual status_t bindTextureImage() = 0;
-    virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) = 0;
+    virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                                    nsecs_t expectedPresentTime) = 0;
 
     virtual status_t updateActiveBuffer() = 0;
     virtual status_t updateFrameNumber(nsecs_t latchTime) = 0;
 
-    virtual void setHwcLayerBuffer(const sp<const DisplayDevice>& displayDevice) = 0;
-
 protected:
+    void latchPerFrameState(compositionengine::LayerFECompositionState& outState) const override;
+
     // Loads the corresponding system property once per process
     static bool latchUnsignaledBuffers();
 
     // Check all of the local sync points to ensure that all transactions
     // which need to have been applied prior to the frame which is about to
     // be latched have signaled
-    bool allTransactionsSignaled();
+    bool allTransactionsSignaled(nsecs_t expectedPresentTime);
 
     static bool getOpacityForFormat(uint32_t format);
 
@@ -175,7 +173,7 @@
     // Returns true if this layer requires filtering
     bool needsFiltering(const sp<const DisplayDevice>& displayDevice) const;
 
-    uint64_t getHeadFrameNumber() const;
+    uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
 
     uint32_t mCurrentScalingMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
 
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index f35a4fd..12be00f 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -137,13 +137,13 @@
     return mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
 }
 
-bool BufferQueueLayer::framePresentTimeIsCurrent() const {
+bool BufferQueueLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
     if (!hasFrameUpdate() || isRemovedFromCurrentState()) {
         return true;
     }
 
     Mutex::Autolock lock(mQueueItemLock);
-    return mQueueItems[0].mTimestamp <= mFlinger->getExpectedPresentTime();
+    return mQueueItems[0].mTimestamp <= expectedPresentTime;
 }
 
 nsecs_t BufferQueueLayer::getDesiredPresentTime() {
@@ -196,13 +196,11 @@
     return mFormat;
 }
 
-uint64_t BufferQueueLayer::getFrameNumber() const {
+uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const {
     Mutex::Autolock lock(mQueueItemLock);
     uint64_t frameNumber = mQueueItems[0].mFrameNumber;
 
     // The head of the queue will be dropped if there are signaled and timely frames behind it
-    nsecs_t expectedPresentTime = mFlinger->getExpectedPresentTime();
-
     if (isRemovedFromCurrentState()) {
         expectedPresentTime = 0;
     }
@@ -268,7 +266,8 @@
     return mConsumer->bindTextureImage();
 }
 
-status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) {
+status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                                          nsecs_t expectedPresentTime) {
     // This boolean is used to make sure that SurfaceFlinger's shadow copy
     // of the buffer queue isn't modified when the buffer queue is returning
     // BufferItem's that weren't actually queued. This can happen in shared
@@ -279,8 +278,6 @@
                     getProducerStickyTransform() != 0, mName.string(), mOverrideScalingMode,
                     getTransformToDisplayInverse(), mFreezeGeometryUpdates);
 
-    nsecs_t expectedPresentTime = mFlinger->getExpectedPresentTime();
-
     if (isRemovedFromCurrentState()) {
         expectedPresentTime = 0;
     }
@@ -379,7 +376,6 @@
     mActiveBuffer = mConsumer->getCurrentBuffer(&mActiveBufferSlot, &mActiveBufferFence);
     auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
     layerCompositionState.buffer = mActiveBuffer;
-    layerCompositionState.bufferSlot = mActiveBufferSlot;
 
     if (mActiveBuffer == nullptr) {
         // this can only happen if the very first buffer was rejected.
@@ -399,32 +395,17 @@
     return NO_ERROR;
 }
 
-void BufferQueueLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    LOG_FATAL_IF(!outputLayer->getState.hwc);
-    auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
-
-    uint32_t hwcSlot = 0;
-    sp<GraphicBuffer> hwcBuffer;
-
-    // INVALID_BUFFER_SLOT is used to identify BufferStateLayers.  Default to 0
-    // for BufferQueueLayers
-    int slot = (mActiveBufferSlot == BufferQueue::INVALID_BUFFER_SLOT) ? 0 : mActiveBufferSlot;
-    (*outputLayer->editState().hwc)
-            .hwcBufferCache.getHwcBuffer(slot, mActiveBuffer, &hwcSlot, &hwcBuffer);
-
-    auto acquireFence = mConsumer->getCurrentFence();
-    auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(), mActiveBuffer->handle,
-              to_string(error).c_str(), static_cast<int32_t>(error));
+void BufferQueueLayer::latchPerFrameState(
+        compositionengine::LayerFECompositionState& compositionState) const {
+    BufferLayer::latchPerFrameState(compositionState);
+    if (compositionState.compositionType == Hwc2::IComposerClient::Composition::SIDEBAND) {
+        return;
     }
 
-    auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-    layerCompositionState.bufferSlot = mActiveBufferSlot;
-    layerCompositionState.buffer = mActiveBuffer;
-    layerCompositionState.acquireFence = acquireFence;
+    compositionState.buffer = mActiveBuffer;
+    compositionState.bufferSlot =
+            (mActiveBufferSlot == BufferQueue::INVALID_BUFFER_SLOT) ? 0 : mActiveBufferSlot;
+    compositionState.acquireFence = mConsumer->getCurrentFence();
 }
 
 // -----------------------------------------------------------------------
@@ -434,7 +415,7 @@
 void BufferQueueLayer::fakeVsync() {
     mRefreshPending = false;
     bool ignored = false;
-    latchBuffer(ignored, systemTime());
+    latchBuffer(ignored, systemTime(), 0 /* expectedPresentTime */);
     usleep(16000);
     releasePendingBuffer(systemTime());
 }
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 7def33a..231a531 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -61,7 +61,7 @@
     // -----------------------------------------------------------------------
 public:
     bool fenceHasSignaled() const override;
-    bool framePresentTimeIsCurrent() const override;
+    bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
 
 private:
     nsecs_t getDesiredPresentTime() override;
@@ -77,7 +77,7 @@
     int getDrawingApi() const override;
     PixelFormat getPixelFormat() const override;
 
-    uint64_t getFrameNumber() const override;
+    uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
 
     bool getAutoRefresh() const override;
     bool getSidebandStreamChanged() const override;
@@ -89,12 +89,13 @@
     void setFilteringEnabled(bool enabled) override;
 
     status_t bindTextureImage() override;
-    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
+    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                            nsecs_t expectedPresentTime) override;
 
     status_t updateActiveBuffer() override;
     status_t updateFrameNumber(nsecs_t latchTime) override;
 
-    void setHwcLayerBuffer(const sp<const DisplayDevice>& displayDevice) override;
+    void latchPerFrameState(compositionengine::LayerFECompositionState&) const override;
 
     // -----------------------------------------------------------------------
     // Interface implementation for BufferLayerConsumer::ContentsChangedListener
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 4b01301..0f26211 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -216,7 +216,7 @@
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
 
-    mFlinger->mTimeStats->setPostTime(getSequence(), getFrameNumber(), getName().c_str(), postTime);
+    mFlinger->mTimeStats->setPostTime(getSequence(), mFrameNumber, getName().c_str(), postTime);
     mDesiredPresentTime = desiredPresentTime;
 
     if (mFlinger->mUseSmart90ForVideo) {
@@ -369,12 +369,12 @@
     return getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
 }
 
-bool BufferStateLayer::framePresentTimeIsCurrent() const {
+bool BufferStateLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
     if (!hasFrameUpdate() || isRemovedFromCurrentState()) {
         return true;
     }
 
-    return mDesiredPresentTime <= mFlinger->getExpectedPresentTime();
+    return mDesiredPresentTime <= expectedPresentTime;
 }
 
 nsecs_t BufferStateLayer::getDesiredPresentTime() {
@@ -446,7 +446,7 @@
     return mActiveBuffer->format;
 }
 
-uint64_t BufferStateLayer::getFrameNumber() const {
+uint64_t BufferStateLayer::getFrameNumber(nsecs_t /*expectedPresentTime*/) const {
     return mFrameNumber;
 }
 
@@ -494,7 +494,8 @@
     return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence);
 }
 
-status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime) {
+status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
+                                          nsecs_t /*expectedPresentTime*/) {
     const State& s(getDrawingState());
 
     if (!s.buffer) {
@@ -528,7 +529,7 @@
         ALOGE("[%s] rejecting buffer: "
               "bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
               mName.string(), bufferWidth, bufferHeight, s.active.w, s.active.h);
-        mFlinger->mTimeStats->removeTimeRecord(layerID, getFrameNumber());
+        mFlinger->mTimeStats->removeTimeRecord(layerID, mFrameNumber);
         return BAD_VALUE;
     }
 
@@ -550,8 +551,8 @@
         }
     }
 
-    mFlinger->mTimeStats->setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime());
-    mFlinger->mTimeStats->setLatchTime(layerID, getFrameNumber(), latchTime);
+    mFlinger->mTimeStats->setAcquireFence(layerID, mFrameNumber, getCurrentFenceTime());
+    mFlinger->mTimeStats->setLatchTime(layerID, mFrameNumber, latchTime);
 
     mCurrentStateModified = false;
 
@@ -569,7 +570,6 @@
     mActiveBufferFence = s.acquireFence;
     auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
     layerCompositionState.buffer = mActiveBuffer;
-    layerCompositionState.bufferSlot = 0;
 
     return NO_ERROR;
 }
@@ -580,24 +580,18 @@
     return NO_ERROR;
 }
 
-void BufferStateLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc);
-    auto& hwcInfo = *outputLayer->editState().hwc;
-    auto& hwcLayer = hwcInfo.hwcLayer;
+void BufferStateLayer::latchPerFrameState(
+        compositionengine::LayerFECompositionState& compositionState) const {
+    BufferLayer::latchPerFrameState(compositionState);
+    if (compositionState.compositionType == Hwc2::IComposerClient::Composition::SIDEBAND) {
+        return;
+    }
 
     const State& s(getDrawingState());
 
-    uint32_t hwcSlot;
-    sp<GraphicBuffer> buffer;
-    hwcInfo.hwcBufferCache.getHwcBuffer(mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId),
-                                        s.buffer, &hwcSlot, &buffer);
-
-    auto error = hwcLayer->setBuffer(hwcSlot, buffer, s.acquireFence);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
-              s.buffer->handle, to_string(error).c_str(), static_cast<int32_t>(error));
-    }
+    compositionState.buffer = s.buffer;
+    compositionState.bufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId);
+    compositionState.acquireFence = s.acquireFence;
 
     mFrameNumber++;
 }
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index db8ae0d..5e5b9b0 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -100,7 +100,7 @@
     // Interface implementation for BufferLayer
     // -----------------------------------------------------------------------
     bool fenceHasSignaled() const override;
-    bool framePresentTimeIsCurrent() const override;
+    bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
 
     // Inherit from ClientCache::ErasedRecipient
     void bufferErased(const client_cache_t& clientCacheId) override;
@@ -119,7 +119,7 @@
     int getDrawingApi() const override;
     PixelFormat getPixelFormat() const override;
 
-    uint64_t getFrameNumber() const override;
+    uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
 
     bool getAutoRefresh() const override;
     bool getSidebandStreamChanged() const override;
@@ -131,12 +131,13 @@
     void setFilteringEnabled(bool enabled) override;
 
     status_t bindTextureImage() override;
-    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
+    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                            nsecs_t expectedPresentTime) override;
 
     status_t updateActiveBuffer() override;
     status_t updateFrameNumber(nsecs_t latchTime) override;
 
-    void setHwcLayerBuffer(const sp<const DisplayDevice>& display) override;
+    void latchPerFrameState(compositionengine::LayerFECompositionState&) const override;
 
 private:
     friend class SlotGenerationTest;
@@ -151,11 +152,11 @@
 
     std::atomic<bool> mSidebandStreamChanged{false};
 
-    uint32_t mFrameNumber{0};
+    mutable uint32_t mFrameNumber{0};
 
     sp<Fence> mPreviousReleaseFence;
 
-    bool mCurrentStateModified = false;
+    mutable bool mCurrentStateModified = false;
     bool mReleasePreviousBuffer = false;
     nsecs_t mCallbackHandleAcquireTime = -1;
 
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index fcc2d97..f15957a 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -18,6 +18,8 @@
 #undef LOG_TAG
 #define LOG_TAG "ColorLayer"
 
+#include "ColorLayer.h"
+
 #include <stdint.h>
 #include <stdlib.h>
 #include <sys/types.h>
@@ -26,6 +28,7 @@
 #include <compositionengine/Display.h>
 #include <compositionengine/Layer.h>
 #include <compositionengine/LayerCreationArgs.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
@@ -34,7 +37,6 @@
 #include <utils/Errors.h>
 #include <utils/Log.h>
 
-#include "ColorLayer.h"
 #include "DisplayDevice.h"
 #include "SurfaceFlinger.h"
 
@@ -91,75 +93,12 @@
     return true;
 }
 
-void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& display,
-                                 const ui::Transform& transform, const Rect& viewport,
-                                 int32_t /* supportedPerFrameMetadata */,
-                                 const ui::Dataspace targetDataspace) {
-    RETURN_IF_NO_HWC_LAYER(display);
+void ColorLayer::latchPerFrameState(
+        compositionengine::LayerFECompositionState& compositionState) const {
+    Layer::latchPerFrameState(compositionState);
 
-    Region visible = transform.transform(visibleRegion.intersect(viewport));
-
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc);
-
-    auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
-
-    auto error = hwcLayer->setVisibleRegion(visible);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(),
-              to_string(error).c_str(), static_cast<int32_t>(error));
-        visible.dump(LOG_TAG);
-    }
-    outputLayer->editState().visibleRegion = visible;
-
-    setCompositionType(display, Hwc2::IComposerClient::Composition::SOLID_COLOR);
-
-    const ui::Dataspace dataspace =
-            isColorSpaceAgnostic() && targetDataspace != ui::Dataspace::UNKNOWN ? targetDataspace
-                                                                                : mCurrentDataSpace;
-    error = hwcLayer->setDataspace(dataspace);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace,
-              to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-
-    auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-    layerCompositionState.dataspace = mCurrentDataSpace;
-
-    half4 color = getColor();
-    error = hwcLayer->setColor({static_cast<uint8_t>(std::round(255.0f * color.r)),
-                                static_cast<uint8_t>(std::round(255.0f * color.g)),
-                                static_cast<uint8_t>(std::round(255.0f * color.b)), 255});
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set color: %s (%d)", mName.string(), to_string(error).c_str(),
-              static_cast<int32_t>(error));
-    }
-    layerCompositionState.color = {static_cast<uint8_t>(std::round(255.0f * color.r)),
-                                   static_cast<uint8_t>(std::round(255.0f * color.g)),
-                                   static_cast<uint8_t>(std::round(255.0f * color.b)), 255};
-
-    // Clear out the transform, because it doesn't make sense absent a source buffer
-    error = hwcLayer->setTransform(HWC2::Transform::None);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to clear transform: %s (%d)", mName.string(), to_string(error).c_str(),
-              static_cast<int32_t>(error));
-    }
-    outputLayer->editState().bufferTransform = static_cast<Hwc2::Transform>(0);
-
-    error = hwcLayer->setColorTransform(getColorTransform());
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(),
-                to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-    layerCompositionState.colorTransform = getColorTransform();
-
-    error = hwcLayer->setSurfaceDamage(surfaceDamageRegion);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(),
-              to_string(error).c_str(), static_cast<int32_t>(error));
-        surfaceDamageRegion.dump(LOG_TAG);
-    }
-    layerCompositionState.surfaceDamage = surfaceDamageRegion;
+    compositionState.color = getColor();
+    compositionState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
 }
 
 void ColorLayer::commitTransaction(const State& stateToCommit) {
diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h
index 53d5b5b..2483ff0 100644
--- a/services/surfaceflinger/ColorLayer.h
+++ b/services/surfaceflinger/ColorLayer.h
@@ -37,10 +37,6 @@
 
     bool setDataspace(ui::Dataspace dataspace) override;
 
-    void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
-                         const Rect& viewport, int32_t supportedPerFrameMetadata,
-                         const ui::Dataspace targetDataspace) override;
-
     void commitTransaction(const State& stateToCommit) override;
 
     bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
@@ -52,6 +48,8 @@
                                     renderengine::LayerSettings& layer);
 
 private:
+    void latchPerFrameState(compositionengine::LayerFECompositionState&) const override;
+
     std::shared_ptr<compositionengine::Layer> mCompositionLayer;
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 896f8aa..31d6365 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -18,6 +18,8 @@
 
 #include <memory>
 
+#include <utils/Timers.h>
+
 namespace android {
 
 class HWComposer;
@@ -31,6 +33,7 @@
 class Display;
 class Layer;
 
+struct CompositionRefreshArgs;
 struct DisplayCreationArgs;
 struct LayerCreationArgs;
 
@@ -51,6 +54,12 @@
 
     virtual renderengine::RenderEngine& getRenderEngine() const = 0;
     virtual void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) = 0;
+
+    virtual bool needsAnotherUpdate() const = 0;
+    virtual nsecs_t getLastFrameRefreshTimestamp() const = 0;
+
+    // TODO(b/121291683): These will become private/internal
+    virtual void preComposition(CompositionRefreshArgs&) = 0;
 };
 
 } // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
new file mode 100644
index 0000000..20f131e
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 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 <compositionengine/Display.h>
+#include <compositionengine/Layer.h>
+
+namespace android::compositionengine {
+
+using Layers = std::vector<std::shared_ptr<compositionengine::Layer>>;
+using Outputs = std::vector<std::shared_ptr<compositionengine::Output>>;
+
+/**
+ * A parameter object for refreshing a set of outputs
+ */
+struct CompositionRefreshArgs {
+    // All the outputs being refreshed
+    Outputs outputs;
+
+    // All the layers that are potentially visible in the outputs. The order of
+    // the layers is important, and should be in traversal order from back to
+    // front.
+    Layers layers;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
index e2a0d42..d93bfa3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <cstdint>
+#include <string>
 
 #include <ui/GraphicTypes.h>
 
@@ -75,6 +76,13 @@
     // Gets the supported HDR capabilities for the profile
     virtual const HdrCapabilities& getHdrCapabilities() const = 0;
 
+    // Returns true if HWC for this profile supports the dataspace
+    virtual bool isDataspaceSupported(ui::Dataspace) const = 0;
+
+    // Returns the target dataspace for picked color mode and dataspace
+    virtual ui::Dataspace getTargetDataspace(ui::ColorMode, ui::Dataspace,
+                                             ui::Dataspace colorSpaceAgnosticDataspace) const = 0;
+
     // Debugging
     virtual void dump(std::string&) const = 0;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
index 8cb9203..451608b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
@@ -21,11 +21,7 @@
 
 #include <utils/StrongPointer.h>
 
-namespace android {
-
-typedef int64_t nsecs_t;
-
-namespace compositionengine {
+namespace android::compositionengine {
 
 class Display;
 class LayerFE;
@@ -62,5 +58,4 @@
     virtual void dump(std::string& result) const = 0;
 };
 
-} // namespace compositionengine
-} // namespace android
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 9f635b9..1f2cae9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <utils/RefBase.h>
+#include <utils/Timers.h>
 
 namespace android {
 
@@ -30,6 +31,11 @@
 // of the front-end layer
 class LayerFE : public virtual RefBase {
 public:
+    // Called before composition starts. Should return true if this layer has
+    // pending updates which would require an extra display refresh cycle to
+    // process.
+    virtual bool onPreComposition(nsecs_t refreshStartTime) = 0;
+
     // Latches the output-independent state. If includeGeometry is false, the
     // geometry state can be skipped.
     virtual void latchCompositionState(LayerFECompositionState&, bool includeGeometry) const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index e6ee078..d96d58c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -36,9 +36,9 @@
  * Used by LayerFE::getCompositionState
  */
 struct LayerFECompositionState {
-    // TODO(lpique): b/121291683 Remove this one we are sure we don't need the
-    // value recomputed / set every frame.
-    Region geomVisibleRegion;
+    // If set to true, forces client composition on all output layers until
+    // the next geometry change.
+    bool forceClientComposition{false};
 
     /*
      * Geometry state
@@ -56,6 +56,10 @@
     Region geomActiveTransparentRegion;
     FloatRect geomLayerBounds;
 
+    // TODO(lpique): b/121291683 Remove this one we are sure we don't need the
+    // value recomputed / set every frame.
+    Region geomVisibleRegion;
+
     /*
      * Presentation
      */
@@ -93,12 +97,16 @@
     sp<NativeHandle> sidebandStream;
 
     // The color for this layer
-    Hwc2::IComposerClient::Color color;
+    half4 color;
 
     /*
      * Per-frame presentation state
      */
 
+    // If true, this layer will use the dataspace chosen for the output and
+    // ignore the dataspace value just below
+    bool isColorspaceAgnostic{false};
+
     // The dataspace for this layer
     ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
 
@@ -107,6 +115,7 @@
 
     // The color transform
     mat4 colorTransform;
+    bool colorTransformIsIdentity{true};
 };
 
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 54e6bd6..5b9e282 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -46,6 +46,7 @@
 class Output {
 public:
     using OutputLayers = std::vector<std::unique_ptr<compositionengine::OutputLayer>>;
+    using ReleasedLayers = std::vector<wp<LayerFE>>;
 
     virtual ~Output();
 
@@ -71,7 +72,8 @@
     virtual void setColorTransform(const mat4&) = 0;
 
     // Sets the output color mode
-    virtual void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) = 0;
+    virtual void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent,
+                              ui::Dataspace colorSpaceAgnosticDataspace) = 0;
 
     // Outputs a string with a state dump
     virtual void dump(std::string&) const = 0;
@@ -130,6 +132,12 @@
     // Gets the ordered set of output layers for this output
     virtual const OutputLayers& getOutputLayersOrderedByZ() const = 0;
 
+    // Sets the new set of layers being released this frame.
+    virtual void setReleasedLayers(ReleasedLayers&&) = 0;
+
+    // Takes (moves) the set of layers being released this frame.
+    virtual ReleasedLayers takeReleasedLayers() = 0;
+
 protected:
     virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
     virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index cd63b57..d3a4e09 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -71,7 +71,7 @@
     // Writes the geometry state to the HWC, or does nothing if this layer does
     // not use the HWC. If includeGeometry is false, the geometry state can be
     // skipped.
-    virtual void writeStateToHWC(bool includeGeometry) const = 0;
+    virtual void writeStateToHWC(bool includeGeometry) = 0;
 
     // Debugging
     virtual void dump(std::string& result) const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index b01eb64..96e609d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -36,9 +36,19 @@
     renderengine::RenderEngine& getRenderEngine() const override;
     void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) override;
 
+    bool needsAnotherUpdate() const override;
+    nsecs_t getLastFrameRefreshTimestamp() const override;
+
+    void preComposition(CompositionRefreshArgs&) override;
+
+    // Testing
+    void setNeedsAnotherUpdateForTest(bool);
+
 private:
     std::unique_ptr<HWComposer> mHwComposer;
     std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+    bool mNeedsAnotherUpdate = false;
+    nsecs_t mRefreshStartTime = 0;
 };
 
 std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 0e20c43..1265533 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -39,7 +39,7 @@
     // compositionengine::Output overrides
     void dump(std::string&) const override;
     void setColorTransform(const mat4&) override;
-    void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
+    void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent, ui::Dataspace) override;
 
     // compositionengine::Display overrides
     const std::optional<DisplayId>& getId() const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
index 49c2d2c..e84a36e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
@@ -54,6 +54,8 @@
     bool hasDolbyVisionSupport() const override;
 
     const HdrCapabilities& getHdrCapabilities() const override;
+    bool isDataspaceSupported(ui::Dataspace) const override;
+    ui::Dataspace getTargetDataspace(ui::ColorMode, ui::Dataspace, ui::Dataspace) const override;
 
     void dump(std::string&) const override;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index b1d1f42..20812d2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -45,7 +45,7 @@
     void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
 
     void setColorTransform(const mat4&) override;
-    void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
+    void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent, ui::Dataspace) override;
 
     void dump(std::string&) const override;
 
@@ -72,6 +72,9 @@
     void setOutputLayersOrderedByZ(OutputLayers&&) override;
     const OutputLayers& getOutputLayersOrderedByZ() const override;
 
+    void setReleasedLayers(ReleasedLayers&&) override;
+    ReleasedLayers takeReleasedLayers() override;
+
     // Testing
     void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
     void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
@@ -93,6 +96,7 @@
     std::unique_ptr<compositionengine::RenderSurface> mRenderSurface;
 
     OutputLayers mOutputLayersOrderedByZ;
+    ReleasedLayers mReleasedLayers;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 0c47eb5..45b8308 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -88,9 +88,12 @@
     // Current active render intent
     ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC};
 
-    // Current active dstaspace
+    // Current active dataspace
     ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
 
+    // Current target dataspace
+    ui::Dataspace targetDataspace{ui::Dataspace::UNKNOWN};
+
     // Debugging
     void dump(std::string& result) const;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 6a4818f..708f3a1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -26,7 +26,11 @@
 
 #include "DisplayHardware/DisplayIdentification.h"
 
-namespace android::compositionengine::impl {
+namespace android::compositionengine {
+
+struct LayerFECompositionState;
+
+namespace impl {
 
 class OutputLayer : public compositionengine::OutputLayer {
 public:
@@ -44,7 +48,7 @@
     OutputLayerCompositionState& editState() override;
 
     void updateCompositionState(bool) override;
-    void writeStateToHWC(bool) const override;
+    void writeStateToHWC(bool) override;
 
     void dump(std::string& result) const override;
 
@@ -54,6 +58,14 @@
 
 private:
     Rect calculateInitialCrop() const;
+    void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
+    void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+    void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*);
+    void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+    void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+    void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+    void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+    void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
 
     const compositionengine::Output& mOutput;
     std::shared_ptr<compositionengine::Layer> mLayer;
@@ -66,4 +78,5 @@
         const CompositionEngine&, std::optional<DisplayId>, const compositionengine::Output&,
         std::shared_ptr<compositionengine::Layer>, sp<compositionengine::LayerFE>);
 
-} // namespace android::compositionengine::impl
+} // namespace impl
+} // namespace android::compositionengine
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index b78e9e0..de0f08a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -23,6 +23,7 @@
 #include <compositionengine/impl/HwcBufferCache.h>
 #include <renderengine/Mesh.h>
 #include <ui/FloatRect.h>
+#include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
@@ -57,6 +58,9 @@
     // The buffer transform to use for this layer o on this output.
     Hwc2::Transform bufferTransform{static_cast<Hwc2::Transform>(0)};
 
+    // The dataspace for this layer
+    ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+
     // The Z order index of this layer on this output
     uint32_t z;
 
@@ -70,7 +74,7 @@
         // The HWC Layer backing this layer
         std::shared_ptr<HWC2::Layer> hwcLayer;
 
-        // The HWC composition type for this layer
+        // The most recently set HWC composition type for this layer
         Hwc2::IComposerClient::Composition hwcCompositionType{
                 Hwc2::IComposerClient::Composition::INVALID};
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index 0f57685..82ecec5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/DisplayCreationArgs.h>
 #include <compositionengine/LayerCreationArgs.h>
 #include <gmock/gmock.h>
@@ -39,6 +40,11 @@
 
     MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&());
     MOCK_METHOD1(setRenderEngine, void(std::unique_ptr<renderengine::RenderEngine>));
+
+    MOCK_CONST_METHOD0(needsAnotherUpdate, bool());
+    MOCK_CONST_METHOD0(getLastFrameRefreshTimestamp, nsecs_t());
+
+    MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
index 8056c9d..1aaebea 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
@@ -42,6 +42,9 @@
     MOCK_CONST_METHOD0(hasDolbyVisionSupport, bool());
 
     MOCK_CONST_METHOD0(getHdrCapabilities, const HdrCapabilities&());
+    MOCK_CONST_METHOD1(isDataspaceSupported, bool(ui::Dataspace));
+    MOCK_CONST_METHOD3(getTargetDataspace,
+                       ui::Dataspace(ui::ColorMode, ui::Dataspace, ui::Dataspace));
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index aab18db..952f702 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -30,6 +30,8 @@
     LayerFE();
     virtual ~LayerFE();
 
+    MOCK_METHOD1(onPreComposition, bool(nsecs_t));
+
     MOCK_CONST_METHOD2(latchCompositionState, void(LayerFECompositionState&, bool));
     MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&));
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index d0e7b19..924d72c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -41,17 +41,18 @@
     MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
 
     MOCK_METHOD1(setColorTransform, void(const mat4&));
-    MOCK_METHOD3(setColorMode, void(ui::ColorMode, ui::Dataspace, ui::RenderIntent));
+    MOCK_METHOD4(setColorMode, void(ui::ColorMode, ui::Dataspace, ui::RenderIntent, ui::Dataspace));
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
     MOCK_CONST_METHOD0(getName, const std::string&());
     MOCK_METHOD1(setName, void(const std::string&));
 
-    MOCK_CONST_METHOD0(getDisplayColorProfile, DisplayColorProfile*());
-    MOCK_METHOD1(setDisplayColorProfile, void(std::unique_ptr<DisplayColorProfile>));
+    MOCK_CONST_METHOD0(getDisplayColorProfile, compositionengine::DisplayColorProfile*());
+    MOCK_METHOD1(setDisplayColorProfile,
+                 void(std::unique_ptr<compositionengine::DisplayColorProfile>));
 
-    MOCK_CONST_METHOD0(getRenderSurface, RenderSurface*());
-    MOCK_METHOD1(setRenderSurface, void(std::unique_ptr<RenderSurface>));
+    MOCK_CONST_METHOD0(getRenderSurface, compositionengine::RenderSurface*());
+    MOCK_METHOD1(setRenderSurface, void(std::unique_ptr<compositionengine::RenderSurface>));
 
     MOCK_CONST_METHOD0(getState, const OutputCompositionState&());
     MOCK_METHOD0(editState, OutputCompositionState&());
@@ -65,8 +66,12 @@
                  std::unique_ptr<compositionengine::OutputLayer>(
                          std::optional<DisplayId>, std::shared_ptr<compositionengine::Layer>,
                          sp<compositionengine::LayerFE>));
+
     MOCK_METHOD1(setOutputLayersOrderedByZ, void(OutputLayers&&));
     MOCK_CONST_METHOD0(getOutputLayersOrderedByZ, OutputLayers&());
+
+    MOCK_METHOD1(setReleasedLayers, void(ReleasedLayers&&));
+    MOCK_METHOD0(takeReleasedLayers, ReleasedLayers());
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 29cd08a..dab8b9d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -39,7 +39,7 @@
     MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
 
     MOCK_METHOD1(updateCompositionState, void(bool));
-    MOCK_CONST_METHOD1(writeStateToHWC, void(bool));
+    MOCK_METHOD1(writeStateToHWC, void(bool));
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index cb08b81..9558266 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
+#include <compositionengine/CompositionRefreshArgs.h>
+#include <compositionengine/LayerFE.h>
 #include <compositionengine/impl/CompositionEngine.h>
 #include <compositionengine/impl/Display.h>
 #include <compositionengine/impl/Layer.h>
 #include <renderengine/RenderEngine.h>
+#include <utils/Trace.h>
 
 #include "DisplayHardware/HWComposer.h"
 
@@ -59,5 +62,35 @@
     mRenderEngine = std::move(renderEngine);
 }
 
+bool CompositionEngine::needsAnotherUpdate() const {
+    return mNeedsAnotherUpdate;
+}
+
+nsecs_t CompositionEngine::getLastFrameRefreshTimestamp() const {
+    return mRefreshStartTime;
+}
+
+void CompositionEngine::preComposition(CompositionRefreshArgs& args) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    bool needsAnotherUpdate = false;
+
+    mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+    for (auto& layer : args.layers) {
+        sp<compositionengine::LayerFE> layerFE = layer->getLayerFE();
+        if (layerFE && layerFE->onPreComposition(mRefreshStartTime)) {
+            needsAnotherUpdate = true;
+        }
+    }
+
+    mNeedsAnotherUpdate = needsAnotherUpdate;
+}
+
+void CompositionEngine::setNeedsAnotherUpdateForTest(bool value) {
+    mNeedsAnotherUpdate = value;
+}
+
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index f9d70e3..8520d70 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -74,9 +74,14 @@
 }
 
 void Display::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
-                           ui::RenderIntent renderIntent) {
+                           ui::RenderIntent renderIntent,
+                           ui::Dataspace colorSpaceAgnosticDataspace) {
+    ui::Dataspace targetDataspace =
+            getDisplayColorProfile()->getTargetDataspace(mode, dataspace,
+                                                         colorSpaceAgnosticDataspace);
+
     if (mode == getState().colorMode && dataspace == getState().dataspace &&
-        renderIntent == getState().renderIntent) {
+        renderIntent == getState().renderIntent && targetDataspace == getState().targetDataspace) {
         return;
     }
 
@@ -85,7 +90,7 @@
         return;
     }
 
-    Output::setColorMode(mode, dataspace, renderIntent);
+    Output::setColorMode(mode, dataspace, renderIntent, colorSpaceAgnosticDataspace);
 
     auto& hwc = getCompositionEngine().getHwComposer();
     hwc.setActiveColorMode(*mId, mode, renderIntent);
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
index 130ab1d..5ef9097 100644
--- a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -64,6 +64,12 @@
         RenderIntent::TONE_MAP_COLORIMETRIC,
 };
 
+// Returns true if the given colorMode is considered an HDR color mode
+bool isHdrColorMode(const ColorMode colorMode) {
+    return std::any_of(std::begin(sHdrColorModes), std::end(sHdrColorModes),
+                       [colorMode](ColorMode hdrColorMode) { return hdrColorMode == colorMode; });
+}
+
 // map known color mode to dataspace
 Dataspace colorModeToDataspace(ColorMode mode) {
     switch (mode) {
@@ -90,13 +96,7 @@
     candidates.push_back(mode);
 
     // check if mode is HDR
-    bool isHdr = false;
-    for (auto hdrMode : sHdrColorModes) {
-        if (hdrMode == mode) {
-            isHdr = true;
-            break;
-        }
-    }
+    bool isHdr = isHdrColorMode(mode);
 
     // add other HDR candidates when mode is HDR
     if (isHdr) {
@@ -376,6 +376,32 @@
     }
 }
 
+bool DisplayColorProfile::isDataspaceSupported(Dataspace dataspace) const {
+    switch (dataspace) {
+        case Dataspace::BT2020_PQ:
+        case Dataspace::BT2020_ITU_PQ:
+            return hasHDR10Support();
+
+        case Dataspace::BT2020_HLG:
+        case Dataspace::BT2020_ITU_HLG:
+            return hasHLGSupport();
+
+        default:
+            return true;
+    }
+}
+
+ui::Dataspace DisplayColorProfile::getTargetDataspace(ColorMode mode, Dataspace dataspace,
+                                                      Dataspace colorSpaceAgnosticDataspace) const {
+    if (isHdrColorMode(mode)) {
+        return Dataspace::UNKNOWN;
+    }
+    if (colorSpaceAgnosticDataspace != ui::Dataspace::UNKNOWN) {
+        return colorSpaceAgnosticDataspace;
+    }
+    return dataspace;
+}
+
 void DisplayColorProfile::dump(std::string& out) const {
     out.append("   Composition Display Color State:");
 
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
index 40c4da9..37d6eaa 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
@@ -24,9 +24,10 @@
 
 using android::compositionengine::impl::dumpVal;
 
-void dumpVal(std::string& out, const char* name, Hwc2::IComposerClient::Color value) {
+void dumpVal(std::string& out, const char* name, half4 value) {
     using android::base::StringAppendF;
-    StringAppendF(&out, "%s=[%d %d %d] ", name, value.r, value.g, value.b);
+    StringAppendF(&out, "%s=[%f %f %f] ", name, static_cast<float>(value.r),
+                  static_cast<float>(value.g), static_cast<float>(value.b));
 }
 
 void dumpFrontEnd(std::string& out, const LayerFECompositionState& state) {
@@ -60,8 +61,8 @@
     dumpVal(out, "composition type", toString(state.compositionType), state.compositionType);
 
     out.append("\n      buffer: ");
+    dumpVal(out, "bufferSlot", state.bufferSlot);
     dumpVal(out, "buffer", state.buffer.get());
-    dumpVal(out, "slot", state.bufferSlot);
 
     out.append("\n      ");
     dumpVal(out, "sideband stream", state.sidebandStream.get());
@@ -70,6 +71,7 @@
     dumpVal(out, "color", state.color);
 
     out.append("\n      ");
+    dumpVal(out, "isColorspaceAgnostic", state.isColorspaceAgnostic);
     dumpVal(out, "dataspace", toString(state.dataspace), state.dataspace);
     dumpVal(out, "hdr metadata types", state.hdrMetadata.validTypes);
     dumpVal(out, "colorTransform", state.colorTransform);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 01b5781..845e3c6 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -104,15 +104,21 @@
 }
 
 void Output::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
-                          ui::RenderIntent renderIntent) {
+                          ui::RenderIntent renderIntent,
+                          ui::Dataspace colorSpaceAgnosticDataspace) {
+    ui::Dataspace targetDataspace =
+            getDisplayColorProfile()->getTargetDataspace(mode, dataspace,
+                                                         colorSpaceAgnosticDataspace);
+
     if (mState.colorMode == mode && mState.dataspace == dataspace &&
-        mState.renderIntent == renderIntent) {
+        mState.renderIntent == renderIntent && mState.targetDataspace == targetDataspace) {
         return;
     }
 
     mState.colorMode = mode;
     mState.dataspace = dataspace;
     mState.renderIntent = renderIntent;
+    mState.targetDataspace = targetDataspace;
 
     mRenderSurface->setBufferDataspace(dataspace);
 
@@ -236,6 +242,14 @@
     return mOutputLayersOrderedByZ;
 }
 
+void Output::setReleasedLayers(Output::ReleasedLayers&& layers) {
+    mReleasedLayers = std::move(layers);
+}
+
+Output::ReleasedLayers Output::takeReleasedLayers() {
+    return std::move(mReleasedLayers);
+}
+
 void Output::dirtyEntireOutput() {
     mState.dirtyRegion.set(mState.bounds);
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 9549054..0b15dad 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -44,6 +44,7 @@
     dumpVal(out, "renderIntent", toString(renderIntent), renderIntent);
     dumpVal(out, "dataspace", toString(dataspace), dataspace);
     dumpVal(out, "colorTransform", colorTransform);
+    dumpVal(out, "target dataspace", toString(targetDataspace), targetDataspace);
 
     out.append("\n");
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 5ce72b0..ebfc704 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -16,6 +16,7 @@
 
 #include <android-base/stringprintf.h>
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/DisplayColorProfile.h>
 #include <compositionengine/Layer.h>
 #include <compositionengine/LayerFE.h>
 #include <compositionengine/Output.h>
@@ -290,20 +291,43 @@
 } // namespace impl
 
 void OutputLayer::updateCompositionState(bool includeGeometry) {
+    const auto& layerFEState = mLayer->getState().frontEnd;
+    const auto& outputState = mOutput.getState();
+    const auto& profile = *mOutput.getDisplayColorProfile();
+
     if (includeGeometry) {
         mState.displayFrame = calculateOutputDisplayFrame();
         mState.sourceCrop = calculateOutputSourceCrop();
         mState.bufferTransform =
                 static_cast<Hwc2::Transform>(calculateOutputRelativeBufferTransform());
 
-        if ((mLayer->getState().frontEnd.isSecure && !mOutput.getState().isSecure) ||
+        if ((layerFEState.isSecure && !outputState.isSecure) ||
             (mState.bufferTransform & ui::Transform::ROT_INVALID)) {
             mState.forceClientComposition = true;
         }
     }
+
+    // Determine the output dependent dataspace for this layer. If it is
+    // colorspace agnostic, it just uses the dataspace chosen for the output to
+    // avoid the need for color conversion.
+    mState.dataspace = layerFEState.isColorspaceAgnostic &&
+                    outputState.targetDataspace != ui::Dataspace::UNKNOWN
+            ? outputState.targetDataspace
+            : layerFEState.dataspace;
+
+    // TODO(lpique): b/121291683 Remove this one we are sure we don't need the
+    // value recomputed / set every frame.
+    mState.visibleRegion = outputState.transform.transform(
+            layerFEState.geomVisibleRegion.intersect(outputState.viewport));
+
+    // These are evaluated every frame as they can potentially change at any
+    // time.
+    if (layerFEState.forceClientComposition || !profile.isDataspaceSupported(mState.dataspace)) {
+        mState.forceClientComposition = true;
+    }
 }
 
-void OutputLayer::writeStateToHWC(bool includeGeometry) const {
+void OutputLayer::writeStateToHWC(bool includeGeometry) {
     // Skip doing this if there is no HWC interface
     if (!mState.hwc) {
         return;
@@ -316,63 +340,213 @@
         return;
     }
 
+    const auto& outputIndependentState = mLayer->getState().frontEnd;
+    auto requestedCompositionType = outputIndependentState.compositionType;
+
     if (includeGeometry) {
-        // Output dependent state
+        writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType);
+        writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), outputIndependentState);
+    }
 
-        if (auto error = hwcLayer->setDisplayFrame(mState.displayFrame);
-            error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
-                  mLayerFE->getDebugName(), mState.displayFrame.left, mState.displayFrame.top,
-                  mState.displayFrame.right, mState.displayFrame.bottom, to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
+    writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
+    writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), outputIndependentState);
 
-        if (auto error = hwcLayer->setSourceCrop(mState.sourceCrop); error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
-                  "%s (%d)",
-                  mLayerFE->getDebugName(), mState.sourceCrop.left, mState.sourceCrop.top,
-                  mState.sourceCrop.right, mState.sourceCrop.bottom, to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
+    writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType);
+}
 
-        if (auto error = hwcLayer->setZOrder(mState.z); error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set Z %u: %s (%d)", mLayerFE->getDebugName(), mState.z,
+void OutputLayer::writeOutputDependentGeometryStateToHWC(
+        HWC2::Layer* hwcLayer, Hwc2::IComposerClient::Composition requestedCompositionType) {
+    const auto& outputDependentState = getState();
+
+    if (auto error = hwcLayer->setDisplayFrame(outputDependentState.displayFrame);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
+              mLayerFE->getDebugName(), outputDependentState.displayFrame.left,
+              outputDependentState.displayFrame.top, outputDependentState.displayFrame.right,
+              outputDependentState.displayFrame.bottom, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+
+    if (auto error = hwcLayer->setSourceCrop(outputDependentState.sourceCrop);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
+              "%s (%d)",
+              mLayerFE->getDebugName(), outputDependentState.sourceCrop.left,
+              outputDependentState.sourceCrop.top, outputDependentState.sourceCrop.right,
+              outputDependentState.sourceCrop.bottom, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+
+    if (auto error = hwcLayer->setZOrder(outputDependentState.z); error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set Z %u: %s (%d)", mLayerFE->getDebugName(), outputDependentState.z,
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    // Solid-color layers should always use an identity transform.
+    const auto bufferTransform =
+            requestedCompositionType != Hwc2::IComposerClient::Composition::SOLID_COLOR
+            ? outputDependentState.bufferTransform
+            : static_cast<Hwc2::Transform>(0);
+    if (auto error = hwcLayer->setTransform(static_cast<HWC2::Transform>(bufferTransform));
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set transform %s: %s (%d)", mLayerFE->getDebugName(),
+              toString(outputDependentState.bufferTransform).c_str(), to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeOutputIndependentGeometryStateToHWC(
+        HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) {
+    if (auto error = hwcLayer->setBlendMode(
+                static_cast<HWC2::BlendMode>(outputIndependentState.blendMode));
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set blend mode %s: %s (%d)", mLayerFE->getDebugName(),
+              toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+
+    if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", mLayerFE->getDebugName(),
+              outputIndependentState.alpha, to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    if (auto error = hwcLayer->setInfo(outputIndependentState.type, outputIndependentState.appId);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set info %s (%d)", mLayerFE->getDebugName(), to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeOutputDependentPerFrameStateToHWC(HWC2::Layer* hwcLayer) {
+    const auto& outputDependentState = getState();
+
+    // TODO(lpique): b/121291683 visibleRegion is output-dependent geometry
+    // state and should not change every frame.
+    if (auto error = hwcLayer->setVisibleRegion(outputDependentState.visibleRegion);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set visible region: %s (%d)", mLayerFE->getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+        outputDependentState.visibleRegion.dump(LOG_TAG);
+    }
+
+    if (auto error = hwcLayer->setDataspace(outputDependentState.dataspace);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mLayerFE->getDebugName(),
+              outputDependentState.dataspace, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeOutputIndependentPerFrameStateToHWC(
+        HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) {
+    switch (auto error = hwcLayer->setColorTransform(outputIndependentState.colorTransform)) {
+        case HWC2::Error::None:
+            break;
+        case HWC2::Error::Unsupported:
+            editState().forceClientComposition = true;
+            break;
+        default:
+            ALOGE("[%s] Failed to set color transform: %s (%d)", mLayerFE->getDebugName(),
                   to_string(error).c_str(), static_cast<int32_t>(error));
-        }
+    }
 
-        if (auto error =
-                    hwcLayer->setTransform(static_cast<HWC2::Transform>(mState.bufferTransform));
+    if (auto error = hwcLayer->setSurfaceDamage(outputIndependentState.surfaceDamage);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set surface damage: %s (%d)", mLayerFE->getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+        outputIndependentState.surfaceDamage.dump(LOG_TAG);
+    }
+
+    // Content-specific per-frame state
+    switch (outputIndependentState.compositionType) {
+        case Hwc2::IComposerClient::Composition::SOLID_COLOR:
+            writeSolidColorStateToHWC(hwcLayer, outputIndependentState);
+            break;
+        case Hwc2::IComposerClient::Composition::SIDEBAND:
+            writeSidebandStateToHWC(hwcLayer, outputIndependentState);
+            break;
+        case Hwc2::IComposerClient::Composition::CURSOR:
+        case Hwc2::IComposerClient::Composition::DEVICE:
+            writeBufferStateToHWC(hwcLayer, outputIndependentState);
+            break;
+        case Hwc2::IComposerClient::Composition::INVALID:
+        case Hwc2::IComposerClient::Composition::CLIENT:
+            // Ignored
+            break;
+    }
+}
+
+void OutputLayer::writeSolidColorStateToHWC(HWC2::Layer* hwcLayer,
+                                            const LayerFECompositionState& outputIndependentState) {
+    hwc_color_t color = {static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.r)),
+                         static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.g)),
+                         static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.b)),
+                         255};
+
+    if (auto error = hwcLayer->setColor(color); error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set color: %s (%d)", mLayerFE->getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeSidebandStateToHWC(HWC2::Layer* hwcLayer,
+                                          const LayerFECompositionState& outputIndependentState) {
+    if (auto error = hwcLayer->setSidebandStream(outputIndependentState.sidebandStream->handle());
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", mLayerFE->getDebugName(),
+              outputIndependentState.sidebandStream->handle(), to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
+                                        const LayerFECompositionState& outputIndependentState) {
+    auto supportedPerFrameMetadata =
+            mOutput.getDisplayColorProfile()->getSupportedPerFrameMetadata();
+    if (auto error = hwcLayer->setPerFrameMetadata(supportedPerFrameMetadata,
+                                                   outputIndependentState.hdrMetadata);
+        error != HWC2::Error::None && error != HWC2::Error::Unsupported) {
+        ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", mLayerFE->getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    uint32_t hwcSlot = 0;
+    sp<GraphicBuffer> hwcBuffer;
+    // We need access to the output-dependent state for the buffer cache there,
+    // though otherwise the buffer is not output-dependent.
+    editState().hwc->hwcBufferCache.getHwcBuffer(outputIndependentState.bufferSlot,
+                                                 outputIndependentState.buffer, &hwcSlot,
+                                                 &hwcBuffer);
+
+    if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, outputIndependentState.acquireFence);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set buffer %p: %s (%d)", mLayerFE->getDebugName(),
+              outputIndependentState.buffer->handle, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeCompositionTypeToHWC(
+        HWC2::Layer* hwcLayer, Hwc2::IComposerClient::Composition requestedCompositionType) {
+    auto& outputDependentState = editState();
+
+    // If we are forcing client composition, we need to tell the HWC
+    if (outputDependentState.forceClientComposition) {
+        requestedCompositionType = Hwc2::IComposerClient::Composition::CLIENT;
+    }
+
+    // Set the requested composition type with the HWC whenever it changes
+    if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType) {
+        outputDependentState.hwc->hwcCompositionType = requestedCompositionType;
+
+        if (auto error = hwcLayer->setCompositionType(
+                    static_cast<HWC2::Composition>(requestedCompositionType));
             error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set transform %s: %s (%d)", mLayerFE->getDebugName(),
-                  toString(mState.bufferTransform).c_str(), to_string(error).c_str(),
+            ALOGE("[%s] Failed to set composition type %s: %s (%d)", mLayerFE->getDebugName(),
+                  toString(requestedCompositionType).c_str(), to_string(error).c_str(),
                   static_cast<int32_t>(error));
         }
-
-        // Output independent state
-
-        const auto& outputIndependentState = mLayer->getState().frontEnd;
-
-        if (auto error = hwcLayer->setBlendMode(
-                    static_cast<HWC2::BlendMode>(outputIndependentState.blendMode));
-            error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set blend mode %s: %s (%d)", mLayerFE->getDebugName(),
-                  toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-
-        if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha);
-            error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", mLayerFE->getDebugName(),
-                  outputIndependentState.alpha, to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-
-        if (auto error =
-                    hwcLayer->setInfo(outputIndependentState.type, outputIndependentState.appId);
-            error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set info %s (%d)", mLayerFE->getDebugName(),
-                  to_string(error).c_str(), static_cast<int32_t>(error));
-        }
     }
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index 861ea57..e320bee 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -47,6 +47,7 @@
     dumpVal(out, "displayFrame", displayFrame);
     dumpVal(out, "sourceCrop", sourceCrop);
     dumpVal(out, "bufferTransform", toString(bufferTransform), bufferTransform);
+    dumpVal(out, "dataspace", toString(dataspace), dataspace);
     dumpVal(out, "z-index", z);
 
     if (hwc) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 3766f27..0dbf8f0 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -14,7 +14,11 @@
  * limitations under the License.
  */
 
+#include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/impl/CompositionEngine.h>
+#include <compositionengine/mock/Layer.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/Output.h>
 #include <gtest/gtest.h>
 #include <renderengine/mock/RenderEngine.h>
 
@@ -23,19 +27,19 @@
 namespace android::compositionengine {
 namespace {
 
+using ::testing::_;
+using ::testing::Return;
+using ::testing::SaveArg;
 using ::testing::StrictMock;
 
 class CompositionEngineTest : public testing::Test {
 public:
-    ~CompositionEngineTest() override;
-    mock::HWComposer* mHwc = new StrictMock<mock::HWComposer>();
+    android::mock::HWComposer* mHwc = new StrictMock<android::mock::HWComposer>();
     renderengine::mock::RenderEngine* mRenderEngine =
             new StrictMock<renderengine::mock::RenderEngine>();
     impl::CompositionEngine mEngine;
 };
 
-CompositionEngineTest::~CompositionEngineTest() = default;
-
 TEST_F(CompositionEngineTest, canInstantiateCompositionEngine) {
     auto engine = impl::createCompositionEngine();
     EXPECT_TRUE(engine.get() != nullptr);
@@ -53,5 +57,84 @@
     EXPECT_EQ(mRenderEngine, &mEngine.getRenderEngine());
 }
 
+/*
+ * CompositionEngine::preComposition
+ */
+
+class PreCompositionTest : public CompositionEngineTest {
+public:
+    PreCompositionTest() {
+        EXPECT_CALL(*mLayer1, getLayerFE()).WillRepeatedly(Return(mLayer1FE));
+        EXPECT_CALL(*mLayer2, getLayerFE()).WillRepeatedly(Return(mLayer2FE));
+        EXPECT_CALL(*mLayer3, getLayerFE()).WillRepeatedly(Return(mLayer3FE));
+        // getLayerFE() can return nullptr. Ensure that this is handled.
+        EXPECT_CALL(*mLayer4, getLayerFE()).WillRepeatedly(Return(nullptr));
+
+        mRefreshArgs.outputs = {mOutput};
+        mRefreshArgs.layers = {mLayer1, mLayer2, mLayer3, mLayer4};
+    }
+
+    std::shared_ptr<mock::Output> mOutput{std::make_shared<StrictMock<mock::Output>>()};
+    std::shared_ptr<mock::Layer> mLayer1{std::make_shared<StrictMock<mock::Layer>>()};
+    std::shared_ptr<mock::Layer> mLayer2{std::make_shared<StrictMock<mock::Layer>>()};
+    std::shared_ptr<mock::Layer> mLayer3{std::make_shared<StrictMock<mock::Layer>>()};
+    std::shared_ptr<mock::Layer> mLayer4{std::make_shared<StrictMock<mock::Layer>>()};
+    sp<StrictMock<mock::LayerFE>> mLayer1FE{new StrictMock<mock::LayerFE>()};
+    sp<StrictMock<mock::LayerFE>> mLayer2FE{new StrictMock<mock::LayerFE>()};
+    sp<StrictMock<mock::LayerFE>> mLayer3FE{new StrictMock<mock::LayerFE>()};
+
+    CompositionRefreshArgs mRefreshArgs;
+};
+
+TEST_F(PreCompositionTest, preCompositionSetsFrameTimestamp) {
+    const nsecs_t before = systemTime(SYSTEM_TIME_MONOTONIC);
+    CompositionRefreshArgs emptyArgs;
+    mEngine.preComposition(emptyArgs);
+    const nsecs_t after = systemTime(SYSTEM_TIME_MONOTONIC);
+
+    // The frame timestamp should be between the before and after timestamps
+    EXPECT_GE(mEngine.getLastFrameRefreshTimestamp(), before);
+    EXPECT_LE(mEngine.getLastFrameRefreshTimestamp(), after);
+}
+
+TEST_F(PreCompositionTest, preCompositionInvokesLayerPreCompositionWithFrameTimestamp) {
+    nsecs_t ts1 = 0;
+    nsecs_t ts2 = 0;
+    nsecs_t ts3 = 0;
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts1), Return(false)));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts2), Return(false)));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
+
+    mEngine.preComposition(mRefreshArgs);
+
+    // Each of the onPreComposition calls should used the same refresh timestamp
+    EXPECT_EQ(ts1, mEngine.getLastFrameRefreshTimestamp());
+    EXPECT_EQ(ts2, mEngine.getLastFrameRefreshTimestamp());
+    EXPECT_EQ(ts3, mEngine.getLastFrameRefreshTimestamp());
+}
+
+TEST_F(PreCompositionTest, preCompositionDefaultsToNoUpdateNeeded) {
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
+
+    mEngine.setNeedsAnotherUpdateForTest(true);
+
+    mEngine.preComposition(mRefreshArgs);
+
+    // The call should have cleared the needsAnotherUpdate flag
+    EXPECT_FALSE(mEngine.needsAnotherUpdate());
+}
+
+TEST_F(PreCompositionTest, preCompositionSetsNeedsAnotherUpdateIfAtLeastOneLayerRequestsIt) {
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(true));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
+
+    mEngine.preComposition(mRefreshArgs);
+
+    EXPECT_TRUE(mEngine.needsAnotherUpdate());
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
index 9215884..c07dfbb 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
@@ -638,5 +638,66 @@
     checkGetBestColorMode(profile, expectedResults);
 }
 
+/*
+ * RenderSurface::isDataspaceSupported()
+ */
+
+TEST_F(DisplayColorProfileTest, isDataspaceSupportedWorksForProfileWithNoHdrSupport) {
+    auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::UNKNOWN));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::V0_SRGB));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_PQ));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_PQ));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_HLG));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_HLG));
+}
+
+TEST_F(DisplayColorProfileTest, isDataspaceSupportedWorksForProfileWithHdr10Support) {
+    auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
+
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::UNKNOWN));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::V0_SRGB));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_PQ));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_PQ));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_HLG));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_HLG));
+}
+
+TEST_F(DisplayColorProfileTest, isDataspaceSupportedWorksForProfileWithHlgSupport) {
+    auto profile = ProfileFactory::createProfileWithBT2100PQSupport();
+
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::UNKNOWN));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::V0_SRGB));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_PQ));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_PQ));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_HLG));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_HLG));
+}
+
+/*
+ * RenderSurface::getTargetDataspace()
+ */
+
+TEST_F(DisplayColorProfileTest, getTargetDataspaceWorks) {
+    auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+    // For a non-HDR colorspace with no colorSpaceAgnosticDataspace override,
+    // the input dataspace should be returned.
+    EXPECT_EQ(Dataspace::DISPLAY_P3,
+              profile.getTargetDataspace(ColorMode::DISPLAY_P3, Dataspace::DISPLAY_P3,
+                                         Dataspace::UNKNOWN));
+
+    // If colorSpaceAgnosticDataspace is set, its value should be returned
+    EXPECT_EQ(Dataspace::V0_SRGB,
+              profile.getTargetDataspace(ColorMode::DISPLAY_P3, Dataspace::DISPLAY_P3,
+                                         Dataspace::V0_SRGB));
+
+    // For an HDR colorspace, Dataspace::UNKNOWN should be returned.
+    EXPECT_EQ(Dataspace::UNKNOWN,
+              profile.getTargetDataspace(ColorMode::BT2100_PQ, Dataspace::BT2020_PQ,
+                                         Dataspace::UNKNOWN));
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 33444a5..f0aea25 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -22,6 +22,7 @@
 #include <compositionengine/RenderSurfaceCreationArgs.h>
 #include <compositionengine/impl/Display.h>
 #include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
 #include <compositionengine/mock/NativeWindow.h>
 #include <compositionengine/mock/RenderSurface.h>
 #include <gtest/gtest.h>
@@ -31,6 +32,7 @@
 namespace android::compositionengine {
 namespace {
 
+using testing::_;
 using testing::Return;
 using testing::ReturnRef;
 using testing::StrictMock;
@@ -140,21 +142,27 @@
 TEST_F(DisplayTest, setColorModeSetsModeUnlessNoChange) {
     mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
     mDisplay.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+    mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mDisplay.setDisplayColorProfileForTest(std::unique_ptr<DisplayColorProfile>(colorProfile));
 
     EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+    EXPECT_CALL(*colorProfile, getTargetDataspace(_, _, _))
+            .WillRepeatedly(Return(ui::Dataspace::UNKNOWN));
 
     // These values are expected to be the initial state.
     ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
     ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
     ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
+    ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().targetDataspace);
 
-    // Otherwise if the values are unchanged, nothing happens
+    // If the set values are unchanged, nothing happens
     mDisplay.setColorMode(ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
-                          ui::RenderIntent::COLORIMETRIC);
+                          ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN);
 
     EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
     EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
     EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().targetDataspace);
 
     // Otherwise if the values are different, updates happen
     EXPECT_CALL(*renderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
@@ -164,23 +172,34 @@
             .Times(1);
 
     mDisplay.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
-                          ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+                          ui::RenderIntent::TONE_MAP_COLORIMETRIC, ui::Dataspace::UNKNOWN);
 
     EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mDisplay.getState().colorMode);
     EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mDisplay.getState().dataspace);
     EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay.getState().renderIntent);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().targetDataspace);
 }
 
 TEST_F(DisplayTest, setColorModeDoesNothingForVirtualDisplay) {
     impl::Display virtualDisplay{mCompositionEngine,
                                  DisplayCreationArgs{false, true, DEFAULT_DISPLAY_ID}};
 
+    mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>();
+    virtualDisplay.setDisplayColorProfileForTest(
+            std::unique_ptr<DisplayColorProfile>(colorProfile));
+
+    EXPECT_CALL(*colorProfile,
+                getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                                   ui::Dataspace::UNKNOWN))
+            .WillOnce(Return(ui::Dataspace::UNKNOWN));
+
     virtualDisplay.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
-                                ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+                                ui::RenderIntent::TONE_MAP_COLORIMETRIC, ui::Dataspace::UNKNOWN);
 
     EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay.getState().colorMode);
     EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay.getState().dataspace);
     EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay.getState().renderIntent);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().targetDataspace);
 }
 
 /* ------------------------------------------------------------------------
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index ae906cd..7b9528b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -16,6 +16,7 @@
 
 #include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
 #include <compositionengine/mock/Layer.h>
 #include <compositionengine/mock/LayerFE.h>
 #include <compositionengine/mock/Output.h>
@@ -25,6 +26,7 @@
 #include "MockHWC2.h"
 #include "MockHWComposer.h"
 #include "RectMatcher.h"
+#include "RegionMatcher.h"
 
 namespace android::compositionengine {
 namespace {
@@ -45,6 +47,15 @@
 
 const std::string kOutputName{"Test Output"};
 
+MATCHER_P(ColorEq, expected, "") {
+    *result_listener << "Colors are not equal\n";
+    *result_listener << "expected " << expected.r << " " << expected.g << " " << expected.b << " "
+                     << expected.a << "\n";
+    *result_listener << "actual " << arg.r << " " << arg.g << " " << arg.b << " " << arg.a << "\n";
+
+    return expected.r == arg.r && expected.g == arg.g && expected.b == arg.b && expected.a == arg.a;
+}
+
 class OutputLayerTest : public testing::Test {
 public:
     OutputLayerTest() {
@@ -429,6 +440,9 @@
     OutputLayerUpdateCompositionStateTest() {
         EXPECT_CALL(*mLayer, getState()).WillRepeatedly(ReturnRef(mLayerState));
         EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState));
+        EXPECT_CALL(mOutput, getDisplayColorProfile())
+                .WillRepeatedly(Return(&mDisplayColorProfile));
+        EXPECT_CALL(mDisplayColorProfile, isDataspaceSupported(_)).WillRepeatedly(Return(true));
     }
 
     ~OutputLayerUpdateCompositionStateTest() = default;
@@ -453,6 +467,7 @@
 
     using OutputLayer = OutputLayerPartialMockForUpdateCompositionState;
     StrictMock<OutputLayer> mOutputLayer{mOutput, mLayer, mLayerFE};
+    StrictMock<mock::DisplayColorProfile> mDisplayColorProfile;
 };
 
 TEST_F(OutputLayerUpdateCompositionStateTest, setsStateNormally) {
@@ -498,12 +513,50 @@
     EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
 }
 
+TEST_F(OutputLayerUpdateCompositionStateTest, setsOutputLayerColorspaceCorrectly) {
+    mLayerState.frontEnd.dataspace = ui::Dataspace::DISPLAY_P3;
+    mOutputState.targetDataspace = ui::Dataspace::V0_SCRGB;
+
+    // If the layer is not colorspace agnostic, the output layer dataspace
+    // should use the layers requested colorspace.
+    mLayerState.frontEnd.isColorspaceAgnostic = false;
+
+    mOutputLayer.updateCompositionState(false);
+
+    EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutputLayer.getState().dataspace);
+
+    // If the layer is colorspace agnostic, the output layer dataspace
+    // should use the colorspace chosen for the whole output.
+    mLayerState.frontEnd.isColorspaceAgnostic = true;
+
+    mOutputLayer.updateCompositionState(false);
+
+    EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
+}
+
 TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) {
     mOutputLayer.updateCompositionState(false);
 
     EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition);
 }
 
+TEST_F(OutputLayerUpdateCompositionStateTest, clientCompositionForcedFromFrontEndFlagAtAnyTime) {
+    mLayerState.frontEnd.forceClientComposition = true;
+
+    mOutputLayer.updateCompositionState(false);
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest,
+       clientCompositionForcedFromUnsupportedDataspaceAtAnyTime) {
+    EXPECT_CALL(mDisplayColorProfile, isDataspaceSupported(_)).WillRepeatedly(Return(false));
+
+    mOutputLayer.updateCompositionState(false);
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
 /*
  * OutputLayer::writeStateToHWC()
  */
@@ -518,8 +571,19 @@
     static constexpr float kAlpha = 51.f;
     static constexpr uint32_t kType = 61u;
     static constexpr uint32_t kAppId = 62u;
+    static constexpr ui::Dataspace kDataspace = static_cast<ui::Dataspace>(71);
+    static constexpr int kSupportedPerFrameMetadata = 101;
+    static constexpr int kExpectedHwcSlot = 0;
 
+    static const half4 kColor;
     static const Rect kDisplayFrame;
+    static const Region kVisibleRegion;
+    static const mat4 kColorTransform;
+    static const Region kSurfaceDamage;
+    static const HdrMetadata kHdrMetadata;
+    static native_handle_t* kSidebandStreamHandle;
+    static const sp<GraphicBuffer> kBuffer;
+    static const sp<Fence> kFence;
 
     OutputLayerWriteStateToHWCTest() {
         auto& outputLayerState = mOutputLayer.editState();
@@ -529,13 +593,31 @@
         outputLayerState.sourceCrop = kSourceCrop;
         outputLayerState.z = kZOrder;
         outputLayerState.bufferTransform = static_cast<Hwc2::Transform>(kBufferTransform);
+        outputLayerState.visibleRegion = kVisibleRegion;
+        outputLayerState.dataspace = kDataspace;
 
         mLayerState.frontEnd.blendMode = kBlendMode;
         mLayerState.frontEnd.alpha = kAlpha;
         mLayerState.frontEnd.type = kType;
         mLayerState.frontEnd.appId = kAppId;
+        mLayerState.frontEnd.colorTransform = kColorTransform;
+        mLayerState.frontEnd.color = kColor;
+        mLayerState.frontEnd.surfaceDamage = kSurfaceDamage;
+        mLayerState.frontEnd.hdrMetadata = kHdrMetadata;
+        mLayerState.frontEnd.sidebandStream = NativeHandle::create(kSidebandStreamHandle, false);
+        mLayerState.frontEnd.buffer = kBuffer;
+        mLayerState.frontEnd.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
+        mLayerState.frontEnd.acquireFence = kFence;
+
+        EXPECT_CALL(mOutput, getDisplayColorProfile())
+                .WillRepeatedly(Return(&mDisplayColorProfile));
+        EXPECT_CALL(mDisplayColorProfile, getSupportedPerFrameMetadata())
+                .WillRepeatedly(Return(kSupportedPerFrameMetadata));
     }
 
+    // Some tests may need to simulate unsupported HWC calls
+    enum class SimulateUnsupported { None, ColorTransform };
+
     void expectGeometryCommonCalls() {
         EXPECT_CALL(*mHwcLayer, setDisplayFrame(kDisplayFrame)).WillOnce(Return(kError));
         EXPECT_CALL(*mHwcLayer, setSourceCrop(kSourceCrop)).WillOnce(Return(kError));
@@ -549,10 +631,62 @@
         EXPECT_CALL(*mHwcLayer, setInfo(kType, kAppId)).WillOnce(Return(kError));
     }
 
+    void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None) {
+        EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(kVisibleRegion)))
+                .WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setDataspace(kDataspace)).WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setColorTransform(kColorTransform))
+                .WillOnce(Return(unsupported == SimulateUnsupported::ColorTransform
+                                         ? HWC2::Error::Unsupported
+                                         : HWC2::Error::None));
+        EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(kSurfaceDamage)))
+                .WillOnce(Return(kError));
+    }
+
+    void expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition compositionType) {
+        EXPECT_CALL(*mHwcLayer, setCompositionType(static_cast<HWC2::Composition>(compositionType)))
+                .WillOnce(Return(kError));
+    }
+
+    void expectNoSetCompositionTypeCall() {
+        EXPECT_CALL(*mHwcLayer, setCompositionType(_)).Times(0);
+    }
+
+    void expectSetColorCall() {
+        hwc_color_t color = {static_cast<uint8_t>(std::round(kColor.r * 255)),
+                             static_cast<uint8_t>(std::round(kColor.g * 255)),
+                             static_cast<uint8_t>(std::round(kColor.b * 255)), 255};
+
+        EXPECT_CALL(*mHwcLayer, setColor(ColorEq(color))).WillOnce(Return(kError));
+    }
+
+    void expectSetSidebandHandleCall() {
+        EXPECT_CALL(*mHwcLayer, setSidebandStream(kSidebandStreamHandle));
+    }
+
+    void expectSetHdrMetadataAndBufferCalls() {
+        EXPECT_CALL(*mHwcLayer, setPerFrameMetadata(kSupportedPerFrameMetadata, kHdrMetadata));
+        EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, kBuffer, kFence));
+    }
+
     std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()};
+    StrictMock<mock::DisplayColorProfile> mDisplayColorProfile;
 };
 
+const half4 OutputLayerWriteStateToHWCTest::kColor{81.f / 255.f, 82.f / 255.f, 83.f / 255.f,
+                                                   84.f / 255.f};
 const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044};
+const Region OutputLayerWriteStateToHWCTest::kVisibleRegion{Rect{1005, 1006, 1007, 1008}};
+const mat4 OutputLayerWriteStateToHWCTest::kColorTransform{
+        1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016,
+        1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024,
+};
+const Region OutputLayerWriteStateToHWCTest::kSurfaceDamage{Rect{1025, 1026, 1027, 1028}};
+const HdrMetadata OutputLayerWriteStateToHWCTest::kHdrMetadata{{/* LightFlattenable */}, 1029};
+native_handle_t* OutputLayerWriteStateToHWCTest::kSidebandStreamHandle =
+        reinterpret_cast<native_handle_t*>(1031);
+const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer;
+const sp<Fence> OutputLayerWriteStateToHWCTest::kFence;
 
 TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) {
     mOutputLayer.editState().hwc.reset();
@@ -566,11 +700,89 @@
     mOutputLayer.writeStateToHWC(true);
 }
 
-TEST_F(OutputLayerWriteStateToHWCTest, canSetsAllState) {
+TEST_F(OutputLayerWriteStateToHWCTest, canSetAllState) {
     expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+
+    expectNoSetCompositionTypeCall();
 
     mOutputLayer.writeStateToHWC(true);
 }
 
+TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSolidColor) {
+    mLayerState.frontEnd.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+
+    expectPerFrameCommonCalls();
+    expectSetColorCall();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SOLID_COLOR);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
+    mLayerState.frontEnd.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
+
+    expectPerFrameCommonCalls();
+    expectSetSidebandHandleCall();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SIDEBAND);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
+    mLayerState.frontEnd.compositionType = Hwc2::IComposerClient::Composition::CURSOR;
+
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CURSOR);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
+    mLayerState.frontEnd.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) {
+    (*mOutputLayer.editState().hwc).hwcCompositionType =
+            Hwc2::IComposerClient::Composition::SOLID_COLOR;
+
+    mLayerState.frontEnd.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+
+    expectPerFrameCommonCalls();
+    expectSetColorCall();
+    expectNoSetCompositionTypeCall();
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
+    mLayerState.frontEnd.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+
+    expectPerFrameCommonCalls(SimulateUnsupported::ColorTransform);
+    expectSetColorCall();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
+    mOutputLayer.editState().forceClientComposition = true;
+
+    mLayerState.frontEnd.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+
+    expectPerFrameCommonCalls();
+    expectSetColorCall();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index fee0c11..6f087d9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -205,24 +205,36 @@
  */
 
 TEST_F(OutputTest, setColorModeSetsStateAndDirtiesOutputIfChanged) {
+    EXPECT_CALL(*mDisplayColorProfile,
+                getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                                   ui::Dataspace::UNKNOWN))
+            .WillOnce(Return(ui::Dataspace::UNKNOWN));
     EXPECT_CALL(*mRenderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
 
     mOutput.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
-                         ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+                         ui::RenderIntent::TONE_MAP_COLORIMETRIC, ui::Dataspace::UNKNOWN);
 
     EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mOutput.getState().colorMode);
     EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutput.getState().dataspace);
     EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput.getState().renderIntent);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, mOutput.getState().targetDataspace);
+
     EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
 }
 
 TEST_F(OutputTest, setColorModeDoesNothingIfNoChange) {
+    EXPECT_CALL(*mDisplayColorProfile,
+                getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                                   ui::Dataspace::UNKNOWN))
+            .WillOnce(Return(ui::Dataspace::UNKNOWN));
+
     mOutput.editState().colorMode = ui::ColorMode::DISPLAY_P3;
     mOutput.editState().dataspace = ui::Dataspace::DISPLAY_P3;
     mOutput.editState().renderIntent = ui::RenderIntent::TONE_MAP_COLORIMETRIC;
+    mOutput.editState().targetDataspace = ui::Dataspace::UNKNOWN;
 
     mOutput.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
-                         ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+                         ui::RenderIntent::TONE_MAP_COLORIMETRIC, ui::Dataspace::UNKNOWN);
 
     EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
 }
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index 7927fa9..3a5f3fa 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -39,7 +39,4 @@
     return !isHiddenByPolicy();
 }
 
-void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&, const ui::Transform&,
-                                     const Rect&, int32_t, const ui::Dataspace) {}
-
 } // namespace android
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index 7222a3e..57267c7 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -33,10 +33,6 @@
 
     bool canReceiveInput() const override;
 
-    void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
-                         const Rect& viewport, int32_t supportedPerFrameMetadata,
-                         const ui::Dataspace targetDataspace) override;
-
     bool isCreatedFromMainThread() const override { return true; }
 
     bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 4a13bfb..d136562 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -126,14 +126,6 @@
     return mVisibleLayersSortedByZ;
 }
 
-void DisplayDevice::setLayersNeedingFences(const Vector< sp<Layer> >& layers) {
-    mLayersNeedingFences = layers;
-}
-
-const Vector< sp<Layer> >& DisplayDevice::getLayersNeedingFences() const {
-    return mLayersNeedingFences;
-}
-
 // ----------------------------------------------------------------------------
 void DisplayDevice::setPowerMode(int mode) {
     mPowerMode = mode;
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 0067b50..8bc19d4 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -88,8 +88,6 @@
 
     void                    setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers);
     const Vector< sp<Layer> >& getVisibleLayersSortedByZ() const;
-    void                    setLayersNeedingFences(const Vector< sp<Layer> >& layers);
-    const Vector< sp<Layer> >& getLayersNeedingFences() const;
 
     void                    setLayerStack(uint32_t stack);
     void                    setDisplaySize(const int newWidth, const int newHeight);
@@ -182,8 +180,6 @@
 
     // list of visible layers on that display
     Vector< sp<Layer> > mVisibleLayersSortedByZ;
-    // list of layers needing fences
-    Vector< sp<Layer> > mLayersNeedingFences;
 
     /*
      * Transaction state
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 26c61ba..b5f0800 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -242,20 +242,6 @@
 // h/w composer set-up
 // ---------------------------------------------------------------------------
 
-bool Layer::hasHwcLayer(const sp<const DisplayDevice>& displayDevice) {
-    auto outputLayer = findOutputLayerForDisplay(displayDevice);
-    LOG_FATAL_IF(!outputLayer);
-    return outputLayer->getState().hwc && (*outputLayer->getState().hwc).hwcLayer != nullptr;
-}
-
-HWC2::Layer* Layer::getHwcLayer(const sp<const DisplayDevice>& displayDevice) {
-    auto outputLayer = findOutputLayerForDisplay(displayDevice);
-    if (!outputLayer || !outputLayer->getState().hwc) {
-        return nullptr;
-    }
-    return (*outputLayer->getState().hwc).hwcLayer.get();
-}
-
 Rect Layer::getContentCrop() const {
     // this is the crop rectangle that applies to the buffer
     // itself (as opposed to the window)
@@ -454,29 +440,38 @@
     compositionState.appId = appId;
 }
 
+void Layer::latchPerFrameState(compositionengine::LayerFECompositionState& compositionState) const {
+    compositionState.forceClientComposition = false;
+
+    // TODO(lpique): b/121291683 Remove this one we are sure we don't need the
+    // value recomputed / set every frame.
+    compositionState.geomVisibleRegion = visibleRegion;
+
+    compositionState.isColorspaceAgnostic = isColorSpaceAgnostic();
+    compositionState.dataspace = mCurrentDataSpace;
+    compositionState.colorTransform = getColorTransform();
+    compositionState.colorTransformIsIdentity = !hasColorTransform();
+    compositionState.surfaceDamage = surfaceDamageRegion;
+
+    // Force client composition for special cases known only to the front-end.
+    if (isHdrY410() || getRoundedCornerState().radius > 0.0f) {
+        compositionState.forceClientComposition = true;
+    }
+}
+
 void Layer::latchCompositionState(compositionengine::LayerFECompositionState& compositionState,
                                   bool includeGeometry) const {
     if (includeGeometry) {
         latchGeometry(compositionState);
     }
+
+    latchPerFrameState(compositionState);
 }
 
 const char* Layer::getDebugName() const {
     return mName.string();
 }
 
-void Layer::forceClientComposition(const sp<DisplayDevice>& display) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    outputLayer->editState().forceClientComposition = true;
-}
-
-bool Layer::getForceClientComposition(const sp<DisplayDevice>& display) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    return outputLayer->getState().forceClientComposition;
-}
-
 void Layer::updateCursorPosition(const sp<const DisplayDevice>& display) {
     const auto outputLayer = findOutputLayerForDisplay(display);
     LOG_FATAL_IF(!outputLayer);
@@ -563,29 +558,6 @@
     return true;
 }
 
-void Layer::setCompositionType(const sp<const DisplayDevice>& display,
-                               Hwc2::IComposerClient::Composition type) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    LOG_FATAL_IF(!outputLayer->getState().hwc);
-    auto& compositionState = outputLayer->editState();
-
-    ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", ((*compositionState.hwc).hwcLayer)->getId(),
-          toString(type).c_str(), 1);
-    if ((*compositionState.hwc).hwcCompositionType != type) {
-        ALOGV("    actually setting");
-        (*compositionState.hwc).hwcCompositionType = type;
-
-        auto error = (*compositionState.hwc)
-                             .hwcLayer->setCompositionType(static_cast<HWC2::Composition>(type));
-        ALOGE_IF(error != HWC2::Error::None,
-                 "[%s] Failed to set "
-                 "composition type %s: %s (%d)",
-                 mName.string(), toString(type).c_str(), to_string(error).c_str(),
-                 static_cast<int32_t>(error));
-    }
-}
-
 Hwc2::IComposerClient::Composition Layer::getCompositionType(
         const sp<const DisplayDevice>& display) const {
     const auto outputLayer = findOutputLayerForDisplay(display);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index b693a47..45199dd 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -469,23 +469,13 @@
 
 protected:
     void latchGeometry(compositionengine::LayerFECompositionState& outState) const;
+    virtual void latchPerFrameState(compositionengine::LayerFECompositionState& outState) const;
 
 public:
     virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
 
     virtual bool isHdrY410() const { return false; }
 
-    void forceClientComposition(const sp<DisplayDevice>& display);
-    bool getForceClientComposition(const sp<DisplayDevice>& display);
-    virtual void setPerFrameData(const sp<const DisplayDevice>& display,
-                                 const ui::Transform& transform, const Rect& viewport,
-                                 int32_t supportedPerFrameMetadata,
-                                 const ui::Dataspace targetDataspace) = 0;
-
-    // callIntoHwc exists so we can update our local state and call
-    // acceptDisplayChanges without unnecessarily updating the device's state
-    void setCompositionType(const sp<const DisplayDevice>& display,
-                            Hwc2::IComposerClient::Composition type);
     Hwc2::IComposerClient::Composition getCompositionType(
             const sp<const DisplayDevice>& display) const;
     bool getClearClientTarget(const sp<const DisplayDevice>& display) const;
@@ -495,12 +485,6 @@
     virtual void setTransformHint(uint32_t /*orientation*/) const { }
 
     /*
-     * called before composition.
-     * returns true if the layer has pending updates.
-     */
-    virtual bool onPreComposition(nsecs_t refreshStartTime) = 0;
-
-    /*
      * called after composition.
      * returns true if the layer latched a new buffer this frame.
      */
@@ -561,8 +545,9 @@
      * operation, so this should be set only if needed). Typically this is used
      * to figure out if the content or size of a surface has changed.
      */
-    virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/) {
-        return {};
+    virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
+                             nsecs_t /*expectedPresentTime*/) {
+        return false;
     }
 
     virtual bool isBufferLatched() const { return false; }
@@ -607,10 +592,6 @@
     virtual int32_t getQueuedFrameCount() const { return 0; }
 
     // -----------------------------------------------------------------------
-
-    bool hasHwcLayer(const sp<const DisplayDevice>& displayDevice);
-    HWC2::Layer* getHwcLayer(const sp<const DisplayDevice>& displayDevice);
-
     inline const State& getDrawingState() const { return mDrawingState; }
     inline const State& getCurrentState() const { return mCurrentState; }
     inline State& getCurrentState() { return mCurrentState; }
@@ -812,7 +793,7 @@
     // this to be called once.
     sp<IBinder> getHandle();
     const String8& getName() const;
-    virtual void notifyAvailableFrames() {}
+    virtual void notifyAvailableFrames(nsecs_t /*expectedPresentTime*/) {}
     virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
     bool getPremultipledAlpha() const;
 
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index 0c94052..ad5eb33 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -64,7 +64,7 @@
     DispSyncThread(const char* name, bool showTraceDetailedInfo)
           : mName(name),
             mStop(false),
-            mModelLocked(false),
+            mModelLocked("DispSync:ModelLocked", false),
             mPeriod(0),
             mPhase(0),
             mReferenceTime(0),
@@ -121,13 +121,11 @@
     void lockModel() {
         Mutex::Autolock lock(mMutex);
         mModelLocked = true;
-        ATRACE_INT("DispSync:ModelLocked", mModelLocked);
     }
 
     void unlockModel() {
         Mutex::Autolock lock(mMutex);
         mModelLocked = false;
-        ATRACE_INT("DispSync:ModelLocked", mModelLocked);
     }
 
     virtual bool threadLoop() {
@@ -431,7 +429,7 @@
     const char* const mName;
 
     bool mStop;
-    bool mModelLocked;
+    TracedOrdinal<bool> mModelLocked;
 
     nsecs_t mPeriod;
     nsecs_t mPhase;
@@ -454,15 +452,14 @@
 
 class ZeroPhaseTracer : public DispSync::Callback {
 public:
-    ZeroPhaseTracer() : mParity(false) {}
+    ZeroPhaseTracer() : mParity("ZERO_PHASE_VSYNC", false) {}
 
     virtual void onDispSyncEvent(nsecs_t /*when*/) {
         mParity = !mParity;
-        ATRACE_INT("ZERO_PHASE_VSYNC", mParity ? 1 : 0);
     }
 
 private:
-    bool mParity;
+    TracedOrdinal<bool> mParity;
 };
 
 DispSync::DispSync(const char* name) : mName(name), mRefreshSkipCount(0) {
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 5faf46e..571c9ca 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -31,19 +31,16 @@
                                nsecs_t offsetThresholdForNextVsync, bool traceVsync,
                                const char* name)
       : mName(name),
+        mValue(base::StringPrintf("VSYNC-%s", name), 0),
         mTraceVsync(traceVsync),
         mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)),
-        mVsyncEventLabel(base::StringPrintf("VSYNC-%s", name)),
-        mVsyncOffsetLabel(base::StringPrintf("VsyncOffset-%s", name)),
-        mVsyncNegativeOffsetLabel(base::StringPrintf("VsyncNegativeOffset-%s", name)),
         mDispSync(dispSync),
-        mPhaseOffset(phaseOffset),
+        mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset),
         mOffsetThresholdForNextVsync(offsetThresholdForNextVsync) {}
 
 void DispSyncSource::setVSyncEnabled(bool enable) {
     std::lock_guard lock(mVsyncMutex);
     if (enable) {
-        tracePhaseOffset();
         status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
                                                    static_cast<DispSync::Callback*>(this),
                                                    mLastCallbackTime);
@@ -83,7 +80,6 @@
     }
 
     mPhaseOffset = phaseOffset;
-    tracePhaseOffset();
 
     // If we're not enabled, we don't need to mess with the listeners
     if (!mEnabled) {
@@ -106,7 +102,6 @@
 
     if (mTraceVsync) {
         mValue = (mValue + 1) % 2;
-        ATRACE_INT(mVsyncEventLabel.c_str(), mValue);
     }
 
     if (callback != nullptr) {
@@ -114,14 +109,4 @@
     }
 }
 
-void DispSyncSource::tracePhaseOffset() {
-    if (mPhaseOffset > 0) {
-        ATRACE_INT(mVsyncOffsetLabel.c_str(), mPhaseOffset);
-        ATRACE_INT(mVsyncNegativeOffsetLabel.c_str(), 0);
-    } else {
-        ATRACE_INT(mVsyncOffsetLabel.c_str(), 0);
-        ATRACE_INT(mVsyncNegativeOffsetLabel.c_str(), -mPhaseOffset);
-    }
-}
-
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 50560a5..740c8c4 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -20,6 +20,7 @@
 
 #include "DispSync.h"
 #include "EventThread.h"
+#include "TracedOrdinal.h"
 
 namespace android {
 
@@ -39,16 +40,11 @@
     // The following method is the implementation of the DispSync::Callback.
     virtual void onDispSyncEvent(nsecs_t when);
 
-    void tracePhaseOffset() REQUIRES(mVsyncMutex);
-
     const char* const mName;
-    int mValue = 0;
+    TracedOrdinal<int> mValue;
 
     const bool mTraceVsync;
     const std::string mVsyncOnLabel;
-    const std::string mVsyncEventLabel;
-    const std::string mVsyncOffsetLabel;
-    const std::string mVsyncNegativeOffsetLabel;
     nsecs_t mLastCallbackTime GUARDED_BY(mVsyncMutex) = 0;
 
     DispSync* mDispSync;
@@ -57,7 +53,7 @@
     VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
 
     std::mutex mVsyncMutex;
-    nsecs_t mPhaseOffset GUARDED_BY(mVsyncMutex);
+    TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
     const nsecs_t mOffsetThresholdForNextVsync;
     bool mEnabled GUARDED_BY(mVsyncMutex) = false;
 };
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 8a2604f..9f8567d 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -18,115 +18,102 @@
 
 #include <cutils/properties.h>
 
+#include <optional>
+
 #include "SurfaceFlingerProperties.h"
 
-namespace android {
-using namespace android::sysprop;
+namespace {
 
-namespace scheduler {
+std::optional<int> getProperty(const char* name) {
+    char value[PROPERTY_VALUE_MAX];
+    property_get(name, value, "-1");
+    if (const int i = atoi(value); i != -1) return i;
+    return std::nullopt;
+}
 
-using RefreshRateType = RefreshRateConfigs::RefreshRateType;
+} // namespace
+
+namespace android::scheduler {
+
 PhaseOffsets::~PhaseOffsets() = default;
 
 namespace impl {
+
 PhaseOffsets::PhaseOffsets() {
-    int64_t vsyncPhaseOffsetNs = vsync_event_phase_offset_ns(1000000);
-
-    int64_t sfVsyncPhaseOffsetNs = vsync_sf_event_phase_offset_ns(1000000);
-
-    char value[PROPERTY_VALUE_MAX];
-    property_get("debug.sf.early_phase_offset_ns", value, "-1");
-    const int earlySfOffsetNs = atoi(value);
-
-    property_get("debug.sf.early_gl_phase_offset_ns", value, "-1");
-    const int earlyGlSfOffsetNs = atoi(value);
-
-    property_get("debug.sf.early_app_phase_offset_ns", value, "-1");
-    const int earlyAppOffsetNs = atoi(value);
-
-    property_get("debug.sf.early_gl_app_phase_offset_ns", value, "-1");
-    const int earlyGlAppOffsetNs = atoi(value);
-
-    property_get("debug.sf.high_fps_early_phase_offset_ns", value, "-1");
-    const int highFpsEarlySfOffsetNs = atoi(value);
-
-    property_get("debug.sf.high_fps_early_gl_phase_offset_ns", value, "-1");
-    const int highFpsEarlyGlSfOffsetNs = atoi(value);
-
-    property_get("debug.sf.high_fps_early_app_phase_offset_ns", value, "-1");
-    const int highFpsEarlyAppOffsetNs = atoi(value);
-
-    property_get("debug.sf.high_fps_early_gl_app_phase_offset_ns", value, "-1");
-    const int highFpsEarlyGlAppOffsetNs = atoi(value);
-
-    // TODO(b/122905996): Define these in device.mk.
-    property_get("debug.sf.high_fps_late_app_phase_offset_ns", value, "2000000");
-    const int highFpsLateAppOffsetNs = atoi(value);
-
-    property_get("debug.sf.high_fps_late_sf_phase_offset_ns", value, "1000000");
-    const int highFpsLateSfOffsetNs = atoi(value);
-
     // Below defines the threshold when an offset is considered to be negative, i.e. targeting
     // for the N+2 vsync instead of N+1. This means that:
     // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync.
     // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
-    property_get("debug.sf.phase_offset_threshold_for_next_vsync_ns", value, "-1");
-    const int phaseOffsetThresholdForNextVsyncNs = atoi(value);
+    const nsecs_t thresholdForNextVsync =
+            getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+                    .value_or(std::numeric_limits<nsecs_t>::max());
 
-    Offsets defaultOffsets;
-    Offsets highFpsOffsets;
-    defaultOffsets.early = {RefreshRateType::DEFAULT,
-                            earlySfOffsetNs != -1 ? earlySfOffsetNs : sfVsyncPhaseOffsetNs,
-                            earlyAppOffsetNs != -1 ? earlyAppOffsetNs : vsyncPhaseOffsetNs};
-    defaultOffsets.earlyGl = {RefreshRateType::DEFAULT,
-                              earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs : sfVsyncPhaseOffsetNs,
-                              earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs : vsyncPhaseOffsetNs};
-    defaultOffsets.late = {RefreshRateType::DEFAULT, sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs};
-
-    highFpsOffsets.early = {RefreshRateType::PERFORMANCE,
-                            highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs
-                                                         : highFpsLateSfOffsetNs,
-                            highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs
-                                                          : highFpsLateAppOffsetNs};
-    highFpsOffsets.earlyGl = {RefreshRateType::PERFORMANCE,
-                              highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs
-                                                             : highFpsLateSfOffsetNs,
-                              highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs
-                                                              : highFpsLateAppOffsetNs};
-    highFpsOffsets.late = {RefreshRateType::PERFORMANCE, highFpsLateSfOffsetNs,
-                           highFpsLateAppOffsetNs};
+    const Offsets defaultOffsets = getDefaultOffsets(thresholdForNextVsync);
+    const Offsets highFpsOffsets = getHighFpsOffsets(thresholdForNextVsync);
 
     mOffsets.insert({RefreshRateType::POWER_SAVING, defaultOffsets});
     mOffsets.insert({RefreshRateType::DEFAULT, defaultOffsets});
     mOffsets.insert({RefreshRateType::PERFORMANCE, highFpsOffsets});
-
-    mOffsetThresholdForNextVsync = phaseOffsetThresholdForNextVsyncNs != -1
-            ? phaseOffsetThresholdForNextVsyncNs
-            : std::numeric_limits<nsecs_t>::max();
 }
 
 PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(
-        android::scheduler::RefreshRateConfigs::RefreshRateType refreshRateType) const {
+        RefreshRateType refreshRateType) const {
     return mOffsets.at(refreshRateType);
 }
 
 void PhaseOffsets::dump(std::string& result) const {
-    const auto [early, earlyGl, late] = getCurrentOffsets();
+    const auto [early, earlyGl, late, threshold] = getCurrentOffsets();
     base::StringAppendF(&result,
                         "         app phase: %9" PRId64 " ns\t         SF phase: %9" PRId64 " ns\n"
                         "   early app phase: %9" PRId64 " ns\t   early SF phase: %9" PRId64 " ns\n"
-                        "GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n",
-                        late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf);
+                        "GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n"
+                        "threshold for next VSYNC: %" PRId64 " ns\n",
+                        late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf, threshold);
 }
 
-nsecs_t PhaseOffsets::getCurrentAppOffset() {
-    return getCurrentOffsets().late.app;
+PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t thresholdForNextVsync) {
+    const int64_t vsyncPhaseOffsetNs = sysprop::vsync_event_phase_offset_ns(1000000);
+    const int64_t sfVsyncPhaseOffsetNs = sysprop::vsync_sf_event_phase_offset_ns(1000000);
+
+    const auto earlySfOffsetNs = getProperty("debug.sf.early_phase_offset_ns");
+    const auto earlyGlSfOffsetNs = getProperty("debug.sf.early_gl_phase_offset_ns");
+    const auto earlyAppOffsetNs = getProperty("debug.sf.early_app_phase_offset_ns");
+    const auto earlyGlAppOffsetNs = getProperty("debug.sf.early_gl_app_phase_offset_ns");
+
+    return {{RefreshRateType::DEFAULT, earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
+             earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs)},
+
+            {RefreshRateType::DEFAULT, earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
+             earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs)},
+
+            {RefreshRateType::DEFAULT, sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs},
+
+            thresholdForNextVsync};
 }
 
-nsecs_t PhaseOffsets::getCurrentSfOffset() {
-    return getCurrentOffsets().late.sf;
+PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t thresholdForNextVsync) {
+    // TODO(b/122905996): Define these in device.mk.
+    const int highFpsLateAppOffsetNs =
+            getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000);
+    const int highFpsLateSfOffsetNs =
+            getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000);
+
+    const auto highFpsEarlySfOffsetNs = getProperty("debug.sf.high_fps_early_phase_offset_ns");
+    const auto highFpsEarlyGlSfOffsetNs = getProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
+    const auto highFpsEarlyAppOffsetNs = getProperty("debug.sf.high_fps_early_app_phase_offset_ns");
+    const auto highFpsEarlyGlAppOffsetNs =
+            getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
+
+    return {{RefreshRateType::PERFORMANCE, highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs),
+             highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs)},
+
+            {RefreshRateType::PERFORMANCE, highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs),
+             highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs)},
+
+            {RefreshRateType::PERFORMANCE, highFpsLateSfOffsetNs, highFpsLateAppOffsetNs},
+
+            thresholdForNextVsync};
 }
 
 } // namespace impl
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index 2b5c2f1..2c52432 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -16,14 +16,12 @@
 
 #pragma once
 
-#include <cinttypes>
 #include <unordered_map>
 
 #include "RefreshRateConfigs.h"
 #include "VSyncModulator.h"
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
 
 /*
  * This class encapsulates offsets for different refresh rates. Depending
@@ -33,35 +31,33 @@
  */
 class PhaseOffsets {
 public:
-    struct Offsets {
-        VSyncModulator::Offsets early;
-        VSyncModulator::Offsets earlyGl;
-        VSyncModulator::Offsets late;
-    };
+    using Offsets = VSyncModulator::OffsetsConfig;
+    using RefreshRateType = RefreshRateConfigs::RefreshRateType;
 
     virtual ~PhaseOffsets();
 
-    virtual nsecs_t getCurrentAppOffset() = 0;
-    virtual nsecs_t getCurrentSfOffset() = 0;
-    virtual Offsets getOffsetsForRefreshRate(
-            RefreshRateConfigs::RefreshRateType refreshRateType) const = 0;
+    nsecs_t getCurrentAppOffset() const { return getCurrentOffsets().late.app; }
+    nsecs_t getCurrentSfOffset() const { return getCurrentOffsets().late.sf; }
+    nsecs_t getOffsetThresholdForNextVsync() const {
+        return getCurrentOffsets().thresholdForNextVsync;
+    }
+
     virtual Offsets getCurrentOffsets() const = 0;
-    virtual void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) = 0;
-    virtual nsecs_t getOffsetThresholdForNextVsync() const = 0;
+    virtual Offsets getOffsetsForRefreshRate(RefreshRateType) const = 0;
+
+    virtual void setRefreshRateType(RefreshRateType) = 0;
+
     virtual void dump(std::string& result) const = 0;
 };
 
 namespace impl {
+
 class PhaseOffsets : public scheduler::PhaseOffsets {
 public:
     PhaseOffsets();
 
-    nsecs_t getCurrentAppOffset() override;
-    nsecs_t getCurrentSfOffset() override;
-
     // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
-    Offsets getOffsetsForRefreshRate(
-            RefreshRateConfigs::RefreshRateType refreshRateType) const override;
+    Offsets getOffsetsForRefreshRate(RefreshRateType) const override;
 
     // Returns early, early GL, and late offsets for Apps and SF.
     Offsets getCurrentOffsets() const override {
@@ -70,23 +66,21 @@
 
     // This function should be called when the device is switching between different
     // refresh rates, to properly update the offsets.
-    void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) override {
+    void setRefreshRateType(RefreshRateType refreshRateType) override {
         mRefreshRateType = refreshRateType;
     }
 
-    nsecs_t getOffsetThresholdForNextVsync() const override { return mOffsetThresholdForNextVsync; }
-
     // Returns current offsets in human friendly format.
     void dump(std::string& result) const override;
 
 private:
-    std::atomic<RefreshRateConfigs::RefreshRateType> mRefreshRateType =
-            RefreshRateConfigs::RefreshRateType::DEFAULT;
+    static Offsets getDefaultOffsets(nsecs_t thresholdForNextVsync);
+    static Offsets getHighFpsOffsets(nsecs_t thresholdForNextVsync);
 
-    std::unordered_map<RefreshRateConfigs::RefreshRateType, Offsets> mOffsets;
-    nsecs_t mOffsetThresholdForNextVsync;
+    std::atomic<RefreshRateType> mRefreshRateType = RefreshRateType::DEFAULT;
+
+    std::unordered_map<RefreshRateType, Offsets> mOffsets;
 };
-} // namespace impl
 
-} // namespace scheduler
-} // namespace android
+} // namespace impl
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 7acb470..952643c 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -89,12 +89,13 @@
         if (mSupportKernelTimer) {
             mIdleTimer = std::make_unique<scheduler::OneShotTimer>(
                     std::chrono::milliseconds(mSetIdleTimerMs),
-                    [this] { resetKernelTimerCallback(); },
-                    [this] { expiredKernelTimerCallback(); });
+                    [this] { kernelIdleTimerCallback(TimerState::Reset); },
+                    [this] { kernelIdleTimerCallback(TimerState::Expired); });
         } else {
             mIdleTimer = std::make_unique<scheduler::OneShotTimer>(
-                    std::chrono::milliseconds(mSetIdleTimerMs), [this] { resetTimerCallback(); },
-                    [this] { expiredTimerCallback(); });
+                    std::chrono::milliseconds(mSetIdleTimerMs),
+                    [this] { idleTimerCallback(TimerState::Reset); },
+                    [this] { idleTimerCallback(TimerState::Expired); });
         }
         mIdleTimer->start();
     }
@@ -102,16 +103,17 @@
     if (mSetTouchTimerMs > 0) {
         // Touch events are coming to SF every 100ms, so the timer needs to be higher than that
         mTouchTimer = std::make_unique<scheduler::OneShotTimer>(
-                std::chrono::milliseconds(mSetTouchTimerMs), [this] { resetTouchTimerCallback(); },
-                [this] { expiredTouchTimerCallback(); });
+                std::chrono::milliseconds(mSetTouchTimerMs),
+                [this] { touchTimerCallback(TimerState::Reset); },
+                [this] { touchTimerCallback(TimerState::Expired); });
         mTouchTimer->start();
     }
 
     if (mSetDisplayPowerTimerMs > 0) {
         mDisplayPowerTimer = std::make_unique<scheduler::OneShotTimer>(
                 std::chrono::milliseconds(mSetDisplayPowerTimerMs),
-                [this] { resetDisplayPowerTimerCallback(); },
-                [this] { expiredDisplayPowerTimerCallback(); });
+                [this] { displayPowerTimerCallback(TimerState::Reset); },
+                [this] { displayPowerTimerCallback(TimerState::Expired); });
         mDisplayPowerTimer->start();
     }
 }
@@ -363,23 +365,22 @@
     RefreshRateType newRefreshRateType;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        if (mContentRefreshRate == refreshRateRound && mIsHDRContent == isHDR) {
+        if (mFeatures.contentRefreshRate == refreshRateRound && mFeatures.isHDRContent == isHDR) {
             return;
         }
-        mContentRefreshRate = refreshRateRound;
-        ATRACE_INT("ContentFPS", mContentRefreshRate);
+        mFeatures.contentRefreshRate = refreshRateRound;
+        ATRACE_INT("ContentFPS", refreshRateRound);
 
-        mIsHDRContent = isHDR;
-        ATRACE_INT("ContentHDR", mIsHDRContent);
+        mFeatures.isHDRContent = isHDR;
+        ATRACE_INT("ContentHDR", isHDR);
 
-        mCurrentContentFeatureState = refreshRateRound > 0
-                ? ContentFeatureState::CONTENT_DETECTION_ON
-                : ContentFeatureState::CONTENT_DETECTION_OFF;
+        mFeatures.contentDetection =
+                refreshRateRound > 0 ? ContentDetectionState::On : ContentDetectionState::Off;
         newRefreshRateType = calculateRefreshRateType();
-        if (mRefreshRateType == newRefreshRateType) {
+        if (mFeatures.refreshRateType == newRefreshRateType) {
             return;
         }
-        mRefreshRateType = newRefreshRateType;
+        mFeatures.refreshRateType = newRefreshRateType;
     }
     changeRefreshRate(newRefreshRateType, ConfigEvent::Changed);
 }
@@ -433,7 +434,7 @@
 void Scheduler::setDisplayPowerState(bool normal) {
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        mIsDisplayPowerStateNormal = normal;
+        mFeatures.isDisplayPowerStateNormal = normal;
     }
 
     if (mDisplayPowerTimer) {
@@ -445,60 +446,41 @@
     mLayerHistory.clearHistory();
 }
 
-void Scheduler::resetTimerCallback() {
-    handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::RESET, false);
-    ATRACE_INT("ExpiredIdleTimer", 0);
-}
+void Scheduler::kernelIdleTimerCallback(TimerState state) {
+    ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
 
-void Scheduler::resetKernelTimerCallback() {
-    ATRACE_INT("ExpiredKernelIdleTimer", 0);
     std::lock_guard<std::mutex> lock(mCallbackLock);
-    if (mGetVsyncPeriod && mGetCurrentRefreshRateTypeCallback) {
+    if (!mGetCurrentRefreshRateTypeCallback || !mGetVsyncPeriod) return;
+
+    const auto type = mGetCurrentRefreshRateTypeCallback();
+    if (state == TimerState::Reset && type == RefreshRateType::PERFORMANCE) {
         // If we're not in performance mode then the kernel timer shouldn't do
         // anything, as the refresh rate during DPU power collapse will be the
         // same.
-        if (mGetCurrentRefreshRateTypeCallback() == Scheduler::RefreshRateType::PERFORMANCE) {
-            resyncToHardwareVsync(true, mGetVsyncPeriod());
-        }
+        resyncToHardwareVsync(true /* makeAvailable */, mGetVsyncPeriod());
+    } else if (state == TimerState::Expired && type != RefreshRateType::PERFORMANCE) {
+        // Disable HW VSYNC if the timer expired, as we don't need it enabled if
+        // we're not pushing frames, and if we're in PERFORMANCE mode then we'll
+        // need to update the DispSync model anyway.
+        disableHardwareVsync(false /* makeUnavailable */);
     }
 }
 
-void Scheduler::expiredTimerCallback() {
-    handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::EXPIRED, false);
-    ATRACE_INT("ExpiredIdleTimer", 1);
+void Scheduler::idleTimerCallback(TimerState state) {
+    handleTimerStateChanged(&mFeatures.idleTimer, state, false /* eventOnContentDetection */);
+    ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state));
 }
 
-void Scheduler::resetTouchTimerCallback() {
-    handleTimerStateChanged(&mCurrentTouchState, TouchState::ACTIVE, true);
-    ATRACE_INT("TouchState", 1);
+void Scheduler::touchTimerCallback(TimerState state) {
+    const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive;
+    handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */);
+    ATRACE_INT("TouchState", static_cast<int>(touch));
 }
 
-void Scheduler::expiredTouchTimerCallback() {
-    handleTimerStateChanged(&mCurrentTouchState, TouchState::INACTIVE, true);
-    ATRACE_INT("TouchState", 0);
-}
-
-void Scheduler::resetDisplayPowerTimerCallback() {
-    handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::RESET, true);
-    ATRACE_INT("ExpiredDisplayPowerTimer", 0);
-}
-
-void Scheduler::expiredDisplayPowerTimerCallback() {
-    handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::EXPIRED, true);
-    ATRACE_INT("ExpiredDisplayPowerTimer", 1);
-}
-
-void Scheduler::expiredKernelTimerCallback() {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    ATRACE_INT("ExpiredKernelIdleTimer", 1);
-    if (mGetCurrentRefreshRateTypeCallback) {
-        if (mGetCurrentRefreshRateTypeCallback() != Scheduler::RefreshRateType::PERFORMANCE) {
-            // Disable HW Vsync if the timer expired, as we don't need it
-            // enabled if we're not pushing frames, and if we're in PERFORMANCE
-            // mode then we'll need to re-update the DispSync model anyways.
-            disableHardwareVsync(false);
-        }
-    }
+void Scheduler::displayPowerTimerCallback(TimerState state) {
+    handleTimerStateChanged(&mFeatures.displayPowerTimer, state,
+                            true /* eventOnContentDetection */);
+    ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
 }
 
 std::string Scheduler::doDump() {
@@ -519,12 +501,11 @@
         }
         *currentState = newState;
         newRefreshRateType = calculateRefreshRateType();
-        if (mRefreshRateType == newRefreshRateType) {
+        if (mFeatures.refreshRateType == newRefreshRateType) {
             return;
         }
-        mRefreshRateType = newRefreshRateType;
-        if (eventOnContentDetection &&
-            mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) {
+        mFeatures.refreshRateType = newRefreshRateType;
+        if (eventOnContentDetection && mFeatures.contentDetection == ContentDetectionState::On) {
             event = ConfigEvent::Changed;
         }
     }
@@ -533,37 +514,38 @@
 
 Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
     // HDR content is not supported on PERFORMANCE mode
-    if (mForceHDRContentToDefaultRefreshRate && mIsHDRContent) {
+    if (mForceHDRContentToDefaultRefreshRate && mFeatures.isHDRContent) {
         return RefreshRateType::DEFAULT;
     }
 
     // If Display Power is not in normal operation we want to be in performance mode.
     // When coming back to normal mode, a grace period is given with DisplayPowerTimer
-    if (!mIsDisplayPowerStateNormal || mDisplayPowerTimerState == DisplayPowerTimerState::RESET) {
+    if (!mFeatures.isDisplayPowerStateNormal || mFeatures.displayPowerTimer == TimerState::Reset) {
         return RefreshRateType::PERFORMANCE;
     }
 
     // As long as touch is active we want to be in performance mode
-    if (mCurrentTouchState == TouchState::ACTIVE) {
+    if (mFeatures.touch == TouchState::Active) {
         return RefreshRateType::PERFORMANCE;
     }
 
     // If timer has expired as it means there is no new content on the screen
-    if (mCurrentIdleTimerState == IdleTimerState::EXPIRED) {
+    if (mFeatures.idleTimer == TimerState::Expired) {
         return RefreshRateType::DEFAULT;
     }
 
     // If content detection is off we choose performance as we don't know the content fps
-    if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_OFF) {
+    if (mFeatures.contentDetection == ContentDetectionState::Off) {
         return RefreshRateType::PERFORMANCE;
     }
 
     // Content detection is on, find the appropriate refresh rate with minimal error
+    const float rate = static_cast<float>(mFeatures.contentRefreshRate);
     auto iter = min_element(mRefreshRateConfigs.getRefreshRates().cbegin(),
                             mRefreshRateConfigs.getRefreshRates().cend(),
-                            [rate = mContentRefreshRate](const auto& l, const auto& r) -> bool {
-                                return std::abs(l.second->fps - static_cast<float>(rate)) <
-                                        std::abs(r.second->fps - static_cast<float>(rate));
+                            [rate](const auto& lhs, const auto& rhs) -> bool {
+                                return std::abs(lhs.second->fps - rate) <
+                                        std::abs(rhs.second->fps - rate);
                             });
     RefreshRateType currRefreshRateType = iter->first;
 
@@ -571,11 +553,10 @@
     // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
     // align well with both
     constexpr float MARGIN = 0.05f;
-    float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps /
-            float(mContentRefreshRate);
+    float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps / rate;
     if (std::abs(std::round(ratio) - ratio) > MARGIN) {
         while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
-            ratio = iter->second->fps / float(mContentRefreshRate);
+            ratio = iter->second->fps / rate;
 
             if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
                 currRefreshRateType = iter->first;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 123036e..0d9d7aa 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -200,10 +200,9 @@
 
     // 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.
-    enum class ContentFeatureState { CONTENT_DETECTION_ON, CONTENT_DETECTION_OFF };
-    enum class IdleTimerState { EXPIRED, RESET };
-    enum class TouchState { INACTIVE, ACTIVE };
-    enum class DisplayPowerTimerState { EXPIRED, RESET };
+    enum class ContentDetectionState { Off, On };
+    enum class TimerState { Reset, Expired };
+    enum class TouchState { Inactive, Active };
 
     // Creates a connection on the given EventThread and forwards the given callbacks.
     sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&,
@@ -212,26 +211,12 @@
     nsecs_t calculateAverage() const;
     void updateFrameSkipping(const int64_t skipCount);
 
-    // Function that is called when the timer resets.
-    void resetTimerCallback();
-    // Function that is called when the timer expires.
-    void expiredTimerCallback();
-    // Function that is called when the timer resets when paired with a display
-    // driver timeout in the kernel. This enables hardware vsync when we move
-    // out from idle.
-    void resetKernelTimerCallback();
-    // Function that is called when the timer expires when paired with a display
-    // driver timeout in the kernel. This disables hardware vsync when we move
-    // into idle.
-    void expiredKernelTimerCallback();
-    // Function that is called when the touch timer resets.
-    void resetTouchTimerCallback();
-    // Function that is called when the touch timer expires.
-    void expiredTouchTimerCallback();
-    // Function that is called when the display power timer resets.
-    void resetDisplayPowerTimerCallback();
-    // Function that is called when the display power timer expires.
-    void expiredDisplayPowerTimerCallback();
+    // Update feature state machine to given state when corresponding timer resets or expires.
+    void kernelIdleTimerCallback(TimerState);
+    void idleTimerCallback(TimerState);
+    void touchTimerCallback(TimerState);
+    void displayPowerTimerCallback(TimerState);
+
     // Sets vsync period.
     void setVsyncPeriod(const nsecs_t period);
     // handles various timer features to change the refresh rate.
@@ -304,16 +289,19 @@
     // 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;
-    ContentFeatureState mCurrentContentFeatureState GUARDED_BY(mFeatureStateLock) =
-            ContentFeatureState::CONTENT_DETECTION_OFF;
-    IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET;
-    TouchState mCurrentTouchState GUARDED_BY(mFeatureStateLock) = TouchState::INACTIVE;
-    DisplayPowerTimerState mDisplayPowerTimerState GUARDED_BY(mFeatureStateLock) =
-            DisplayPowerTimerState::EXPIRED;
-    uint32_t mContentRefreshRate GUARDED_BY(mFeatureStateLock);
-    RefreshRateType mRefreshRateType GUARDED_BY(mFeatureStateLock);
-    bool mIsHDRContent GUARDED_BY(mFeatureStateLock) = false;
-    bool mIsDisplayPowerStateNormal GUARDED_BY(mFeatureStateLock) = true;
+
+    struct {
+        ContentDetectionState contentDetection = ContentDetectionState::Off;
+        TimerState idleTimer = TimerState::Reset;
+        TouchState touch = TouchState::Inactive;
+        TimerState displayPowerTimer = TimerState::Expired;
+
+        RefreshRateType refreshRateType = RefreshRateType::DEFAULT;
+        uint32_t contentRefreshRate = 0;
+
+        bool isHDRContent = false;
+        bool isDisplayPowerStateNormal = true;
+    } mFeatures GUARDED_BY(mFeatureStateLock);
 
     const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
 
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
index 7a3bf8e..f267c99 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
@@ -24,25 +24,24 @@
 #include <cinttypes>
 #include <mutex>
 
-namespace android {
+namespace android::scheduler {
 
-using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
-VSyncModulator::VSyncModulator() {
+VSyncModulator::VSyncModulator(Scheduler& scheduler,
+                               const sp<Scheduler::ConnectionHandle>& appConnectionHandle,
+                               const sp<Scheduler::ConnectionHandle>& sfConnectionHandle,
+                               const OffsetsConfig& config)
+      : mScheduler(scheduler),
+        mAppConnectionHandle(appConnectionHandle),
+        mSfConnectionHandle(sfConnectionHandle),
+        mOffsetsConfig(config) {
     char value[PROPERTY_VALUE_MAX];
     property_get("debug.sf.vsync_trace_detailed_info", value, "0");
     mTraceDetailedInfo = atoi(value);
-    // Populate the offset map with some default offsets.
-    const Offsets defaultOffsets = {RefreshRateType::DEFAULT, 0, 0};
-    setPhaseOffsets(defaultOffsets, defaultOffsets, defaultOffsets, 0);
 }
 
-void VSyncModulator::setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late,
-                                     nsecs_t thresholdForNextVsync) {
+void VSyncModulator::setPhaseOffsets(const OffsetsConfig& config) {
     std::lock_guard<std::mutex> lock(mMutex);
-    mOffsetMap.insert_or_assign(OffsetType::Early, early);
-    mOffsetMap.insert_or_assign(OffsetType::EarlyGl, earlyGl);
-    mOffsetMap.insert_or_assign(OffsetType::Late, late);
-    mThresholdForNextVsync = thresholdForNextVsync;
+    mOffsetsConfig = config;
     updateOffsetsLocked();
 }
 
@@ -100,25 +99,21 @@
     }
 }
 
-VSyncModulator::Offsets VSyncModulator::getOffsets() {
+VSyncModulator::Offsets VSyncModulator::getOffsets() const {
     std::lock_guard<std::mutex> lock(mMutex);
     return mOffsets;
 }
 
-VSyncModulator::Offsets VSyncModulator::getNextOffsets() {
-    return mOffsetMap.at(getNextOffsetType());
-}
-
-VSyncModulator::OffsetType VSyncModulator::getNextOffsetType() {
+const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const {
     // Early offsets are used if we're in the middle of a refresh rate
     // change, or if we recently begin a transaction.
     if (mTransactionStart == Scheduler::TransactionStart::EARLY || mRemainingEarlyFrameCount > 0 ||
         mRefreshRateChangePending) {
-        return OffsetType::Early;
+        return mOffsetsConfig.early;
     } else if (mRemainingRenderEngineUsageCount > 0) {
-        return OffsetType::EarlyGl;
+        return mOffsetsConfig.earlyGl;
     } else {
-        return OffsetType::Late;
+        return mOffsetsConfig.late;
     }
 }
 
@@ -128,37 +123,29 @@
 }
 
 void VSyncModulator::updateOffsetsLocked() {
-    const Offsets desired = getNextOffsets();
+    const Offsets& offsets = getNextOffsets();
 
-    if (mSfConnectionHandle != nullptr) {
-        mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf);
-    }
+    mScheduler.setPhaseOffset(mSfConnectionHandle, offsets.sf);
+    mScheduler.setPhaseOffset(mAppConnectionHandle, offsets.app);
 
-    if (mAppConnectionHandle != nullptr) {
-        mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app);
-    }
+    mOffsets = offsets;
 
-    flushOffsets();
-}
-
-void VSyncModulator::flushOffsets() {
-    OffsetType type = getNextOffsetType();
-    mOffsets = mOffsetMap.at(type);
     if (!mTraceDetailedInfo) {
         return;
     }
-    ATRACE_INT("Vsync-EarlyOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::Early);
-    ATRACE_INT("Vsync-EarlyGLOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::EarlyGl);
-    ATRACE_INT("Vsync-LateOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::Late);
-    ATRACE_INT("Vsync-HighFpsEarlyOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::Early);
-    ATRACE_INT("Vsync-HighFpsEarlyGLOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::EarlyGl);
-    ATRACE_INT("Vsync-HighFpsLateOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::Late);
+
+    const bool isDefault = mOffsets.fpsMode == RefreshRateType::DEFAULT;
+    const bool isPerformance = mOffsets.fpsMode == RefreshRateType::PERFORMANCE;
+    const bool isEarly = &offsets == &mOffsetsConfig.early;
+    const bool isEarlyGl = &offsets == &mOffsetsConfig.earlyGl;
+    const bool isLate = &offsets == &mOffsetsConfig.late;
+
+    ATRACE_INT("Vsync-EarlyOffsetsOn", isDefault && isEarly);
+    ATRACE_INT("Vsync-EarlyGLOffsetsOn", isDefault && isEarlyGl);
+    ATRACE_INT("Vsync-LateOffsetsOn", isDefault && isLate);
+    ATRACE_INT("Vsync-HighFpsEarlyOffsetsOn", isPerformance && isEarly);
+    ATRACE_INT("Vsync-HighFpsEarlyGLOffsetsOn", isPerformance && isEarlyGl);
+    ATRACE_INT("Vsync-HighFpsLateOffsetsOn", isPerformance && isLate);
 }
 
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index ddbd221..636c8c8 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -16,12 +16,11 @@
 
 #pragma once
 
-#include <cinttypes>
 #include <mutex>
 
 #include "Scheduler.h"
 
-namespace android {
+namespace android::scheduler {
 
 /*
  * Modulates the vsync-offsets depending on current SurfaceFlinger state.
@@ -31,51 +30,36 @@
     // Number of frames we'll keep the early phase offsets once they are activated for a
     // transaction. This acts as a low-pass filter in case the client isn't quick enough in
     // sending new transactions.
-    const int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2;
+    static constexpr int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2;
 
     // Number of frames we'll keep the early gl phase offsets once they are activated.
     // This acts as a low-pass filter to avoid scenarios where we rapidly
     // switch in and out of gl composition.
-    const int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
+    static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
+
+    using RefreshRateType = RefreshRateConfigs::RefreshRateType;
 
 public:
-    VSyncModulator();
-
     // Wrapper for a collection of surfaceflinger/app offsets for a particular
-    // configuration .
+    // configuration.
     struct Offsets {
-        scheduler::RefreshRateConfigs::RefreshRateType fpsMode;
+        RefreshRateType fpsMode;
         nsecs_t sf;
         nsecs_t app;
     };
 
-    enum class OffsetType {
-        Early,
-        EarlyGl,
-        Late,
+    struct OffsetsConfig {
+        Offsets early;   // For transactions with the eEarlyWakeup flag.
+        Offsets earlyGl; // As above but while compositing with GL.
+        Offsets late;    // Default.
+
+        nsecs_t thresholdForNextVsync;
     };
 
-    // Sets the phase offsets
-    //
-    // sfEarly: The phase offset when waking up SF early, which happens when marking a transaction
-    //          as early. May be the same as late, in which case we don't shift offsets.
-    // sfEarlyGl: Like sfEarly, but only if we used GL composition. If we use both GL composition
-    //            and the transaction was marked as early, we'll use sfEarly.
-    // sfLate: The regular SF vsync phase offset.
-    // appEarly: Like sfEarly, but for the app-vsync
-    // appEarlyGl: Like sfEarlyGl, but for the app-vsync.
-    // appLate: The regular app vsync phase offset.
-    void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late,
-                         nsecs_t thresholdForNextVsync) EXCLUDES(mMutex);
+    VSyncModulator(Scheduler&, const sp<Scheduler::ConnectionHandle>& appConnectionHandle,
+                   const sp<Scheduler::ConnectionHandle>& sfConnectionHandle, const OffsetsConfig&);
 
-    // Sets the scheduler and vsync connection handlers.
-    void setSchedulerAndHandles(Scheduler* scheduler,
-                                Scheduler::ConnectionHandle* appConnectionHandle,
-                                Scheduler::ConnectionHandle* sfConnectionHandle) {
-        mScheduler = scheduler;
-        mAppConnectionHandle = appConnectionHandle;
-        mSfConnectionHandle = sfConnectionHandle;
-    }
+    void setPhaseOffsets(const OffsetsConfig&) EXCLUDES(mMutex);
 
     // Signals that a transaction has started, and changes offsets accordingly.
     void setTransactionStart(Scheduler::TransactionStart transactionStart);
@@ -98,28 +82,23 @@
     void onRefreshed(bool usedRenderEngine);
 
     // Returns the offsets that we are currently using
-    Offsets getOffsets() EXCLUDES(mMutex);
+    Offsets getOffsets() const EXCLUDES(mMutex);
 
 private:
     // Returns the next offsets that we should be using
-    Offsets getNextOffsets() REQUIRES(mMutex);
-    // Returns the next offset type that we should use.
-    OffsetType getNextOffsetType();
+    const Offsets& getNextOffsets() const REQUIRES(mMutex);
     // Updates offsets and persists them into the scheduler framework.
     void updateOffsets() EXCLUDES(mMutex);
     void updateOffsetsLocked() REQUIRES(mMutex);
-    // Updates the internal offsets and offset type.
-    void flushOffsets() REQUIRES(mMutex);
+
+    Scheduler& mScheduler;
+    const sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
+    const sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
 
     mutable std::mutex mMutex;
-    std::unordered_map<OffsetType, Offsets> mOffsetMap GUARDED_BY(mMutex);
-    nsecs_t mThresholdForNextVsync;
+    OffsetsConfig mOffsetsConfig GUARDED_BY(mMutex);
 
-    Scheduler* mScheduler = nullptr;
-    Scheduler::ConnectionHandle* mAppConnectionHandle = nullptr;
-    Scheduler::ConnectionHandle* mSfConnectionHandle = nullptr;
-
-    Offsets mOffsets GUARDED_BY(mMutex) = {Scheduler::RefreshRateType::DEFAULT, 0, 0};
+    Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late};
 
     std::atomic<Scheduler::TransactionStart> mTransactionStart =
             Scheduler::TransactionStart::NORMAL;
@@ -130,4 +109,4 @@
     bool mTraceDetailedInfo = false;
 };
 
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 99440a6..7345e8d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -38,6 +38,7 @@
 #include <binder/PermissionCache.h>
 
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/Display.h>
 #include <compositionengine/DisplayColorProfile.h>
 #include <compositionengine/Layer.h>
@@ -162,28 +163,6 @@
     return false;
 }
 
-bool isHdrColorMode(const ColorMode colorMode) {
-    switch (colorMode) {
-        case ColorMode::BT2100_PQ:
-        case ColorMode::BT2100_HLG:
-            return true;
-        case ColorMode::DISPLAY_P3:
-        case ColorMode::ADOBE_RGB:
-        case ColorMode::DCI_P3:
-        case ColorMode::BT2020:
-        case ColorMode::DISPLAY_BT2020:
-        case ColorMode::NATIVE:
-        case ColorMode::STANDARD_BT601_625:
-        case ColorMode::STANDARD_BT601_625_UNADJUSTED:
-        case ColorMode::STANDARD_BT601_525:
-        case ColorMode::STANDARD_BT601_525_UNADJUSTED:
-        case ColorMode::STANDARD_BT709:
-        case ColorMode::SRGB:
-            return false;
-    }
-    return false;
-}
-
 ui::Transform::orientation_flags fromSurfaceComposerRotation(ISurfaceComposer::Rotation rotation) {
     switch (rotation) {
         case ISurfaceComposer::eRotateNone:
@@ -276,11 +255,11 @@
 
 SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
       : mFactory(factory),
-        mPhaseOffsets(mFactory.createPhaseOffsets()),
         mInterceptor(mFactory.createSurfaceInterceptor(this)),
         mTimeStats(mFactory.createTimeStats()),
         mEventQueue(mFactory.createMessageQueue()),
-        mCompositionEngine(mFactory.createCompositionEngine()) {}
+        mCompositionEngine(mFactory.createCompositionEngine()),
+        mPhaseOffsets(mFactory.createPhaseOffsets()) {}
 
 SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
     ALOGI("SurfaceFlinger is starting");
@@ -386,10 +365,6 @@
     property_get("debug.sf.luma_sampling", value, "1");
     mLumaSampling = atoi(value);
 
-    const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
-    mVsyncModulator.setPhaseOffsets(early, gl, late,
-                                    mPhaseOffsets->getOffsetThresholdForNextVsync());
-
     // We should be reading 'persist.sys.sf.color_saturation' here
     // but since /data may be encrypted, we need to wait until after vold
     // comes online to attempt to read the property. The property is
@@ -616,7 +591,7 @@
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
 
-    ALOGI("Phase offset NS: %" PRId64 "", mPhaseOffsets->getCurrentAppOffset());
+    ALOGI("Phase offset: %" PRId64 " ns", mPhaseOffsets->getCurrentAppOffset());
 
     Mutex::Autolock _l(mStateLock);
     // start the EventThread
@@ -627,20 +602,21 @@
             mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
 
     mAppConnectionHandle =
-            mScheduler->createConnection("app", mVsyncModulator.getOffsets().app,
+            mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
                                          mPhaseOffsets->getOffsetThresholdForNextVsync(),
                                          resyncCallback,
                                          impl::EventThread::InterceptVSyncsCallback());
     mSfConnectionHandle =
-            mScheduler->createConnection("sf", mVsyncModulator.getOffsets().sf,
+            mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
                                          mPhaseOffsets->getOffsetThresholdForNextVsync(),
                                          resyncCallback, [this](nsecs_t timestamp) {
                                              mInterceptor->saveVSyncEvent(timestamp);
                                          });
 
     mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
-    mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(),
-                                           mSfConnectionHandle.get());
+
+    mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
+                            mPhaseOffsets->getCurrentOffsets());
 
     mRegionSamplingThread =
             new RegionSamplingThread(*this, *mScheduler,
@@ -703,7 +679,11 @@
     // set initial conditions (e.g. unblank default device)
     initializeDisplays();
 
-    getRenderEngine().primeCache();
+    char primeShaderCache[PROPERTY_VALUE_MAX];
+    property_get("service.sf.prime_shader_cache", primeShaderCache, "1");
+    if (atoi(primeShaderCache)) {
+        getRenderEngine().primeCache();
+    }
 
     // Inform native graphics APIs whether the present timestamp is supported:
 
@@ -969,14 +949,11 @@
         mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
         // As we called to set period, we will call to onRefreshRateChangeCompleted once
         // DispSync model is locked.
-        mVsyncModulator.onRefreshRateChangeInitiated();
+        mVSyncModulator->onRefreshRateChangeInitiated();
         mPhaseOffsets->setRefreshRateType(info.type);
-        const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
-        mVsyncModulator.setPhaseOffsets(early, gl, late,
-                                        mPhaseOffsets->getOffsetThresholdForNextVsync());
+        mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
     }
     mDesiredActiveConfigChanged = true;
-    ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
 
     if (mRefreshRateOverlay) {
         mRefreshRateOverlay->changeRefreshRate(mDesiredActiveConfig.type);
@@ -1006,9 +983,7 @@
     display->setActiveConfig(mUpcomingActiveConfig.configId);
 
     mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type);
-    const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
-    mVsyncModulator.setPhaseOffsets(early, gl, late,
-                                    mPhaseOffsets->getOffsetThresholdForNextVsync());
+    mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
     ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
 
     if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
@@ -1021,13 +996,10 @@
     std::lock_guard<std::mutex> lock(mActiveConfigLock);
     mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
     mDesiredActiveConfigChanged = false;
-    ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
 
     mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
     mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type);
-    const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
-    mVsyncModulator.setPhaseOffsets(early, gl, late,
-                                    mPhaseOffsets->getOffsetThresholdForNextVsync());
+    mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
 }
 
 bool SurfaceFlinger::performSetActiveConfig() {
@@ -1160,7 +1132,8 @@
                   decodeColorMode(mode).c_str(), mode);
         } else {
             display->getCompositionDisplay()->setColorMode(mode, Dataspace::UNKNOWN,
-                                                           RenderIntent::COLORIMETRIC);
+                                                           RenderIntent::COLORIMETRIC,
+                                                           Dataspace::UNKNOWN);
         }
     }));
 
@@ -1494,7 +1467,7 @@
     bool periodFlushed = false;
     mScheduler->addResyncSample(timestamp, &periodFlushed);
     if (periodFlushed) {
-        mVsyncModulator.onRefreshRateChangeCompleted();
+        mVSyncModulator->onRefreshRateChangeCompleted();
     }
 }
 
@@ -1686,22 +1659,22 @@
     // woken up before the actual vsync but targeting the next vsync, we need to check
     // fence N-2
     const sp<Fence>& fence =
-            mVsyncModulator.getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync()
+            mVSyncModulator->getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync()
             ? mPreviousPresentFences[0]
             : mPreviousPresentFences[1];
 
     return fence != Fence::NO_FENCE && (fence->getStatus() == Fence::Status::Unsignaled);
 }
 
-void SurfaceFlinger::populateExpectedPresentTime() NO_THREAD_SAFETY_ANALYSIS {
+void SurfaceFlinger::populateExpectedPresentTime() {
     DisplayStatInfo stats;
     mScheduler->getDisplayStatInfo(&stats);
     const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime();
     // Inflate the expected present time if we're targetting the next vsync.
-    mExpectedPresentTime =
-            mVsyncModulator.getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync()
-            ? presentTime
-            : presentTime + stats.vsyncPeriod;
+    mExpectedPresentTime.store(mVSyncModulator->getOffsets().sf <
+                                               mPhaseOffsets->getOffsetThresholdForNextVsync()
+                                       ? presentTime
+                                       : presentTime + stats.vsyncPeriod);
 }
 
 void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS {
@@ -1713,12 +1686,12 @@
             // seeing this same value.
             populateExpectedPresentTime();
 
-            bool frameMissed = previousFrameMissed();
-            bool hwcFrameMissed = mHadDeviceComposition && frameMissed;
-            bool gpuFrameMissed = mHadClientComposition && frameMissed;
-            ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
-            ATRACE_INT("HwcFrameMissed", static_cast<int>(hwcFrameMissed));
-            ATRACE_INT("GpuFrameMissed", static_cast<int>(gpuFrameMissed));
+            const TracedOrdinal<bool> frameMissed = {"FrameMissed", previousFrameMissed()};
+            const TracedOrdinal<bool> hwcFrameMissed = {"HwcFrameMissed",
+                                                        mHadDeviceComposition && frameMissed};
+            const TracedOrdinal<bool> gpuFrameMissed = {"GpuFrameMissed",
+                                                        mHadClientComposition && frameMissed};
+
             if (frameMissed) {
                 mFrameMissedCount++;
                 mTimeStats->incrementMissedFrames();
@@ -1804,8 +1777,17 @@
 
     mRefreshPending = false;
 
+    compositionengine::CompositionRefreshArgs refreshArgs;
+    for (const auto& [_, display] : mDisplays) {
+        refreshArgs.outputs.push_back(display->getCompositionDisplay());
+    }
+    mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
+        auto compositionLayer = layer->getCompositionLayer();
+        if (compositionLayer) refreshArgs.layers.push_back(compositionLayer);
+    });
+
     const bool repaintEverything = mRepaintEverything.exchange(false);
-    preComposition();
+    mCompositionEngine->preComposition(refreshArgs);
     rebuildLayerStacks();
     calculateWorkingSet();
     for (const auto& [token, display] : mDisplays) {
@@ -1831,7 +1813,7 @@
                 mHadDeviceComposition || getHwComposer().hasDeviceComposition(displayId);
     }
 
-    mVsyncModulator.onRefreshed(mHadClientComposition);
+    mVSyncModulator->onRefreshed(mHadClientComposition);
 
     mLayersWithQueuedFrames.clear();
     if (mVisibleRegionsDirty) {
@@ -1840,6 +1822,10 @@
             mTracing.notify("visibleRegionsDirty");
         }
     }
+
+    if (mCompositionEngine->needsAnotherUpdate()) {
+        signalLayerUpdate();
+    }
 }
 
 
@@ -1864,104 +1850,80 @@
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
 
-    // build the h/w work list
-    if (CC_UNLIKELY(mGeometryInvalid)) {
-        mGeometryInvalid = false;
+    const bool updatingGeometryThisFrame = mGeometryInvalid;
+    mGeometryInvalid = false;
+
+    {
+        // Use a map so that we latch the state of each front-end layer once.
+        std::unordered_map<compositionengine::LayerFE*, compositionengine::LayerFECompositionState*>
+                uniqueVisibleLayers;
+
+        // Figure out which frontend layers are being composed, and build the unique
+        // set of them (and the corresponding composition layer)
         for (const auto& [token, displayDevice] : mDisplays) {
             auto display = displayDevice->getCompositionDisplay();
+            for (auto& layer : display->getOutputLayersOrderedByZ()) {
+                uniqueVisibleLayers.insert(std::make_pair(&layer->getLayerFE(),
+                                                          &layer->getLayer().editState().frontEnd));
+            }
+        }
 
+        // Update the composition state from each front-end layer.
+        for (auto& [layerFE, state] : uniqueVisibleLayers) {
+            layerFE->latchCompositionState(*state, updatingGeometryThisFrame);
+        }
+    }
+
+    if (CC_UNLIKELY(updatingGeometryThisFrame)) {
+        for (const auto& [token, displayDevice] : mDisplays) {
+            auto display = displayDevice->getCompositionDisplay();
             uint32_t zOrder = 0;
 
             for (auto& layer : display->getOutputLayersOrderedByZ()) {
-                auto& compositionState = layer->editState();
-                compositionState.forceClientComposition = false;
-                if (!compositionState.hwc || mDebugDisableHWC || mDebugRegion) {
-                    compositionState.forceClientComposition = true;
-                }
-
-                // The output Z order is set here based on a simple counter.
-                compositionState.z = zOrder++;
-
-                // Update the display independent composition state. This goes
-                // to the general composition layer state structure.
-                // TODO: Do this once per compositionengine::CompositionLayer.
-                layer->getLayerFE().latchCompositionState(layer->getLayer().editState().frontEnd,
-                                                          true);
-
-                // Recalculate the geometry state of the output layer.
-                layer->updateCompositionState(true);
-
-                // Write the updated geometry state to the HWC
-                layer->writeStateToHWC(true);
+                // Assign a simple Z order sequence to each visible layer.
+                layer->editState().z = zOrder++;
             }
         }
     }
 
-    // Set the per-frame data
+    // Determine the color configuration of each output
     for (const auto& [token, displayDevice] : mDisplays) {
         auto display = displayDevice->getCompositionDisplay();
-        const auto displayId = display->getId();
-        if (!displayId) {
-            continue;
-        }
-        auto* profile = display->getDisplayColorProfile();
 
-        if (mDrawingState.colorMatrixChanged) {
+        ColorMode colorMode = ColorMode::NATIVE;
+        Dataspace dataspace = Dataspace::UNKNOWN;
+        RenderIntent renderIntent = RenderIntent::COLORIMETRIC;
+        if (useColorManagement) {
+            pickColorMode(displayDevice, &colorMode, &dataspace, &renderIntent);
+        }
+        display->setColorMode(colorMode, dataspace, renderIntent, mColorSpaceAgnosticDataspace);
+    }
+
+    for (const auto& [token, displayDevice] : mDisplays) {
+        auto display = displayDevice->getCompositionDisplay();
+
+        for (auto& layer : display->getOutputLayersOrderedByZ()) {
+            // Update the composition state of the output layer, as needed
+            // recomputing it from the state given by the front-end layer.
+            layer->updateCompositionState(updatingGeometryThisFrame);
+        }
+    }
+
+    for (const auto& [token, displayDevice] : mDisplays) {
+        auto display = displayDevice->getCompositionDisplay();
+
+        for (auto& layer : display->getOutputLayersOrderedByZ()) {
+            // Send the updated state to the HWC, if appropriate.
+            layer->writeStateToHWC(updatingGeometryThisFrame);
+        }
+    }
+
+    if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
+        for (const auto& [token, displayDevice] : mDisplays) {
+            auto display = displayDevice->getCompositionDisplay();
             display->setColorTransform(mDrawingState.colorMatrix);
         }
-        Dataspace targetDataspace = Dataspace::UNKNOWN;
-        if (useColorManagement) {
-            ColorMode colorMode;
-            RenderIntent renderIntent;
-            pickColorMode(displayDevice, &colorMode, &targetDataspace, &renderIntent);
-            display->setColorMode(colorMode, targetDataspace, renderIntent);
-
-            if (isHdrColorMode(colorMode)) {
-                targetDataspace = Dataspace::UNKNOWN;
-            } else if (mColorSpaceAgnosticDataspace != Dataspace::UNKNOWN) {
-                targetDataspace = mColorSpaceAgnosticDataspace;
-            }
-        }
-
-        for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-            if (layer->isHdrY410()) {
-                layer->forceClientComposition(displayDevice);
-            } else if ((layer->getDataSpace() == Dataspace::BT2020_PQ ||
-                        layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) &&
-                       !profile->hasHDR10Support()) {
-                layer->forceClientComposition(displayDevice);
-            } else if ((layer->getDataSpace() == Dataspace::BT2020_HLG ||
-                        layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) &&
-                       !profile->hasHLGSupport()) {
-                layer->forceClientComposition(displayDevice);
-            }
-
-            if (layer->getRoundedCornerState().radius > 0.0f) {
-                layer->forceClientComposition(displayDevice);
-            }
-
-            if (layer->getForceClientComposition(displayDevice)) {
-                ALOGV("[%s] Requesting Client composition", layer->getName().string());
-                layer->setCompositionType(displayDevice,
-                                          Hwc2::IComposerClient::Composition::CLIENT);
-                continue;
-            }
-
-            const auto& displayState = display->getState();
-            layer->setPerFrameData(displayDevice, displayState.transform, displayState.viewport,
-                                   displayDevice->getSupportedPerFrameMetadata(), targetDataspace);
-        }
-    }
-
-    mDrawingState.colorMatrixChanged = false;
-
-    for (const auto& [token, displayDevice] : mDisplays) {
-        auto display = displayDevice->getCompositionDisplay();
-        for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-            auto& layerState = layer->getCompositionLayer()->editState().frontEnd;
-            layerState.compositionType = static_cast<Hwc2::IComposerClient::Composition>(
-                    layer->getCompositionType(displayDevice));
-        }
+        mDrawingState.colorMatrixChanged = false;
     }
 }
 
@@ -2009,25 +1971,6 @@
     }
 }
 
-void SurfaceFlinger::preComposition()
-{
-    ATRACE_CALL();
-    ALOGV("preComposition");
-
-    mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
-
-    bool needExtraInvalidate = false;
-    mDrawingState.traverseInZOrder([&](Layer* layer) {
-        if (layer->onPreComposition(mRefreshStartTime)) {
-            needExtraInvalidate = true;
-        }
-    });
-
-    if (needExtraInvalidate) {
-        signalLayerUpdate();
-    }
-}
-
 void SurfaceFlinger::updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
                                             std::shared_ptr<FenceTime>& presentFenceTime) {
     // Update queue of past composite+present times and determine the
@@ -2121,10 +2064,11 @@
     DisplayStatInfo stats;
     mScheduler->getDisplayStatInfo(&stats);
 
-    // We use the mRefreshStartTime which might be sampled a little later than
-    // when we started doing work for this frame, but that should be okay
-    // since updateCompositorTiming has snapping logic.
-    updateCompositorTiming(stats, mRefreshStartTime, presentFenceTime);
+    // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
+    // be sampled a little later than when we started doing work for this frame,
+    // but that should be okay since updateCompositorTiming has snapping logic.
+    updateCompositorTiming(stats, mCompositionEngine->getLastFrameRefreshTimestamp(),
+                           presentFenceTime);
     CompositorTiming compositorTiming;
     {
         std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
@@ -2257,8 +2201,8 @@
             Region opaqueRegion;
             Region dirtyRegion;
             compositionengine::Output::OutputLayers layersSortedByZ;
+            compositionengine::Output::ReleasedLayers releasedLayers;
             Vector<sp<Layer>> deprecated_layersSortedByZ;
-            Vector<sp<Layer>> layersNeedingFences;
             const ui::Transform& tr = displayState.transform;
             const Rect bounds = displayState.bounds;
             if (displayState.isEnabled) {
@@ -2306,16 +2250,16 @@
                                                          layer) != mLayersWithQueuedFrames.cend();
 
                         if (hasExistingOutputLayer && hasQueuedFrames) {
-                            layersNeedingFences.add(layer);
+                            releasedLayers.push_back(layer);
                         }
                     }
                 });
             }
 
             display->setOutputLayersOrderedByZ(std::move(layersSortedByZ));
+            display->setReleasedLayers(std::move(releasedLayers));
 
             displayDevice->setVisibleLayersSortedByZ(deprecated_layersSortedByZ);
-            displayDevice->setLayersNeedingFences(layersNeedingFences);
 
             Region undefinedRegion{bounds};
             undefinedRegion.subtractSelf(tr.transform(opaqueRegion));
@@ -2358,7 +2302,8 @@
             case Dataspace::BT2020_ITU_PQ:
                 bestDataSpace = Dataspace::DISPLAY_P3;
                 *outHdrDataSpace = Dataspace::BT2020_PQ;
-                *outIsHdrClientComposition = layer->getForceClientComposition(display);
+                *outIsHdrClientComposition =
+                        layer->getCompositionLayer()->getState().frontEnd.forceClientComposition;
                 break;
             case Dataspace::BT2020_HLG:
             case Dataspace::BT2020_ITU_HLG:
@@ -2550,11 +2495,14 @@
         // We've got a list of layers needing fences, that are disjoint with
         // display->getVisibleLayersSortedByZ.  The best we can do is to
         // supply them with the present fence.
-        if (!displayDevice->getLayersNeedingFences().isEmpty()) {
+        auto releasedLayers = display->takeReleasedLayers();
+        if (!releasedLayers.empty()) {
             sp<Fence> presentFence =
                     displayId ? getHwComposer().getPresentFence(*displayId) : Fence::NO_FENCE;
-            for (auto& layer : displayDevice->getLayersNeedingFences()) {
-                layer->getCompositionLayer()->getLayerFE()->onLayerDisplayed(presentFence);
+            for (auto& weakLayer : releasedLayers) {
+                if (auto layer = weakLayer.promote(); layer != nullptr) {
+                    layer->onLayerDisplayed(presentFence);
+                }
             }
         }
 
@@ -2583,7 +2531,7 @@
     // with mStateLock held to guarantee that mCurrentState won't change
     // until the transaction is committed.
 
-    mVsyncModulator.onTransactionHandled();
+    mVSyncModulator->onTransactionHandled();
     transactionFlags = getTransactionFlags(eTransactionMask);
     handleTransactionLocked(transactionFlags);
 
@@ -2699,7 +2647,7 @@
         defaultDataSpace = Dataspace::V0_SRGB;
     }
     display->getCompositionDisplay()->setColorMode(defaultColorMode, defaultDataSpace,
-                                                   RenderIntent::COLORIMETRIC);
+                                                   RenderIntent::COLORIMETRIC, Dataspace::UNKNOWN);
     if (!state.isVirtual()) {
         LOG_ALWAYS_FATAL_IF(!displayId);
         display->setActiveConfig(getHwComposer().getActiveConfigIndex(*displayId));
@@ -2856,9 +2804,11 @@
 
 void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
 {
+    const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
+
     // Notify all layers of available frames
-    mCurrentState.traverseInZOrder([](Layer* layer) {
-        layer->notifyAvailableFrames();
+    mCurrentState.traverseInZOrder([expectedPresentTime](Layer* layer) {
+        layer->notifyAvailableFrames(expectedPresentTime);
     });
 
     /*
@@ -3057,7 +3007,7 @@
 void SurfaceFlinger::latchAndReleaseBuffer(const sp<Layer>& layer) {
     if (layer->hasReadyFrame()) {
         bool ignored = false;
-        layer->latchBuffer(ignored, systemTime());
+        layer->latchBuffer(ignored, systemTime(), 0 /* expectedPresentTime */);
     }
     layer->releasePendingBuffer(systemTime());
 }
@@ -3305,6 +3255,8 @@
     bool frameQueued = false;
     bool newDataLatched = false;
 
+    const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
+
     // Store the set of layers that need updates. This set must not change as
     // buffers are being latched, as this could result in a deadlock.
     // Example: Two producers share the same command stream and:
@@ -3317,7 +3269,6 @@
     mDrawingState.traverseInZOrder([&](Layer* layer) {
         if (layer->hasReadyFrame()) {
             frameQueued = true;
-            const nsecs_t expectedPresentTime = getExpectedPresentTime();
             if (layer->shouldPresentNow(expectedPresentTime)) {
                 mLayersWithQueuedFrames.push_back(layer);
             } else {
@@ -3335,7 +3286,7 @@
         Mutex::Autolock lock(mStateLock);
 
         for (auto& layer : mLayersWithQueuedFrames) {
-            if (layer->latchBuffer(visibleRegions, latchTime)) {
+            if (layer->latchBuffer(visibleRegions, latchTime, expectedPresentTime)) {
                 mLayersPendingRefresh.push_back(layer);
             }
             layer->useSurfaceDamage();
@@ -3402,8 +3353,9 @@
 
     const Region bounds(displayState.bounds);
     const DisplayRenderArea renderArea(displayDevice);
-    const bool hasClientComposition = getHwComposer().hasClientComposition(displayId);
-    ATRACE_INT("hasClientComposition", hasClientComposition);
+    const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition",
+                                                      getHwComposer().hasClientComposition(
+                                                              displayId)};
 
     bool applyColorMatrix = false;
 
@@ -3633,7 +3585,7 @@
 uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags,
                                              Scheduler::TransactionStart transactionStart) {
     uint32_t old = mTransactionFlags.fetch_or(flags);
-    mVsyncModulator.setTransactionStart(transactionStart);
+    mVSyncModulator->setTransactionStart(transactionStart);
     if ((old & flags)==0) { // wake the server up
         signalTransaction();
     }
@@ -3656,6 +3608,7 @@
             while (!transactionQueue.empty()) {
                 const auto& transaction = transactionQueue.front();
                 if (!transactionIsReadyToBeApplied(transaction.desiredPresentTime,
+                                                   true /* useCachedExpectedPresentTime */,
                                                    transaction.states)) {
                     setTransactionFlags(eTransactionFlushNeeded);
                     break;
@@ -3685,31 +3638,14 @@
     return !mTransactionQueues.empty();
 }
 
-bool SurfaceFlinger::containsAnyInvalidClientState(const Vector<ComposerState>& states) {
-    for (const ComposerState& state : states) {
-        // Here we need to check that the interface we're given is indeed
-        // one of our own. A malicious client could give us a nullptr
-        // IInterface, or one of its own or even one of our own but a
-        // different type. All these situations would cause us to crash.
-        if (state.client == nullptr) {
-            return true;
-        }
-
-        sp<IBinder> binder = IInterface::asBinder(state.client);
-        if (binder == nullptr) {
-            return true;
-        }
-
-        if (binder->queryLocalInterface(ISurfaceComposerClient::descriptor) == nullptr) {
-            return true;
-        }
-    }
-    return false;
-}
 
 bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
+                                                   bool useCachedExpectedPresentTime,
                                                    const Vector<ComposerState>& states) {
-    nsecs_t expectedPresentTime = getExpectedPresentTime();
+    if (!useCachedExpectedPresentTime)
+        populateExpectedPresentTime();
+
+    const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
     // 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 &&
@@ -3744,10 +3680,6 @@
 
     Mutex::Autolock _l(mStateLock);
 
-    if (containsAnyInvalidClientState(states)) {
-        return;
-    }
-
     // If its TransactionQueue already has a pending TransactionState or if it is pending
     auto itr = mTransactionQueues.find(applyToken);
     // if this is an animation frame, wait until prior animation frame has
@@ -3764,8 +3696,10 @@
             itr = mTransactionQueues.find(applyToken);
         }
     }
-    if (itr != mTransactionQueues.end() ||
-        !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
+
+    // Expected present time is computed and cached on invalidate, so it may be stale.
+    if (itr != mTransactionQueues.end() || !transactionIsReadyToBeApplied(
+            desiredPresentTime, false /* useCachedExpectedPresentTime */, states)) {
         mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime,
                                                uncacheBuffer, listenerCallbacks, postTime,
                                                privileged);
@@ -3959,9 +3893,8 @@
         const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime,
         bool privileged) {
     const layer_state_t& s = composerState.state;
-    sp<Client> client(static_cast<Client*>(composerState.client.get()));
 
-    sp<Layer> layer(client->getLayerUser(s.surface));
+    sp<Layer> layer(fromHandle(s.surface));
     if (layer == nullptr) {
         for (auto& listenerCallback : listenerCallbacks) {
             mTransactionCompletedThread.registerUnpresentedCallbackHandle(
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 0e6eacf..04e0c89 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -60,6 +60,7 @@
 #include "Scheduler/VSyncModulator.h"
 #include "SurfaceFlingerFactory.h"
 #include "SurfaceTracing.h"
+#include "TracedOrdinal.h"
 #include "TransactionCompletedThread.h"
 
 #include <atomic>
@@ -68,6 +69,7 @@
 #include <map>
 #include <memory>
 #include <mutex>
+#include <optional>
 #include <queue>
 #include <set>
 #include <string>
@@ -295,18 +297,13 @@
 
     // main thread function to enable/disable h/w composer event
     void setPrimaryVsyncEnabledInternal(bool enabled);
+    void setVsyncEnabledInHWC(DisplayId displayId, HWC2::Vsync enabled);
 
     // called on the main thread by MessageQueue when an internal message
     // is received
     // TODO: this should be made accessible only to MessageQueue
     void onMessageReceived(int32_t what);
 
-    // populates the expected present time for this frame.
-    // When we are in negative offsets, we perform a correction so that the
-    // predicted vsync for the *next* frame is used instead.
-    void populateExpectedPresentTime();
-    nsecs_t getExpectedPresentTime() const { return mExpectedPresentTime; }
-
     // for debugging only
     // TODO: this should be made accessible only to HWComposer
     const Vector<sp<Layer>>& getLayerSortedByZForHwcDisplay(DisplayId displayId);
@@ -583,8 +580,8 @@
     void latchAndReleaseBuffer(const sp<Layer>& layer);
     void commitTransaction() REQUIRES(mStateLock);
     void commitOffscreenLayers();
-    bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
     bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
+                                       bool useCachedExpectedPresentTime,
                                        const Vector<ComposerState>& states);
     uint32_t setClientStateLocked(const ComposerState& composerState, int64_t desiredPresentTime,
                                   const std::vector<ListenerCallbacks>& listenerCallbacks,
@@ -748,7 +745,6 @@
     void computeVisibleRegions(const sp<const DisplayDevice>& display, Region& dirtyRegion,
                                Region& opaqueRegion);
 
-    void preComposition();
     void postComposition();
     void getCompositorTiming(CompositorTiming* compositorTiming);
     void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
@@ -816,6 +812,12 @@
 
     bool isDisplayConfigAllowed(int32_t configId) const REQUIRES(mStateLock);
 
+    bool previousFrameMissed();
+
+    // Populates the expected present time for this frame. For negative offsets, performs a
+    // correction using the predicted vsync for the next frame instead.
+    void populateExpectedPresentTime();
+
     /*
      * Display identification
      */
@@ -844,9 +846,6 @@
         return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
     }
 
-    bool previousFrameMissed();
-    void setVsyncEnabledInHWC(DisplayId displayId, HWC2::Vsync enabled);
-
     /*
      * Debugging & dumpsys
      */
@@ -963,11 +962,6 @@
     std::unique_ptr<EventThread> mInjectorEventThread;
     std::unique_ptr<InjectVSyncSource> mVSyncInjector;
 
-    // Calculates correct offsets.
-    VSyncModulator mVsyncModulator;
-    // Keeps track of all available phase offsets for different refresh types.
-    const std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets;
-
     // Can only accessed from the main thread, these members
     // don't need synchronization
     State mDrawingState{LayerVector::StateSet::Drawing};
@@ -1132,9 +1126,17 @@
     sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
     sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
 
+    // Stores phase offsets configured per refresh rate.
+    const std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets;
+
+    // Optional to defer construction until scheduler connections are created.
+    std::optional<scheduler::VSyncModulator> mVSyncModulator;
+
     scheduler::RefreshRateConfigs mRefreshRateConfigs;
     scheduler::RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, *mTimeStats};
 
+    std::atomic<nsecs_t> mExpectedPresentTime = 0;
+
     // All configs are allowed if the set is empty.
     using DisplayConfigs = std::set<int32_t>;
     DisplayConfigs mAllowedDisplayConfigs GUARDED_BY(mStateLock);
@@ -1148,7 +1150,8 @@
     ActiveConfigInfo mDesiredActiveConfig GUARDED_BY(mActiveConfigLock);
 
     // below flags are set by main thread only
-    bool mDesiredActiveConfigChanged GUARDED_BY(mActiveConfigLock) = false;
+    TracedOrdinal<bool> mDesiredActiveConfigChanged
+            GUARDED_BY(mActiveConfigLock) = {"DesiredActiveConfigChanged", false};
     bool mCheckPendingFence = false;
 
     bool mLumaSampling = true;
@@ -1188,8 +1191,6 @@
     // Flags to capture the state of Vsync in HWC
     HWC2::Vsync mHWCVsyncState = HWC2::Vsync::Disable;
     HWC2::Vsync mHWCVsyncPendingState = HWC2::Vsync::Disable;
-
-    nsecs_t mExpectedPresentTime;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 7f86c14..fdc6c58 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -39,6 +39,12 @@
 struct DisplayDeviceState;
 struct DisplayState;
 struct layer_state_t;
+using Transaction = surfaceflinger::Transaction;
+using Trace = surfaceflinger::Trace;
+using Rectangle = surfaceflinger::Rectangle;
+using SurfaceChange = surfaceflinger::SurfaceChange;
+using Increment = surfaceflinger::Increment;
+using DisplayChange = surfaceflinger::DisplayChange;
 
 constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
 
@@ -183,6 +189,7 @@
 };
 
 } // namespace impl
+
 } // namespace android
 
 #endif // ANDROID_SURFACEINTERCEPTOR_H
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
new file mode 100644
index 0000000..c145a39
--- /dev/null
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2019 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 <android-base/stringprintf.h>
+#include <utils/Trace.h>
+#include <cmath>
+#include <string>
+
+template <typename T>
+class TracedOrdinal {
+public:
+    static_assert(std::is_same<bool, T>() || (std::is_signed<T>() && std::is_integral<T>()),
+                  "Type is not supported. Please test it with systrace before adding "
+                  "it to the list.");
+
+    TracedOrdinal(const std::string& name, T initialValue)
+          : mName(name),
+            mNameNegative(android::base::StringPrintf("%sNegative", name.c_str())),
+            mHasGoneNegative(std::signbit(initialValue)),
+            mData(initialValue) {
+        trace();
+    }
+
+    operator T() const { return mData; }
+
+    TracedOrdinal& operator=(T other) {
+        mData = other;
+        mHasGoneNegative = mHasGoneNegative || std::signbit(mData);
+        trace();
+        return *this;
+    }
+
+private:
+    void trace() {
+        if (!std::signbit(mData)) {
+            ATRACE_INT64(mName.c_str(), int64_t(mData));
+            if (mHasGoneNegative) {
+                ATRACE_INT64(mNameNegative.c_str(), 0);
+            }
+        } else {
+            ATRACE_INT64(mNameNegative.c_str(), -int64_t(mData));
+            ATRACE_INT64(mName.c_str(), 0);
+        }
+    }
+
+    const std::string mName;
+    const std::string mNameNegative;
+    bool mHasGoneNegative;
+    T mData;
+};
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 26c6da9..bc5f1aa 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -36,6 +36,9 @@
 namespace android {
 
 using Transaction = SurfaceComposerClient::Transaction;
+using SurfaceChange = surfaceflinger::SurfaceChange;
+using Trace = surfaceflinger::Trace;
+using Increment = surfaceflinger::Increment;
 
 constexpr int32_t SCALING_UPDATE = 1;
 constexpr uint32_t BUFFER_UPDATES = 18;
@@ -980,5 +983,4 @@
     ASSERT_TRUE(bufferUpdatesFound(capturedTrace));
     ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kSurfaceCreation));
 }
-
 }
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index e6211c4..0f21ad8 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -502,7 +502,7 @@
 
         EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
         bool ignoredRecomputeVisibleRegions;
-        layer->latchBuffer(ignoredRecomputeVisibleRegions, 0);
+        layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, 0);
         Mock::VerifyAndClear(test->mRenderEngine);
     }
 
@@ -988,7 +988,9 @@
 
 struct ForcedClientCompositionResultVariant : public RECompositionResultVariant {
     static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
-        layer->forceClientComposition(test->mDisplay);
+        const auto outputLayer = layer->findOutputLayerForDisplay(test->mDisplay);
+        LOG_FATAL_IF(!outputLayer);
+        outputLayer->editState().forceClientComposition = true;
     }
 
     template <typename Case>
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
index 1d75011..66c7f6b 100644
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -20,42 +20,22 @@
 
 #include "Scheduler/PhaseOffsets.h"
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
 
-using RefreshRateType = RefreshRateConfigs::RefreshRateType;
+struct FakePhaseOffsets : PhaseOffsets {
+    static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
 
-class FakePhaseOffsets : public android::scheduler::PhaseOffsets {
-    nsecs_t FAKE_PHASE_OFFSET_NS = 0;
+    Offsets getOffsetsForRefreshRate(RefreshRateType) const override { return getCurrentOffsets(); }
 
-public:
-    FakePhaseOffsets() = default;
-    ~FakePhaseOffsets() = default;
-
-    nsecs_t getCurrentAppOffset() override { return FAKE_PHASE_OFFSET_NS; }
-    nsecs_t getCurrentSfOffset() override { return FAKE_PHASE_OFFSET_NS; }
-
-    PhaseOffsets::Offsets getOffsetsForRefreshRate(
-            RefreshRateType /*refreshRateType*/) const override {
-        return getCurrentOffsets();
+    Offsets getCurrentOffsets() const override {
+        return {{RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+                {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+                {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+                FAKE_PHASE_OFFSET_NS};
     }
 
-    // Returns early, early GL, and late offsets for Apps and SF.
-    PhaseOffsets::Offsets getCurrentOffsets() const override {
-        return Offsets{{RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
-                       {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
-                       {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
-    }
-
-    // This function should be called when the device is switching between different
-    // refresh rates, to properly update the offsets.
-    void setRefreshRateType(RefreshRateType /*refreshRateType*/) override {}
-
-    nsecs_t getOffsetThresholdForNextVsync() const override { return FAKE_PHASE_OFFSET_NS; }
-
-    // Returns current offsets in human friendly format.
-    void dump(std::string& /*result*/) const override {}
+    void setRefreshRateType(RefreshRateType) override {}
+    void dump(std::string&) const override {}
 };
 
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler