Merge "[native] Migrate SkSurface creation to SkSurfaces factories"
diff --git a/cmds/lshal/libprocpartition/Android.bp b/cmds/lshal/libprocpartition/Android.bp
index af85666..d0e4b74 100644
--- a/cmds/lshal/libprocpartition/Android.bp
+++ b/cmds/lshal/libprocpartition/Android.bp
@@ -37,4 +37,8 @@
         "include",
     ],
     min_sdk_version: "30",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.neuralnetworks",
+    ],
 }
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index c1a04dd..520d6f9 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -131,7 +131,10 @@
     }
 
     IPCThreadState::self()->setTheContextObject(manager);
-    ps->becomeContextManager();
+    if (!ps->becomeContextManager()) {
+        LOG(ERROR) << "Could not become context manager";
+        // TODO(b/280514080): fatal
+    }
 
     sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
 
diff --git a/include/input/Input.h b/include/input/Input.h
index 7acafbf..d4750dd 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -30,7 +30,6 @@
 #include <stdint.h>
 #include <ui/Transform.h>
 #include <utils/BitSet.h>
-#include <utils/RefBase.h>
 #include <utils/Timers.h>
 #include <array>
 #include <limits>
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index 5e539f2..1a9766d 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -72,6 +72,7 @@
         "//apex_available:platform",
         "com.android.media",
         "com.android.media.swcodec",
+        "com.android.neuralnetworks",
     ],
 
 }
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index bd85c77..00e040f 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -124,8 +124,13 @@
     return mMaxThreads;
 }
 
-void RpcServer::setProtocolVersion(uint32_t version) {
+bool RpcServer::setProtocolVersion(uint32_t version) {
+    if (!RpcState::validateProtocolVersion(version)) {
+        return false;
+    }
+
     mProtocolVersion = version;
+    return true;
 }
 
 void RpcServer::setSupportedFileDescriptorTransportModes(
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index fbad0f7..c3dee16 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -104,11 +104,7 @@
 }
 
 bool RpcSession::setProtocolVersionInternal(uint32_t version, bool checkStarted) {
-    if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT &&
-        version != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
-        ALOGE("Cannot start RPC session with version %u which is unknown (current protocol version "
-              "is %u).",
-              version, RPC_WIRE_PROTOCOL_VERSION);
+    if (!RpcState::validateProtocolVersion(version)) {
         return false;
     }
 
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index 03fa699..ff35f5f 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -398,6 +398,18 @@
     return OK;
 }
 
+bool RpcState::validateProtocolVersion(uint32_t version) {
+    if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT &&
+        version != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
+        ALOGE("Cannot use RPC binder protocol version %u which is unknown (current protocol "
+              "version "
+              "is %u).",
+              version, RPC_WIRE_PROTOCOL_VERSION);
+        return false;
+    }
+    return true;
+}
+
 status_t RpcState::readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
                                           const sp<RpcSession>& session, uint32_t* version) {
     RpcNewSessionResponse response;
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 0e23ea7..1fe71a5 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -63,6 +63,8 @@
     RpcState();
     ~RpcState();
 
+    [[nodiscard]] static bool validateProtocolVersion(uint32_t version);
+
     [[nodiscard]] status_t readNewSessionResponse(const sp<RpcSession::RpcConnection>& connection,
                                                   const sp<RpcSession>& session, uint32_t* version);
     [[nodiscard]] status_t sendConnectionInit(const sp<RpcSession::RpcConnection>& connection,
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index ce578e3..81391e9 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -55,7 +55,7 @@
     // For main functions - dangerous for libraries to use
     void startThreadPool();
 
-    bool becomeContextManager();
+    [[nodiscard]] bool becomeContextManager();
 
     sp<IBinder> getStrongProxyForHandle(int32_t handle);
     void expungeHandle(int32_t handle, IBinder* binder);
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 4816395..56ac7b0 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -137,7 +137,7 @@
      * used. However, this can be used in order to prevent newer protocol
      * versions from ever being used. This is expected to be useful for testing.
      */
-    void setProtocolVersion(uint32_t version);
+    [[nodiscard]] bool setProtocolVersion(uint32_t version);
 
     /**
      * Set the supported transports for sending and receiving file descriptors.
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index cefc42f..664894e 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -497,14 +497,28 @@
 
 struct DeathRecipientCookie {
     std::function<void(void)>*onDeath, *onUnlink;
+
+    // may contain additional data
+    // - if it contains AIBinder, then you must call AIBinder_unlinkToDeath manually,
+    //   because it would form a strong reference cycle
+    // - if it points to a data member of another structure, this should have a weak
+    //   promotable reference or a strong reference, in case that object is deleted
+    //   while the death recipient is firing
 };
 void LambdaOnDeath(void* cookie) {
     auto funcs = static_cast<DeathRecipientCookie*>(cookie);
+
+    // may reference other cookie members
+
     (*funcs->onDeath)();
 };
 void LambdaOnUnlink(void* cookie) {
     auto funcs = static_cast<DeathRecipientCookie*>(cookie);
     (*funcs->onUnlink)();
+
+    // may reference other cookie members
+
+    delete funcs;
 };
 TEST(NdkBinder, DeathRecipient) {
     using namespace std::chrono_literals;
@@ -536,12 +550,12 @@
         unlinkCv.notify_one();
     };
 
-    DeathRecipientCookie cookie = {&onDeath, &onUnlink};
+    DeathRecipientCookie* cookie = new DeathRecipientCookie{&onDeath, &onUnlink};
 
     AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath);
     AIBinder_DeathRecipient_setOnUnlinked(recipient, LambdaOnUnlink);
 
-    EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&cookie)));
+    EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(cookie)));
 
     // the binder driver should return this if the service dies during the transaction
     EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
diff --git a/libs/binder/tests/binderRecordReplayTest.cpp b/libs/binder/tests/binderRecordReplayTest.cpp
index db78546..17d5c8a 100644
--- a/libs/binder/tests/binderRecordReplayTest.cpp
+++ b/libs/binder/tests/binderRecordReplayTest.cpp
@@ -234,7 +234,7 @@
 TEST_F(BinderRecordReplayTest, ReplayLongArray) {
     std::vector<int64_t> savedArray = {int64_t{1LL << 11}, int64_t{1LL << 55}, int64_t{1LL << 45}};
     std::vector<int64_t> changedArray = {int64_t{1LL << 1}, int64_t{1LL << 21}, int64_t{1LL << 33},
-                                         int64_t{1LL << 63}};
+                                         int64_t{1LL << 62}};
     recordReplay(&IBinderRecordReplayTest::setLongArray, savedArray,
                  &IBinderRecordReplayTest::getLongArray, changedArray);
 }
@@ -251,7 +251,8 @@
     std::vector<String16> savedArray = {String16("This is saved value"), String16(),
                                         String16("\0\0", 2), String16("\xF3\x01\xAC\xAD\x21\xAF")};
 
-    std::vector<String16> changedArray = {String16("This is changed value"), String16("\1\2", 30)};
+    std::vector<String16> changedArray = {String16("This is changed value"),
+                                          String16("\xF0\x90\x90\xB7\xE2\x82\xAC")};
     recordReplay(&IBinderRecordReplayTest::setStringArray, savedArray,
                  &IBinderRecordReplayTest::getStringArray, changedArray);
 }
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index d01e9d7..505f30f 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1355,7 +1355,7 @@
     base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)));
     int sinkFd = sink.get();
     auto server = RpcServer::make(newTlsFactory(std::get<0>(GetParam())));
-    server->setProtocolVersion(std::get<1>(GetParam()));
+    ASSERT_TRUE(server->setProtocolVersion(std::get<1>(GetParam())));
     ASSERT_FALSE(server->hasServer());
     ASSERT_EQ(OK, server->setupExternalServer(std::move(sink)));
     ASSERT_TRUE(server->hasServer());
@@ -1371,7 +1371,7 @@
 
     auto addr = allocateSocketAddress();
     auto server = RpcServer::make(newTlsFactory(std::get<0>(GetParam())));
-    server->setProtocolVersion(std::get<1>(GetParam()));
+    ASSERT_TRUE(server->setProtocolVersion(std::get<1>(GetParam())));
     ASSERT_EQ(OK, server->setupUnixDomainServer(addr.c_str()));
     auto joinEnds = std::make_shared<OneOffSignal>();
 
@@ -1420,7 +1420,9 @@
                 std::unique_ptr<RpcAuth> auth = std::make_unique<RpcAuthSelfSigned>()) {
             auto [socketType, rpcSecurity, certificateFormat, serverVersion] = param;
             auto rpcServer = RpcServer::make(newTlsFactory(rpcSecurity));
-            rpcServer->setProtocolVersion(serverVersion);
+            if (!rpcServer->setProtocolVersion(serverVersion)) {
+                return AssertionFailure() << "Invalid protocol version: " << serverVersion;
+            }
             switch (socketType) {
                 case SocketType::PRECONNECTED: {
                     return AssertionFailure() << "Not supported by this test";
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index a9736d5..5e83fbf 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -118,7 +118,7 @@
     auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
     sp<RpcServer> server = RpcServer::make(newTlsFactory(rpcSecurity, certVerifier));
 
-    server->setProtocolVersion(serverConfig.serverVersion);
+    CHECK(server->setProtocolVersion(serverConfig.serverVersion));
     server->setMaxThreads(serverConfig.numThreads);
     server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes);
 
diff --git a/libs/binder/tests/binderRpcTestServiceTrusty.cpp b/libs/binder/tests/binderRpcTestServiceTrusty.cpp
index 8557389..5c7a96a 100644
--- a/libs/binder/tests/binderRpcTestServiceTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestServiceTrusty.cpp
@@ -90,7 +90,9 @@
 
         auto server = std::move(*serverOrErr);
         serverInfo.server = server;
-        serverInfo.server->setProtocolVersion(serverVersion);
+        if (!serverInfo.server->setProtocolVersion(serverVersion)) {
+            return EXIT_FAILURE;
+        }
         serverInfo.server->setPerSessionRootObject([=](const void* /*addrPtr*/, size_t /*len*/) {
             auto service = sp<MyBinderRpcTestTrusty>::make();
             // Assign a unique connection identifier to service->port so
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h
index a9a6197..cb37cfa 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_driver.h
@@ -19,7 +19,17 @@
 #include <binder/IBinder.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
+#include <vector>
+
 namespace android {
+
+/**
+ * See fuzzService, but fuzzes multiple services at the same time.
+ *
+ * Consumes providers.
+ */
+void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& provider);
+
 /**
  * Based on the random data in provider, construct an arbitrary number of
  * Parcel objects and send them to the service in serial.
@@ -34,4 +44,5 @@
  *   }
  */
 void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider);
+
 } // namespace android
diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h
index f2b7823..d8bf87a 100644
--- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h
+++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/libbinder_ndk_driver.h
@@ -16,10 +16,21 @@
 
 #pragma once
 
+#include <android/binder_auto_utils.h>
 #include <android/binder_parcel.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
+#include <vector>
+
 namespace android {
+
+/**
+ * See fuzzService, but fuzzes multiple services at the same time.
+ *
+ * Consumes providers.
+ */
+void fuzzService(const std::vector<ndk::SpAIBinder>& binders, FuzzedDataProvider&& provider);
+
 /**
  * Based on the random data in provider, construct an arbitrary number of
  * Parcel objects and send them to the service in serial.
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 8bef33f..69f7147 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -24,10 +24,12 @@
 namespace android {
 
 void fuzzService(const sp<IBinder>& binder, FuzzedDataProvider&& provider) {
-    sp<IBinder> target;
+    fuzzService(std::vector<sp<IBinder>>{binder}, std::move(provider));
+}
 
+void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& provider) {
     RandomParcelOptions options{
-            .extraBinders = {binder},
+            .extraBinders = binders,
             .extraFds = {},
     };
 
@@ -37,47 +39,73 @@
     }
 
     while (provider.remaining_bytes() > 0) {
-        // Most of the AIDL services will have small set of transaction codes.
-        uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
-                                               : provider.ConsumeIntegralInRange<uint32_t>(0, 100);
-        uint32_t flags = provider.ConsumeIntegral<uint32_t>();
-        Parcel data;
-        // for increased fuzz coverage
-        data.setEnforceNoDataAvail(provider.ConsumeBool());
+        provider.PickValueInArray<std::function<void()>>({
+                [&]() {
+                    // Most of the AIDL services will have small set of transaction codes.
+                    uint32_t code = provider.ConsumeBool()
+                            ? provider.ConsumeIntegral<uint32_t>()
+                            : provider.ConsumeIntegralInRange<uint32_t>(0, 100);
+                    uint32_t flags = provider.ConsumeIntegral<uint32_t>();
+                    Parcel data;
+                    // for increased fuzz coverage
+                    data.setEnforceNoDataAvail(provider.ConsumeBool());
 
-        sp<IBinder> target = options.extraBinders.at(
-                provider.ConsumeIntegralInRange<size_t>(0, options.extraBinders.size() - 1));
-        options.writeHeader = [&target](Parcel* p, FuzzedDataProvider& provider) {
-            // most code will be behind checks that the head of the Parcel
-            // is exactly this, so make it easier for fuzzers to reach this
-            if (provider.ConsumeBool()) {
-                p->writeInterfaceToken(target->getInterfaceDescriptor());
-            }
-        };
+                    sp<IBinder> target = options.extraBinders.at(
+                            provider.ConsumeIntegralInRange<size_t>(0,
+                                                                    options.extraBinders.size() -
+                                                                            1));
+                    options.writeHeader = [&target](Parcel* p, FuzzedDataProvider& provider) {
+                        // most code will be behind checks that the head of the Parcel
+                        // is exactly this, so make it easier for fuzzers to reach this
+                        if (provider.ConsumeBool()) {
+                            p->writeInterfaceToken(target->getInterfaceDescriptor());
+                        }
+                    };
 
-        std::vector<uint8_t> subData = provider.ConsumeBytes<uint8_t>(
-                provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
-        fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()), &options);
+                    std::vector<uint8_t> subData = provider.ConsumeBytes<uint8_t>(
+                            provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
+                    fillRandomParcel(&data, FuzzedDataProvider(subData.data(), subData.size()),
+                                     &options);
 
-        Parcel reply;
-        // for increased fuzz coverage
-        reply.setEnforceNoDataAvail(provider.ConsumeBool());
-        (void)target->transact(code, data, &reply, flags);
+                    Parcel reply;
+                    // for increased fuzz coverage
+                    reply.setEnforceNoDataAvail(provider.ConsumeBool());
+                    (void)target->transact(code, data, &reply, flags);
 
-        // feed back in binders and fds that are returned from the service, so that
-        // we can fuzz those binders, and use the fds and binders to feed back into
-        // the binders
-        auto retBinders = reply.debugReadAllStrongBinders();
-        options.extraBinders.insert(options.extraBinders.end(), retBinders.begin(),
-                                    retBinders.end());
-        auto retFds = reply.debugReadAllFileDescriptors();
-        for (size_t i = 0; i < retFds.size(); i++) {
-            options.extraFds.push_back(base::unique_fd(dup(retFds[i])));
-        }
+                    // feed back in binders and fds that are returned from the service, so that
+                    // we can fuzz those binders, and use the fds and binders to feed back into
+                    // the binders
+                    auto retBinders = reply.debugReadAllStrongBinders();
+                    options.extraBinders.insert(options.extraBinders.end(), retBinders.begin(),
+                                                retBinders.end());
+                    auto retFds = reply.debugReadAllFileDescriptors();
+                    for (size_t i = 0; i < retFds.size(); i++) {
+                        options.extraFds.push_back(base::unique_fd(dup(retFds[i])));
+                    }
+                },
+                [&]() {
+                    if (options.extraFds.size() == 0) {
+                        return;
+                    }
+                    uint32_t toDelete =
+                            provider.ConsumeIntegralInRange<uint32_t>(0,
+                                                                      options.extraFds.size() - 1);
+                    options.extraFds.erase(options.extraFds.begin() + toDelete);
+                },
+                [&]() {
+                    if (options.extraBinders.size() <= 1) {
+                        return;
+                    }
+                    uint32_t toDelete =
+                            provider.ConsumeIntegralInRange<uint32_t>(0,
+                                                                      options.extraBinders.size() -
+                                                                              1);
+                    options.extraBinders.erase(options.extraBinders.begin() + toDelete);
+                },
+        })();
     }
 
     // invariants
-
     auto ps = ProcessState::selfOrNull();
     if (ps) {
         CHECK_EQ(0, ps->getThreadPoolMaxTotalThreadCount())
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp
index a1fb701..0b0ca34 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_ndk_driver.cpp
@@ -24,6 +24,15 @@
 
 namespace android {
 
+void fuzzService(const std::vector<ndk::SpAIBinder>& binders, FuzzedDataProvider&& provider) {
+    std::vector<sp<IBinder>> cppBinders;
+    for (const auto& binder : binders) {
+        cppBinders.push_back(binder.get()->getBinder());
+    }
+
+    fuzzService(cppBinders, std::move(provider));
+}
+
 void fuzzService(AIBinder* binder, FuzzedDataProvider&& provider) {
     fuzzService(binder->getBinder(), std::move(provider));
 }
diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp
index 68b0008..8f64323 100644
--- a/libs/binder/trusty/RpcServerTrusty.cpp
+++ b/libs/binder/trusty/RpcServerTrusty.cpp
@@ -67,7 +67,7 @@
 
     // TODO(b/266741352): follow-up to prevent needing this in the future
     // Trusty needs to be set to the latest stable version that is in prebuilts there.
-    mRpcServer->setProtocolVersion(0);
+    LOG_ALWAYS_FATAL_IF(!mRpcServer->setProtocolVersion(0));
 
     if (mPortAcl) {
         // Initialize the array of pointers to uuids.
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
index 6678eb8..119f2a3 100644
--- a/libs/binder/trusty/include/binder/RpcServerTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -59,7 +59,9 @@
             size_t msgMaxSize,
             std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
 
-    void setProtocolVersion(uint32_t version) { mRpcServer->setProtocolVersion(version); }
+    [[nodiscard]] bool setProtocolVersion(uint32_t version) {
+        return mRpcServer->setProtocolVersion(version);
+    }
     void setSupportedFileDescriptorTransportModes(
             const std::vector<RpcSession::FileDescriptorTransportMode>& modes) {
         mRpcServer->setSupportedFileDescriptorTransportModes(modes);
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 9a2343b..808388f 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -630,7 +630,8 @@
     BQ_LOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x",
             *outSlot,
             mSlots[*outSlot].mFrameNumber,
-            mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
+            mSlots[*outSlot].mGraphicBuffer != nullptr ?
+            mSlots[*outSlot].mGraphicBuffer->handle : nullptr, returnFlags);
 
     if (outBufferAge) {
         *outBufferAge = mCore->mBufferAge;
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 42bdf57..6aae25d 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -44,12 +44,20 @@
         "-Wno-unused-parameter",
     ],
     sanitize: {
+        hwaddress: true,
         undefined: true,
         all_undefined: true,
         diag: {
             undefined: true,
         },
     },
+    target: {
+        host: {
+            sanitize: {
+                address: true,
+            },
+        },
+    },
     shared_libs: [
         "libbase",
         "libbinder",
diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h
index 334106f..ac75f43 100644
--- a/libs/ui/include/ui/FenceTime.h
+++ b/libs/ui/include/ui/FenceTime.h
@@ -142,8 +142,6 @@
     std::atomic<nsecs_t> mSignalTime{Fence::SIGNAL_TIME_INVALID};
 };
 
-using FenceTimePtr = std::shared_ptr<FenceTime>;
-
 // A queue of FenceTimes that are expected to signal in FIFO order.
 // Only maintains a queue of weak pointers so it doesn't keep references
 // to Fences on its own.
@@ -192,15 +190,8 @@
 // before the new one is added.
 class FenceToFenceTimeMap {
 public:
-    using FencePair = std::pair<sp<Fence>, FenceTimePtr>;
-
-    FencePair makePendingFenceForTest() {
-        const auto fence = sp<Fence>::make();
-        return {fence, createFenceTimeForTest(fence)};
-    }
-
-    // Create a new FenceTime that wraps the provided Fence.
-    FenceTimePtr createFenceTimeForTest(const sp<Fence>&);
+    // Create a new FenceTime with that wraps the provided Fence.
+    std::shared_ptr<FenceTime> createFenceTimeForTest(const sp<Fence>& fence);
 
     // Signals all FenceTimes created through this class that are wrappers
     // around |fence|.
@@ -214,6 +205,7 @@
     std::unordered_map<Fence*, std::vector<std::weak_ptr<FenceTime>>> mMap;
 };
 
-} // namespace android
+
+}; // namespace android
 
 #endif // ANDROID_FENCE_TIME_H
diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp
index ef927e3..cb8197c 100644
--- a/libs/ultrahdr/jpegr.cpp
+++ b/libs/ultrahdr/jpegr.cpp
@@ -1041,21 +1041,18 @@
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
-  size_t src_luma_stride = src->luma_stride;
-  size_t src_chroma_stride = src->chroma_stride;
   uint16_t* src_luma_data = reinterpret_cast<uint16_t*>(src->data);
-  uint16_t* src_chroma_data = reinterpret_cast<uint16_t*>(src->chroma_data);
+  size_t src_luma_stride = src->luma_stride == 0 ? src->width : src->luma_stride;
 
-  if (src_chroma_data == nullptr) {
-    src_chroma_data = &reinterpret_cast<uint16_t*>(src->data)[src_luma_stride * src->height];
+  uint16_t* src_chroma_data;
+  size_t src_chroma_stride;
+  if (src->chroma_data == nullptr) {
+     src_chroma_stride = src_luma_stride;
+     src_chroma_data = &reinterpret_cast<uint16_t*>(src->data)[src_luma_stride * src->height];
+  } else {
+     src_chroma_stride = src->chroma_stride;
+     src_chroma_data = reinterpret_cast<uint16_t*>(src->chroma_data);
   }
-  if (src_luma_stride == 0) {
-    src_luma_stride = src->width;
-  }
-  if (src_chroma_stride == 0) {
-    src_chroma_stride = src_luma_stride;
-  }
-
   dest->width = src->width;
   dest->height = src->height;
 
diff --git a/services/gpuservice/tests/fuzzers/Android.bp b/services/gpuservice/tests/fuzzers/Android.bp
new file mode 100644
index 0000000..6bcc5e8
--- /dev/null
+++ b/services/gpuservice/tests/fuzzers/Android.bp
@@ -0,0 +1,26 @@
+package {
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_fuzz {
+    name: "gpu_service_fuzzer",
+    defaults: [
+        "service_fuzzer_defaults",
+        "fuzzer_disable_leaks",
+    ],
+    static_libs: [
+        "liblog",
+    ],
+    fuzz_config: {
+        cc: [
+            "paulthomson@google.com",
+            "pbaiget@google.com",
+        ],
+        triage_assignee: "waghpawan@google.com",
+    },
+    include_dirs: ["frameworks/native/services/gpuservice/"],
+    srcs: ["GpuServiceFuzzer.cpp"],
+    shared_libs: [
+        "libgpuservice",
+    ],
+}
diff --git a/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp b/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp
new file mode 100644
index 0000000..c2574a3
--- /dev/null
+++ b/services/gpuservice/tests/fuzzers/GpuServiceFuzzer.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 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 <fuzzbinder/libbinder_driver.h>
+
+#include "GpuService.h"
+
+using ::android::fuzzService;
+using ::android::GpuService;
+using ::android::sp;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    sp<GpuService> gpuService = new GpuService();
+    fuzzService(gpuService, FuzzedDataProvider(data, size));
+    return 0;
+}
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index da4e42f..492551e 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -94,6 +94,7 @@
 
 cc_library_static {
     name: "libinputdispatcher",
+    host_supported: true,
     defaults: [
         "inputflinger_defaults",
         "libinputdispatcher_defaults",
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index f844078..4ee8173 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2811,6 +2811,30 @@
     }
 }
 
+std::optional<InputTarget> InputDispatcher::createInputTargetLocked(
+        const sp<android::gui::WindowInfoHandle>& windowHandle,
+        ftl::Flags<InputTarget::Flags> targetFlags,
+        std::optional<nsecs_t> firstDownTimeInTarget) const {
+    std::shared_ptr<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
+    if (inputChannel == nullptr) {
+        ALOGW("Not creating InputTarget for %s, no input channel", windowHandle->getName().c_str());
+        return {};
+    }
+    InputTarget inputTarget;
+    inputTarget.inputChannel = inputChannel;
+    inputTarget.flags = targetFlags;
+    inputTarget.globalScaleFactor = windowHandle->getInfo()->globalScaleFactor;
+    inputTarget.firstDownTimeInTarget = firstDownTimeInTarget;
+    const auto& displayInfoIt = mDisplayInfos.find(windowHandle->getInfo()->displayId);
+    if (displayInfoIt != mDisplayInfos.end()) {
+        inputTarget.displayTransform = displayInfoIt->second.transform;
+    } else {
+        // DisplayInfo not found for this window on display windowInfo->displayId.
+        // TODO(b/198444055): Make this an error message after 'setInputWindows' API is removed.
+    }
+    return inputTarget;
+}
+
 void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle,
                                             ftl::Flags<InputTarget::Flags> targetFlags,
                                             std::bitset<MAX_POINTER_ID + 1> pointerIds,
@@ -2826,25 +2850,12 @@
     const WindowInfo* windowInfo = windowHandle->getInfo();
 
     if (it == inputTargets.end()) {
-        InputTarget inputTarget;
-        std::shared_ptr<InputChannel> inputChannel =
-                getInputChannelLocked(windowHandle->getToken());
-        if (inputChannel == nullptr) {
-            ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
+        std::optional<InputTarget> target =
+                createInputTargetLocked(windowHandle, targetFlags, firstDownTimeInTarget);
+        if (!target) {
             return;
         }
-        inputTarget.inputChannel = inputChannel;
-        inputTarget.flags = targetFlags;
-        inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
-        inputTarget.firstDownTimeInTarget = firstDownTimeInTarget;
-        const auto& displayInfoIt = mDisplayInfos.find(windowInfo->displayId);
-        if (displayInfoIt != mDisplayInfos.end()) {
-            inputTarget.displayTransform = displayInfoIt->second.transform;
-        } else {
-            // DisplayInfo not found for this window on display windowInfo->displayId.
-            // TODO(b/198444055): Make this an error message after 'setInputWindows' API is removed.
-        }
-        inputTargets.push_back(inputTarget);
+        inputTargets.push_back(*target);
         it = inputTargets.end() - 1;
     }
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 9b12f2f..0e9cfef 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -556,6 +556,10 @@
     std::vector<Monitor> selectResponsiveMonitorsLocked(
             const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock);
 
+    std::optional<InputTarget> createInputTargetLocked(
+            const sp<android::gui::WindowInfoHandle>& windowHandle,
+            ftl::Flags<InputTarget::Flags> targetFlags,
+            std::optional<nsecs_t> firstDownTimeInTarget) const REQUIRES(mLock);
     void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
                                ftl::Flags<InputTarget::Flags> targetFlags,
                                std::bitset<MAX_POINTER_ID + 1> pointerIds,
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 0eb4ad2..0354164 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -1072,16 +1072,8 @@
     Device* device = getDeviceLocked(deviceId);
     if (device != nullptr && device->keyMap.haveKeyLayout()) {
         for (size_t codeIndex = 0; codeIndex < keyCodes.size(); codeIndex++) {
-            std::vector<int32_t> scanCodes =
-                    device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex]);
-
-            // check the possible scan codes identified by the layout map against the
-            // map of codes actually emitted by the driver
-            for (const int32_t scanCode : scanCodes) {
-                if (device->keyBitmask.test(scanCode)) {
-                    outFlags[codeIndex] = 1;
-                    break;
-                }
+            if (device->hasKeycodeLocked(keyCodes[codeIndex])) {
+                outFlags[codeIndex] = 1;
             }
         }
         return true;
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index ff30429..0a64a1c 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -503,9 +503,9 @@
         classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) {
         mappers.push_back(createInputMapper<TouchpadInputMapper>(contextPtr, readerConfig));
     } else if (classes.test(InputDeviceClass::TOUCH_MT)) {
-        mappers.push_back(createInputMapper<MultiTouchInputMapper>(contextPtr, readerConfig));
+        mappers.push_back(std::make_unique<MultiTouchInputMapper>(contextPtr, readerConfig));
     } else if (classes.test(InputDeviceClass::TOUCH)) {
-        mappers.push_back(createInputMapper<SingleTouchInputMapper>(contextPtr, readerConfig));
+        mappers.push_back(std::make_unique<SingleTouchInputMapper>(contextPtr, readerConfig));
     }
 
     // Joystick-like devices.
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 1d788df..f300ee1 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -27,6 +27,8 @@
     friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
                                                 const InputReaderConfiguration& readerConfig,
                                                 Args... args);
+    explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
+                                   const InputReaderConfiguration& readerConfig);
 
     ~MultiTouchInputMapper() override;
 
@@ -39,8 +41,6 @@
     bool hasStylus() const override;
 
 private:
-    explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
-                                   const InputReaderConfiguration& readerConfig);
     // simulate_stylus_with_touch is a debug mode that converts all finger pointers reported by this
     // mapper's touchscreen into stylus pointers, and adds SOURCE_STYLUS to the input device.
     // It is used to simulate stylus events for debugging and testing on a device that does not
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index 7726bfb..dac53cf 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -27,6 +27,8 @@
     friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
                                                 const InputReaderConfiguration& readerConfig,
                                                 Args... args);
+    explicit SingleTouchInputMapper(InputDeviceContext& deviceContext,
+                                    const InputReaderConfiguration& readerConfig);
 
     ~SingleTouchInputMapper() override;
 
@@ -40,8 +42,6 @@
 
 private:
     SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
-    explicit SingleTouchInputMapper(InputDeviceContext& deviceContext,
-                                    const InputReaderConfiguration& readerConfig);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reporter/Android.bp b/services/inputflinger/reporter/Android.bp
index 693ff06..b1e1aee 100644
--- a/services/inputflinger/reporter/Android.bp
+++ b/services/inputflinger/reporter/Android.bp
@@ -37,6 +37,7 @@
 cc_defaults {
     name: "libinputreporter_defaults",
     srcs: [":libinputreporter_sources"],
+    host_supported: true,
     shared_libs: [
         "liblog",
         "libutils",
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index fecd0d1..74be843 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1564,8 +1564,7 @@
 
         // Set mouse cursor position for the most common cases to avoid boilerplate.
         if (mSource == AINPUT_SOURCE_MOUSE &&
-            !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) &&
-            mPointers.size() == 1) {
+            !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
             mRawXCursorPosition = pointerCoords[0].getX();
             mRawYCursorPosition = pointerCoords[0].getY();
         }
@@ -1677,8 +1676,7 @@
 
         // Set mouse cursor position for the most common cases to avoid boilerplate.
         if (mSource == AINPUT_SOURCE_MOUSE &&
-            !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) &&
-            mPointers.size() == 1) {
+            !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
             mRawXCursorPosition = pointerCoords[0].getX();
             mRawYCursorPosition = pointerCoords[0].getY();
         }
@@ -1729,19 +1727,21 @@
         InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT,
         nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC),
         std::optional<int32_t> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
-    MotionEvent event = MotionEventBuilder(action, source)
-                                .displayId(displayId)
-                                .eventTime(eventTime)
-                                .rawXCursorPosition(cursorPosition.x)
-                                .rawYCursorPosition(cursorPosition.y)
-                                .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER)
-                                                 .x(position.x)
-                                                 .y(position.y))
-                                .build();
+    MotionEventBuilder motionBuilder =
+            MotionEventBuilder(action, source)
+                    .displayId(displayId)
+                    .eventTime(eventTime)
+                    .rawXCursorPosition(cursorPosition.x)
+                    .rawYCursorPosition(cursorPosition.y)
+                    .pointer(
+                            PointerBuilder(/*id=*/0, ToolType::FINGER).x(position.x).y(position.y));
+    if (MotionEvent::getActionMasked(action) == ACTION_DOWN) {
+        motionBuilder.downTime(eventTime);
+    }
 
     // Inject event until dispatch out.
-    return injectMotionEvent(dispatcher, event, injectionTimeout, injectionMode, targetUid,
-                             policyFlags);
+    return injectMotionEvent(dispatcher, motionBuilder.build(), injectionTimeout, injectionMode,
+                             targetUid, policyFlags);
 }
 
 static InputEventInjectionResult injectMotionDown(
@@ -4923,6 +4923,25 @@
     mFakePolicy->assertUserActivityPoked();
 }
 
+TEST_F(InputDispatcherTest, InjectedTouchesPokeUserActivity) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+                                                             "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {100, 100}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    window->consumeMotionEvent(
+            AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+
+    // Should have poked user activity
+    mFakePolicy->assertUserActivityPoked();
+}
+
 TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
diff --git a/services/surfaceflinger/FrontEnd/readme.md b/services/surfaceflinger/FrontEnd/readme.md
new file mode 100644
index 0000000..e5f51a5
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/readme.md
@@ -0,0 +1,110 @@
+# SurfaceFlinger FrontEnd
+
+SurfaceFlinger FrontEnd implements the client APIs that describe how buffers should be
+composited on the screen. Layers are used to capture how the buffer should be composited
+and each buffer is associated with a Layer. Transactions contain an atomic set of changes
+to one or more of these layers. The FrontEnd consumes these transactions, maintains the
+layer lifecycle, and provides a snapshot to the composition engine every frame that
+describes how a set of buffers should be composited.
+
+
+
+## Layers
+Layers are used to describe how a buffer should be placed on the display relative to other
+buffers. They are represented as a hierarchy, similar to a scene graph. Child layers can
+inherit some properties from their parents, which allows higher-level system components to
+maintain policies at different levels without needing to understand the entire hierarchy.
+This allows control to be delegated to different parts of the system - such as SystemServer,
+SysUI and Apps.
+
+### Layer Lifecycle
+Layer is created by a client. The client receives a strong binder reference to the layer
+handle, which will keep the layer alive as long as the client holds the reference. The
+layer can also be kept alive if the layer has a parent, since the parent will hold a
+strong reference to the children. If the layer is not reachable but its handle is alive,
+the layer will be offscreen and its resources will not be freed. Clients must explicitly
+release all references to the handle as soon as it's done with the layer. It's strongly
+recommended to explicitly release the layer in Java and not rely on the GC.
+
+
+
+## Transactions
+Transactions contain a group of changes to one or more layers that are applied together.
+Transactions can be merged to apply a set of changes atomically. Merges are associative,
+meaning how you group the merges does not matter, but they are not commutative, meaning
+that the order in which you merge them does.
+For example:
+
+`Transaction a; a.setAlpha(sc, 2);`
+
+`Transaction b; b.setAlpha(sc, 4);`
+
+`a.merge(b)` is not the same as `b.merge(a)`
+
+<p>
+
+`Transaction c; c.setAlpha(sc, 6);`
+
+`a.merge(b).merge(c)` is the same as `b.merge(c); a.merge(b);`
+
+Transactions are queued in SurfaceFlinger per ApplyToken so order is only guaranteed for
+Transactions with the same applyToken. By default each process and each buffer producer
+provides a unique ApplyToken. This prevents clients from affecting one another, and possibly
+slowing each other down.
+
+
+
+## Architecture
+SurfaceFlinger FrontEnd intends to optimize for predictability and performance because state
+generation is on the hotpath. Simple buffer updates should be as fast as possible, and they
+should be consistently fast. This means avoiding contention (e.g., locks) and context
+switching. We also want to avoid doing anything that does not contribute to putting a pixel
+on the display.
+
+The pipeline can be broken down into five stages:
+- Queue and filter transactions that are ready to be committed.
+- Handle layer lifecycles and update server-side state per layer.
+- Generate and/or update the traversal trees.
+- Generate a z-ordered list of snapshots.
+- Emit callbacks back to clients
+
+
+### TransactionHandler
+TransactionHandler is responsible for queuing and filtering transactions that are ready to
+be applied. On commit, we filter the transactions that are ready. We provide an interface
+for other components to apply their own filter to determine if a transaction is ready to be
+applied.
+
+
+### LayerLifecycleManager
+RequestedLayerState is a simple data class that stores the server side layer state.
+Transactions are merged into this state, similar to how transactions can be merged on the
+client side. The states can always be reconstructed from LayerCreationArgs and a list of
+transactions. LayerLifecycleManager keeps track of Layer handle lifecycle and the layer
+lifecycle itself. It consumes a list of transactions and generates a list of server side
+states and change flags. Other components can register to listen to layer lifecycles.
+
+
+### LayerHierarchyBuilder
+LayerHierarchyBuilder consumes a list of RequestedLayerStates to generate a LayerHierarchy.
+The hierarchy provides functions for breadth-first traversal and z-order traversal of the
+entire tree or a subtree. Internally, the hierarchy is represented by a graph. Mirrored
+layers are represented by the same node in the graph with multiple parents. This allows us
+to implement mirroring without cloning Layers and maintaining complex hierarchies.
+
+
+### LayerSnapshotBuilder
+LayerSnapshotBuilder consumes a LayerHierarchy along with a list of RequestedLayerStates to
+generate a flattened z-ordered list of LayerSnapshots. LayerSnapshots contain all the data
+required for CompositionEngine and RenderEngine. It has no dependencies to FrontEnd, or the
+LayerHierarchy used to create them. They can be cloned and consumed freely. Other consumers
+like WindowInfo listeners (input and accessibility) also updated from these snapshots.
+
+Change flags are used to efficiently traverse this hierarchy where possible. This allows us
+to support short circuiting parts of the hierarchy, partial hierarchy updates and fast paths
+for buffer updates.
+
+
+While they can be cloned, the current implementation moves the snapshot from FrontEnd to
+CompositionEngine to avoid needless work in the hotpath. For snapshot consumers not critical
+to composition, the goal is to clone the snapshots and consume them on a background thread.
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index 6d2586a..d5d8688 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -40,7 +40,6 @@
     name: "libscheduler",
     defaults: ["libscheduler_defaults"],
     srcs: [
-        "src/FrameTargeter.cpp",
         "src/PresentLatencyTracker.cpp",
         "src/Timer.cpp",
     ],
@@ -53,7 +52,6 @@
     test_suites: ["device-tests"],
     defaults: ["libscheduler_defaults"],
     srcs: [
-        "tests/FrameTargeterTest.cpp",
         "tests/PresentLatencyTrackerTest.cpp",
         "tests/TimerTest.cpp",
     ],
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index af9acf3..281b0ae 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -612,6 +612,15 @@
                 preferredExpectedPresentationTime + multiplier * frameInterval;
         if (expectedPresentationTime >= preferredExpectedPresentationTime +
                     scheduler::VsyncConfig::kEarlyLatchMaxThreshold.count()) {
+            if (currentIndex == 0) {
+                ALOGW("%s: Expected present time is too far in the future but no timelines are "
+                      "valid. preferred EPT=%" PRId64 ", Calculated EPT=%" PRId64
+                      ", multiplier=%" PRId64 ", frameInterval=%" PRId64 ", threshold=%" PRId64,
+                      __func__, preferredExpectedPresentationTime, expectedPresentationTime,
+                      multiplier, frameInterval,
+                      static_cast<int64_t>(
+                              scheduler::VsyncConfig::kEarlyLatchMaxThreshold.count()));
+            }
             break;
         }
 
@@ -625,6 +634,20 @@
                  .expectedPresentationTime = expectedPresentationTime};
         currentIndex++;
     }
+
+    if (currentIndex == 0) {
+        ALOGW("%s: No timelines are valid. preferred EPT=%" PRId64 ", frameInterval=%" PRId64
+              ", threshold=%" PRId64,
+              __func__, preferredExpectedPresentationTime, frameInterval,
+              static_cast<int64_t>(scheduler::VsyncConfig::kEarlyLatchMaxThreshold.count()));
+        outVsyncEventData.frameTimelines[currentIndex] =
+                {.vsyncId = generateToken(timestamp, preferredDeadlineTimestamp,
+                                          preferredExpectedPresentationTime),
+                 .deadlineTimestamp = preferredDeadlineTimestamp,
+                 .expectedPresentationTime = preferredExpectedPresentationTime};
+        currentIndex++;
+    }
+
     outVsyncEventData.frameTimelinesLength = currentIndex;
 }
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 41639b6..918d401 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -171,21 +171,14 @@
 
 void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId,
                               TimePoint expectedVsyncTime) {
-    mPacesetterFrameTargeter.beginFrame({.frameBeginTime = SchedulerClock::now(),
-                                         .vsyncId = vsyncId,
-                                         .expectedVsyncTime = expectedVsyncTime,
-                                         .sfWorkDuration =
-                                                 mVsyncModulator->getVsyncConfig().sfWorkDuration},
-                                        *getVsyncSchedule());
+    const TimePoint frameTime = SchedulerClock::now();
 
-    if (!compositor.commit(mPacesetterFrameTargeter.target())) {
+    if (!compositor.commit(frameTime, vsyncId, expectedVsyncTime)) {
         return;
     }
 
-    const auto compositeResult = compositor.composite(mPacesetterFrameTargeter);
+    compositor.composite(frameTime, vsyncId);
     compositor.sample();
-
-    mPacesetterFrameTargeter.endFrame(compositeResult);
 }
 
 std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
@@ -195,23 +188,23 @@
             .getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
 }
 
-bool Scheduler::isVsyncValid(TimePoint expectedVsyncTime, uid_t uid) const {
+bool Scheduler::isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const {
     const auto frameRate = getFrameRateOverride(uid);
     if (!frameRate.has_value()) {
         return true;
     }
 
     ATRACE_FORMAT("%s uid: %d frameRate: %s", __func__, uid, to_string(*frameRate).c_str());
-    return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTime.ns(), *frameRate);
+    return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTimestamp.ns(), *frameRate);
 }
 
-bool Scheduler::isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const {
-    return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTime.ns(), frameRate);
+bool Scheduler::isVsyncInPhase(TimePoint timePoint, const Fps frameRate) const {
+    return getVsyncSchedule()->getTracker().isVSyncInPhase(timePoint.ns(), frameRate);
 }
 
 impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
-    return [this](nsecs_t expectedVsyncTime, uid_t uid) {
-        return !isVsyncValid(TimePoint::fromNs(expectedVsyncTime), uid);
+    return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
+        return !isVsyncValid(TimePoint::fromNs(expectedVsyncTimestamp), uid);
     };
 }
 
@@ -723,8 +716,6 @@
 
     mFrameRateOverrideMappings.dump(dumper);
     dumper.eol();
-
-    mPacesetterFrameTargeter.dump(dumper);
 }
 
 void Scheduler::dumpVsync(std::string& out) const {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 17e9cea..a1354fa 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -35,7 +35,6 @@
 #include <ftl/fake_guard.h>
 #include <ftl/optional.h>
 #include <scheduler/Features.h>
-#include <scheduler/FrameTargeter.h>
 #include <scheduler/Time.h>
 #include <scheduler/VsyncConfig.h>
 #include <ui/DisplayId.h>
@@ -250,11 +249,9 @@
         return std::const_pointer_cast<VsyncSchedule>(std::as_const(*this).getVsyncSchedule(idOpt));
     }
 
-    const FrameTarget& pacesetterFrameTarget() { return mPacesetterFrameTargeter.target(); }
-
     // Returns true if a given vsync timestamp is considered valid vsync
     // for a given uid
-    bool isVsyncValid(TimePoint expectedVsyncTime, uid_t uid) const;
+    bool isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const;
 
     bool isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const;
 
@@ -449,8 +446,6 @@
     ftl::Optional<PhysicalDisplayId> mPacesetterDisplayId GUARDED_BY(mDisplayLock)
             GUARDED_BY(kMainThreadContext);
 
-    FrameTargeter mPacesetterFrameTargeter{mFeatures.test(Feature::kBackpressureGpuComposition)};
-
     ftl::Optional<DisplayRef> pacesetterDisplayLocked() REQUIRES(mDisplayLock) {
         return static_cast<const Scheduler*>(this)->pacesetterDisplayLocked().transform(
                 [](const Display& display) { return std::ref(const_cast<Display&>(display)); });
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 47e92e1..58e0432 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -20,17 +20,14 @@
 #include <memory>
 #include <string>
 
+#include <ThreadContext.h>
 #include <android-base/thread_annotations.h>
 #include <ThreadContext.h>
 #include <ftl/enum.h>
 #include <ftl/optional.h>
-#include <ui/DisplayId.h>
-
 #include <scheduler/Features.h>
-#include <scheduler/IVsyncSource.h>
 #include <scheduler/Time.h>
-
-#include "ThreadContext.h"
+#include <ui/DisplayId.h>
 
 namespace android {
 class EventThreadTest;
@@ -52,16 +49,15 @@
 using VsyncTracker = VSyncTracker;
 
 // Schedule that synchronizes to hardware VSYNC of a physical display.
-class VsyncSchedule final : public IVsyncSource {
+class VsyncSchedule {
 public:
     using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>;
 
     VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync);
     ~VsyncSchedule();
 
-    // IVsyncSource overrides:
-    Period period() const override;
-    TimePoint vsyncDeadlineAfter(TimePoint) const override;
+    Period period() const;
+    TimePoint vsyncDeadlineAfter(TimePoint) const;
 
     // Inform the schedule that the period is changing and the schedule needs to recalibrate
     // itself. The schedule will end the period transition internally. This will
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Features.h b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
index 200407d..b3a6a60 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Features.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
@@ -23,11 +23,10 @@
 namespace android::scheduler {
 
 enum class Feature : std::uint8_t {
-    kPresentFences = 1 << 0,
-    kKernelIdleTimer = 1 << 1,
-    kContentDetection = 1 << 2,
-    kTracePredictedVsync = 1 << 3,
-    kBackpressureGpuComposition = 1 << 4,
+    kPresentFences = 0b1,
+    kKernelIdleTimer = 0b10,
+    kContentDetection = 0b100,
+    kTracePredictedVsync = 0b1000,
 };
 
 using FeatureFlags = ftl::Flags<Feature>;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
deleted file mode 100644
index 6878370..0000000
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2023 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 <array>
-#include <atomic>
-#include <memory>
-
-#include <ui/Fence.h>
-#include <ui/FenceTime.h>
-
-#include <scheduler/Time.h>
-#include <scheduler/VsyncId.h>
-#include <scheduler/interface/CompositeResult.h>
-
-// TODO(b/185536303): Pull to FTL.
-#include "../../../TracedOrdinal.h"
-#include "../../../Utils/Dumper.h"
-
-namespace android::scheduler {
-
-struct IVsyncSource;
-
-// Read-only interface to the metrics computed by FrameTargeter for the latest frame.
-class FrameTarget {
-public:
-    VsyncId vsyncId() const { return mVsyncId; }
-
-    // The time when the frame actually began, as opposed to when it had been scheduled to begin.
-    TimePoint frameBeginTime() const { return mFrameBeginTime; }
-
-    // Relative to when the frame actually began, as opposed to when it had been scheduled to begin.
-    Duration expectedFrameDuration() const { return mExpectedPresentTime - mFrameBeginTime; }
-
-    TimePoint expectedPresentTime() const { return mExpectedPresentTime; }
-
-    // The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details.
-    TimePoint pastVsyncTime(Period vsyncPeriod) const;
-
-    // The present fence for the frame that had targeted the most recent VSYNC before this frame.
-    // If the target VSYNC for any given frame is more than `vsyncPeriod` in the future, then the
-    // VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the
-    // fence of the previous frame if running N VSYNCs ahead, but the one that should have been
-    // signaled by now (unless that frame missed).
-    const FenceTimePtr& presentFenceForPastVsync(Period vsyncPeriod) const;
-
-    bool wouldPresentEarly(Period vsyncPeriod) const;
-
-    bool isFramePending() const { return mFramePending; }
-    bool didMissFrame() const { return mFrameMissed; }
-    bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; }
-
-protected:
-    ~FrameTarget() = default;
-
-    VsyncId mVsyncId;
-    TimePoint mFrameBeginTime;
-    TimePoint mExpectedPresentTime;
-
-    TracedOrdinal<bool> mFramePending{"PrevFramePending", false};
-    TracedOrdinal<bool> mFrameMissed{"PrevFrameMissed", false};
-    TracedOrdinal<bool> mHwcFrameMissed{"PrevHwcFrameMissed", false};
-    TracedOrdinal<bool> mGpuFrameMissed{"PrevGpuFrameMissed", false};
-
-    struct FenceWithFenceTime {
-        sp<Fence> fence = Fence::NO_FENCE;
-        FenceTimePtr fenceTime = FenceTime::NO_FENCE;
-    };
-    std::array<FenceWithFenceTime, 2> mPresentFences;
-
-private:
-    template <int N>
-    inline bool targetsVsyncsAhead(Period vsyncPeriod) const {
-        static_assert(N > 1);
-        return expectedFrameDuration() > (N - 1) * vsyncPeriod;
-    }
-};
-
-// Computes a display's per-frame metrics about past/upcoming targeting of present deadlines.
-class FrameTargeter final : private FrameTarget {
-public:
-    explicit FrameTargeter(bool backpressureGpuComposition)
-          : mBackpressureGpuComposition(backpressureGpuComposition) {}
-
-    const FrameTarget& target() const { return *this; }
-
-    struct BeginFrameArgs {
-        TimePoint frameBeginTime;
-        VsyncId vsyncId;
-        TimePoint expectedVsyncTime;
-        Duration sfWorkDuration;
-    };
-
-    void beginFrame(const BeginFrameArgs&, const IVsyncSource&);
-
-    // TODO(b/241285191): Merge with FrameTargeter::endFrame.
-    FenceTimePtr setPresentFence(sp<Fence>);
-
-    void endFrame(const CompositeResult&);
-
-    void dump(utils::Dumper&) const;
-
-private:
-    friend class FrameTargeterTest;
-
-    // For tests.
-    using IsFencePendingFuncPtr = bool (*)(const FenceTimePtr&, int graceTimeMs);
-    void beginFrame(const BeginFrameArgs&, const IVsyncSource&, IsFencePendingFuncPtr);
-    FenceTimePtr setPresentFence(sp<Fence>, FenceTimePtr);
-
-    static bool isFencePending(const FenceTimePtr&, int graceTimeMs);
-
-    const bool mBackpressureGpuComposition;
-
-    TimePoint mScheduledPresentTime;
-    CompositionCoverageFlags mCompositionCoverage;
-
-    std::atomic_uint mFrameMissedCount = 0;
-    std::atomic_uint mHwcFrameMissedCount = 0;
-    std::atomic_uint mGpuFrameMissedCount = 0;
-};
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
deleted file mode 100644
index bb2de75..0000000
--- a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2023 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 <scheduler/Time.h>
-
-namespace android::scheduler {
-
-struct IVsyncSource {
-    virtual Period period() const = 0;
-    virtual TimePoint vsyncDeadlineAfter(TimePoint) const = 0;
-
-protected:
-    ~IVsyncSource() = default;
-};
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h
deleted file mode 100644
index f795f1f..0000000
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositeResult.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2023 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 <scheduler/interface/CompositionCoverage.h>
-
-namespace android {
-
-struct CompositeResult {
-    CompositionCoverageFlags compositionCoverage;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
index 2696076..cc41925 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
@@ -18,15 +18,8 @@
 
 #include <scheduler/Time.h>
 #include <scheduler/VsyncId.h>
-#include <scheduler/interface/CompositeResult.h>
 
 namespace android {
-namespace scheduler {
-
-class FrameTarget;
-class FrameTargeter;
-
-} // namespace scheduler
 
 struct ICompositor {
     // Configures physical displays, processing hotplug and/or mode setting via the Composer HAL.
@@ -34,11 +27,11 @@
 
     // Commits transactions for layers and displays. Returns whether any state has been invalidated,
     // i.e. whether a frame should be composited for each display.
-    virtual bool commit(const scheduler::FrameTarget&) = 0;
+    virtual bool commit(TimePoint frameTime, VsyncId, TimePoint expectedVsyncTime) = 0;
 
     // Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition
     // via RenderEngine and the Composer HAL, respectively.
-    virtual CompositeResult composite(scheduler::FrameTargeter&) = 0;
+    virtual void composite(TimePoint frameTime, VsyncId) = 0;
 
     // Samples the composited frame via RegionSamplingThread.
     virtual void sample() = 0;
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
deleted file mode 100644
index cf4c0a0..0000000
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2023 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 <gui/TraceUtils.h>
-
-#include <scheduler/FrameTargeter.h>
-#include <scheduler/IVsyncSource.h>
-
-namespace android::scheduler {
-
-TimePoint FrameTarget::pastVsyncTime(Period vsyncPeriod) const {
-    // TODO(b/267315508): Generalize to N VSYNCs.
-    const int shift = static_cast<int>(targetsVsyncsAhead<2>(vsyncPeriod));
-    return mExpectedPresentTime - Period::fromNs(vsyncPeriod.ns() << shift);
-}
-
-const FenceTimePtr& FrameTarget::presentFenceForPastVsync(Period vsyncPeriod) const {
-    // TODO(b/267315508): Generalize to N VSYNCs.
-    const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(vsyncPeriod));
-    return mPresentFences[i].fenceTime;
-}
-
-bool FrameTarget::wouldPresentEarly(Period vsyncPeriod) const {
-    // TODO(b/267315508): Generalize to N VSYNCs.
-    if (targetsVsyncsAhead<3>(vsyncPeriod)) {
-        return true;
-    }
-
-    const auto fence = presentFenceForPastVsync(vsyncPeriod);
-    return fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
-}
-
-void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& vsyncSource) {
-    return beginFrame(args, vsyncSource, &FrameTargeter::isFencePending);
-}
-
-void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& vsyncSource,
-                               IsFencePendingFuncPtr isFencePendingFuncPtr) {
-    mVsyncId = args.vsyncId;
-    mFrameBeginTime = args.frameBeginTime;
-
-    // The `expectedVsyncTime`, which was predicted when this frame was scheduled, is normally in
-    // the future relative to `frameBeginTime`, but may not be for delayed frames. Adjust
-    // `mExpectedPresentTime` accordingly, but not `mScheduledPresentTime`.
-    const TimePoint lastScheduledPresentTime = mScheduledPresentTime;
-    mScheduledPresentTime = args.expectedVsyncTime;
-
-    const Period vsyncPeriod = vsyncSource.period();
-
-    // Calculate the expected present time once and use the cached value throughout this frame to
-    // make sure all layers are seeing this same value.
-    if (args.expectedVsyncTime >= args.frameBeginTime) {
-        mExpectedPresentTime = args.expectedVsyncTime;
-    } else {
-        mExpectedPresentTime = vsyncSource.vsyncDeadlineAfter(args.frameBeginTime);
-        if (args.sfWorkDuration > vsyncPeriod) {
-            // Inflate the expected present time if we're targeting the next VSYNC.
-            mExpectedPresentTime += vsyncPeriod;
-        }
-    }
-
-    ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(args.vsyncId),
-                  ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
-                  mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)");
-
-    const FenceTimePtr& pastPresentFence = presentFenceForPastVsync(vsyncPeriod);
-
-    // In cases where the present fence is about to fire, give it a small grace period instead of
-    // giving up on the frame.
-    //
-    // TODO(b/280667110): The grace period should depend on `sfWorkDuration` and `vsyncPeriod` being
-    // approximately equal, not whether backpressure propagation is enabled.
-    const int graceTimeForPresentFenceMs = static_cast<int>(
-            mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu));
-
-    // Pending frames may trigger backpressure propagation.
-    const auto& isFencePending = *isFencePendingFuncPtr;
-    mFramePending = pastPresentFence != FenceTime::NO_FENCE &&
-            isFencePending(pastPresentFence, graceTimeForPresentFenceMs);
-
-    // A frame is missed if the prior frame is still pending. If no longer pending, then we still
-    // count the frame as missed if the predicted present time was further in the past than when the
-    // fence actually fired. Add some slop to correct for drift. This should generally be smaller
-    // than a typical frame duration, but should not be so small that it reports reasonable drift as
-    // a missed frame.
-    mFrameMissed = mFramePending || [&] {
-        const nsecs_t pastPresentTime = pastPresentFence->getSignalTime();
-        if (pastPresentTime < 0) return false;
-        const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
-        return lastScheduledPresentTime.ns() < pastPresentTime - frameMissedSlop;
-    }();
-
-    mHwcFrameMissed = mFrameMissed && mCompositionCoverage.test(CompositionCoverage::Hwc);
-    mGpuFrameMissed = mFrameMissed && mCompositionCoverage.test(CompositionCoverage::Gpu);
-
-    if (mFrameMissed) mFrameMissedCount++;
-    if (mHwcFrameMissed) mHwcFrameMissedCount++;
-    if (mGpuFrameMissed) mGpuFrameMissedCount++;
-}
-
-void FrameTargeter::endFrame(const CompositeResult& result) {
-    mCompositionCoverage = result.compositionCoverage;
-}
-
-FenceTimePtr FrameTargeter::setPresentFence(sp<Fence> presentFence) {
-    auto presentFenceTime = std::make_shared<FenceTime>(presentFence);
-    return setPresentFence(std::move(presentFence), std::move(presentFenceTime));
-}
-
-FenceTimePtr FrameTargeter::setPresentFence(sp<Fence> presentFence, FenceTimePtr presentFenceTime) {
-    mPresentFences[1] = mPresentFences[0];
-    mPresentFences[0] = {std::move(presentFence), presentFenceTime};
-    return presentFenceTime;
-}
-
-void FrameTargeter::dump(utils::Dumper& dumper) const {
-    using namespace std::string_view_literals;
-
-    utils::Dumper::Section section(dumper, "Frame Targeting"sv);
-
-    dumper.dump("frameMissedCount"sv, mFrameMissedCount);
-    dumper.dump("hwcFrameMissedCount"sv, mHwcFrameMissedCount);
-    dumper.dump("gpuFrameMissedCount"sv, mGpuFrameMissedCount);
-}
-
-bool FrameTargeter::isFencePending(const FenceTimePtr& fence, int graceTimeMs) {
-    ATRACE_CALL();
-    const status_t status = fence->wait(graceTimeMs);
-
-    // This is the same as Fence::Status::Unsignaled, but it saves a call to getStatus,
-    // which calls wait(0) again internally.
-    return status == -ETIME;
-}
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
deleted file mode 100644
index 908f214..0000000
--- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <ftl/optional.h>
-#include <gtest/gtest.h>
-
-#include <scheduler/Fps.h>
-#include <scheduler/FrameTargeter.h>
-#include <scheduler/IVsyncSource.h>
-
-using namespace std::chrono_literals;
-
-namespace android::scheduler {
-namespace {
-
-struct VsyncSource final : IVsyncSource {
-    VsyncSource(Period period, TimePoint deadline) : vsyncPeriod(period), vsyncDeadline(deadline) {}
-
-    const Period vsyncPeriod;
-    const TimePoint vsyncDeadline;
-
-    Period period() const override { return vsyncPeriod; }
-    TimePoint vsyncDeadlineAfter(TimePoint) const override { return vsyncDeadline; }
-};
-
-} // namespace
-
-class FrameTargeterTest : public testing::Test {
-public:
-    const auto& target() const { return mTargeter.target(); }
-
-    struct Frame {
-        Frame(FrameTargeterTest* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime,
-              Duration frameDuration, Fps refreshRate,
-              FrameTargeter::IsFencePendingFuncPtr isFencePendingFuncPtr = Frame::fenceSignaled,
-              const ftl::Optional<VsyncSource>& vsyncSourceOpt = std::nullopt)
-              : testPtr(testPtr), frameBeginTime(frameBeginTime), period(refreshRate.getPeriod()) {
-            const FrameTargeter::BeginFrameArgs args{.frameBeginTime = frameBeginTime,
-                                                     .vsyncId = vsyncId,
-                                                     .expectedVsyncTime =
-                                                             frameBeginTime + frameDuration,
-                                                     .sfWorkDuration = 10ms};
-
-            testPtr->mTargeter.beginFrame(args,
-                                          vsyncSourceOpt
-                                                  .or_else([&] {
-                                                      return std::make_optional(
-                                                              VsyncSource(period,
-                                                                          args.expectedVsyncTime));
-                                                  })
-                                                  .value(),
-                                          isFencePendingFuncPtr);
-        }
-
-        FenceTimePtr end(CompositionCoverage coverage = CompositionCoverage::Hwc) {
-            if (ended) return nullptr;
-            ended = true;
-
-            auto [fence, fenceTime] = testPtr->mFenceMap.makePendingFenceForTest();
-            testPtr->mTargeter.setPresentFence(std::move(fence), fenceTime);
-
-            testPtr->mTargeter.endFrame({.compositionCoverage = coverage});
-            return fenceTime;
-        }
-
-        ~Frame() {
-            end();
-            frameBeginTime += period;
-        }
-
-        static bool fencePending(const FenceTimePtr&, int) { return true; }
-        static bool fenceSignaled(const FenceTimePtr&, int) { return false; }
-
-        FrameTargeterTest* const testPtr;
-
-        TimePoint& frameBeginTime;
-        const Period period;
-
-        bool ended = false;
-    };
-
-private:
-    FenceToFenceTimeMap mFenceMap;
-
-    static constexpr bool kBackpressureGpuComposition = true;
-    FrameTargeter mTargeter{kBackpressureGpuComposition};
-};
-
-TEST_F(FrameTargeterTest, targetsFrames) {
-    VsyncId vsyncId{42};
-    {
-        TimePoint frameBeginTime(989ms);
-        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, 60_Hz);
-
-        EXPECT_EQ(target().vsyncId(), VsyncId{42});
-        EXPECT_EQ(target().frameBeginTime(), TimePoint(989ms));
-        EXPECT_EQ(target().expectedPresentTime(), TimePoint(999ms));
-        EXPECT_EQ(target().expectedFrameDuration(), 10ms);
-    }
-    {
-        TimePoint frameBeginTime(1100ms);
-        const Frame frame(this, vsyncId++, frameBeginTime, 11ms, 60_Hz);
-
-        EXPECT_EQ(target().vsyncId(), VsyncId{43});
-        EXPECT_EQ(target().frameBeginTime(), TimePoint(1100ms));
-        EXPECT_EQ(target().expectedPresentTime(), TimePoint(1111ms));
-        EXPECT_EQ(target().expectedFrameDuration(), 11ms);
-    }
-}
-
-TEST_F(FrameTargeterTest, inflatesExpectedPresentTime) {
-    // Negative such that `expectedVsyncTime` is in the past.
-    constexpr Duration kFrameDuration = -3ms;
-    TimePoint frameBeginTime(777ms);
-
-    constexpr Fps kRefreshRate = 120_Hz;
-    const VsyncSource vsyncSource(kRefreshRate.getPeriod(), frameBeginTime + 5ms);
-    const Frame frame(this, VsyncId{123}, frameBeginTime, kFrameDuration, kRefreshRate,
-                      Frame::fenceSignaled, vsyncSource);
-
-    EXPECT_EQ(target().expectedPresentTime(), vsyncSource.vsyncDeadline + vsyncSource.vsyncPeriod);
-}
-
-TEST_F(FrameTargeterTest, recallsPastVsync) {
-    VsyncId vsyncId{111};
-    TimePoint frameBeginTime(1000ms);
-    constexpr Fps kRefreshRate = 60_Hz;
-    constexpr Period kPeriod = kRefreshRate.getPeriod();
-    constexpr Duration kFrameDuration = 13ms;
-
-    for (int n = 5; n-- > 0;) {
-        Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate);
-        const auto fence = frame.end();
-
-        EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - kPeriod);
-        EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), fence);
-    }
-}
-
-TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAhead) {
-    VsyncId vsyncId{222};
-    TimePoint frameBeginTime(2000ms);
-    constexpr Fps kRefreshRate = 120_Hz;
-    constexpr Period kPeriod = kRefreshRate.getPeriod();
-    constexpr Duration kFrameDuration = 10ms;
-
-    FenceTimePtr previousFence = FenceTime::NO_FENCE;
-
-    for (int n = 5; n-- > 0;) {
-        Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate);
-        const auto fence = frame.end();
-
-        EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
-        EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence);
-
-        previousFence = fence;
-    }
-}
-
-TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) {
-    constexpr Period kPeriod = (60_Hz).getPeriod();
-    EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), FenceTime::NO_FENCE);
-    EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
-}
-
-TEST_F(FrameTargeterTest, detectsEarlyPresent) {
-    VsyncId vsyncId{333};
-    TimePoint frameBeginTime(3000ms);
-    constexpr Fps kRefreshRate = 60_Hz;
-    constexpr Period kPeriod = kRefreshRate.getPeriod();
-
-    // The target is not early while past present fences are pending.
-    for (int n = 3; n-- > 0;) {
-        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
-        EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
-    }
-
-    // The target is early if the past present fence was signaled.
-    Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
-    const auto fence = frame.end();
-    fence->signalForTest(frameBeginTime.ns());
-
-    EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
-}
-
-TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) {
-    VsyncId vsyncId{444};
-    TimePoint frameBeginTime(4000ms);
-    constexpr Fps kRefreshRate = 120_Hz;
-    constexpr Period kPeriod = kRefreshRate.getPeriod();
-
-    // The target is not early while past present fences are pending.
-    for (int n = 3; n-- > 0;) {
-        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
-        EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
-    }
-
-    Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
-    const auto fence = frame.end();
-    fence->signalForTest(frameBeginTime.ns());
-
-    // The target is two VSYNCs ahead, so the past present fence is still pending.
-    EXPECT_FALSE(target().wouldPresentEarly(kPeriod));
-
-    { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); }
-
-    // The target is early if the past present fence was signaled.
-    EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
-}
-
-TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) {
-    TimePoint frameBeginTime(5000ms);
-    constexpr Fps kRefreshRate = 144_Hz;
-    constexpr Period kPeriod = kRefreshRate.getPeriod();
-
-    const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate);
-
-    // The target is more than two VSYNCs ahead, but present fences are not tracked that far back.
-    EXPECT_TRUE(target().wouldPresentEarly(kPeriod));
-}
-
-TEST_F(FrameTargeterTest, detectsMissedFrames) {
-    VsyncId vsyncId{555};
-    TimePoint frameBeginTime(5000ms);
-    constexpr Fps kRefreshRate = 60_Hz;
-    constexpr Period kPeriod = kRefreshRate.getPeriod();
-
-    EXPECT_FALSE(target().isFramePending());
-    EXPECT_FALSE(target().didMissFrame());
-    EXPECT_FALSE(target().didMissHwcFrame());
-
-    {
-        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
-        EXPECT_FALSE(target().isFramePending());
-
-        // The frame did not miss if the past present fence is invalid.
-        EXPECT_FALSE(target().didMissFrame());
-        EXPECT_FALSE(target().didMissHwcFrame());
-    }
-    {
-        Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, Frame::fencePending);
-        EXPECT_TRUE(target().isFramePending());
-
-        // The frame missed if the past present fence is pending.
-        EXPECT_TRUE(target().didMissFrame());
-        EXPECT_TRUE(target().didMissHwcFrame());
-
-        frame.end(CompositionCoverage::Gpu);
-    }
-    {
-        const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, Frame::fencePending);
-        EXPECT_TRUE(target().isFramePending());
-
-        // The GPU frame missed if the past present fence is pending.
-        EXPECT_TRUE(target().didMissFrame());
-        EXPECT_FALSE(target().didMissHwcFrame());
-    }
-    {
-        Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
-        EXPECT_FALSE(target().isFramePending());
-
-        const auto fence = frame.end();
-        const auto expectedPresentTime = target().expectedPresentTime();
-        fence->signalForTest(expectedPresentTime.ns() + kPeriod.ns() / 2 + 1);
-    }
-    {
-        Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
-        EXPECT_FALSE(target().isFramePending());
-
-        const auto fence = frame.end();
-        const auto expectedPresentTime = target().expectedPresentTime();
-        fence->signalForTest(expectedPresentTime.ns() + kPeriod.ns() / 2);
-
-        // The frame missed if the past present fence was signaled but not within slop.
-        EXPECT_TRUE(target().didMissFrame());
-        EXPECT_TRUE(target().didMissHwcFrame());
-    }
-    {
-        Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate);
-        EXPECT_FALSE(target().isFramePending());
-
-        // The frame did not miss if the past present fence was signaled within slop.
-        EXPECT_FALSE(target().didMissFrame());
-        EXPECT_FALSE(target().didMissHwcFrame());
-    }
-}
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp b/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp
index df2ea83..8952ca9 100644
--- a/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp
@@ -23,6 +23,16 @@
 #include <ui/FenceTime.h>
 
 namespace android::scheduler {
+namespace {
+
+using FencePair = std::pair<sp<Fence>, std::shared_ptr<FenceTime>>;
+
+FencePair makePendingFence(FenceToFenceTimeMap& fenceMap) {
+    const auto fence = sp<Fence>::make();
+    return {fence, fenceMap.createFenceTimeForTest(fence)};
+}
+
+} // namespace
 
 TEST(PresentLatencyTrackerTest, skipsInvalidFences) {
     PresentLatencyTracker tracker;
@@ -33,7 +43,7 @@
     EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE), Duration::zero());
 
     FenceToFenceTimeMap fenceMap;
-    const auto [fence, fenceTime] = fenceMap.makePendingFenceForTest();
+    const auto [fence, fenceTime] = makePendingFence(fenceMap);
     EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, fenceTime), Duration::zero());
 
     fenceTime->signalForTest(9999);
@@ -46,9 +56,8 @@
     PresentLatencyTracker tracker;
 
     FenceToFenceTimeMap fenceMap;
-    std::array<FenceToFenceTimeMap::FencePair, PresentLatencyTracker::kMaxPendingFrames> fences;
-    std::generate(fences.begin(), fences.end(),
-                  [&fenceMap] { return fenceMap.makePendingFenceForTest(); });
+    std::array<FencePair, PresentLatencyTracker::kMaxPendingFrames> fences;
+    std::generate(fences.begin(), fences.end(), [&fenceMap] { return makePendingFence(fenceMap); });
 
     // The present latency is 0 if all fences are pending.
     const TimePoint kCompositeTime = TimePoint::fromNs(1234);
@@ -62,7 +71,7 @@
         fences[i].second->signalForTest(kCompositeTime.ns() + static_cast<nsecs_t>(i));
     }
 
-    const auto fence = fenceMap.makePendingFenceForTest();
+    const auto fence = makePendingFence(fenceMap);
 
     // ...then the present latency is measured using the latest frame.
     constexpr Duration kPresentLatency = Duration::fromNs(static_cast<nsecs_t>(kPresentCount) - 1);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f665529..8f506f1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -77,7 +77,6 @@
 #include <processgroup/processgroup.h>
 #include <renderengine/RenderEngine.h>
 #include <renderengine/impl/ExternalTexture.h>
-#include <scheduler/FrameTargeter.h>
 #include <sys/types.h>
 #include <ui/ColorSpace.h>
 #include <ui/DataspaceUtils.h>
@@ -2142,6 +2141,44 @@
     }
 }
 
+bool SurfaceFlinger::wouldPresentEarly(TimePoint frameTime, Period vsyncPeriod) const {
+    const bool isThreeVsyncsAhead = mExpectedPresentTime - frameTime > 2 * vsyncPeriod;
+    return isThreeVsyncsAhead ||
+            getPreviousPresentFence(frameTime, vsyncPeriod)->getSignalTime() !=
+            Fence::SIGNAL_TIME_PENDING;
+}
+
+auto SurfaceFlinger::getPreviousPresentFence(TimePoint frameTime, Period vsyncPeriod) const
+        -> const FenceTimePtr& {
+    const bool isTwoVsyncsAhead = mExpectedPresentTime - frameTime > vsyncPeriod;
+    const size_t i = static_cast<size_t>(isTwoVsyncsAhead);
+    return mPreviousPresentFences[i].fenceTime;
+}
+
+bool SurfaceFlinger::isFencePending(const FenceTimePtr& fence, int graceTimeMs) {
+    ATRACE_CALL();
+    if (fence == FenceTime::NO_FENCE) {
+        return false;
+    }
+
+    const status_t status = fence->wait(graceTimeMs);
+    // This is the same as Fence::Status::Unsignaled, but it saves a getStatus() call,
+    // which calls wait(0) again internally
+    return status == -ETIME;
+}
+
+TimePoint SurfaceFlinger::calculateExpectedPresentTime(TimePoint frameTime) const {
+    const auto& schedule = mScheduler->getVsyncSchedule();
+
+    const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(frameTime);
+    if (mScheduler->vsyncModulator().getVsyncConfig().sfOffset > 0) {
+        return vsyncDeadline;
+    }
+
+    // Inflate the expected present time if we're targeting the next vsync.
+    return vsyncDeadline + schedule->period();
+}
+
 void SurfaceFlinger::configure() FTL_FAKE_GUARD(kMainThreadContext) {
     Mutex::Autolock lock(mStateLock);
     if (configureLocked()) {
@@ -2315,15 +2352,75 @@
     return mustComposite;
 }
 
-bool SurfaceFlinger::commit(const scheduler::FrameTarget& pacesetterFrameTarget)
+bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime)
         FTL_FAKE_GUARD(kMainThreadContext) {
-    const VsyncId vsyncId = pacesetterFrameTarget.vsyncId();
-    ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
+    // The expectedVsyncTime, which was predicted when this frame was scheduled, is normally in the
+    // future relative to frameTime, but may not be for delayed frames. Adjust mExpectedPresentTime
+    // accordingly, but not mScheduledPresentTime.
+    const TimePoint lastScheduledPresentTime = mScheduledPresentTime;
+    mScheduledPresentTime = expectedVsyncTime;
 
-    if (pacesetterFrameTarget.didMissFrame()) {
+    // Calculate the expected present time once and use the cached value throughout this frame to
+    // make sure all layers are seeing this same value.
+    mExpectedPresentTime = expectedVsyncTime >= frameTime ? expectedVsyncTime
+                                                          : calculateExpectedPresentTime(frameTime);
+
+    ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(vsyncId),
+                  ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
+                  mExpectedPresentTime == expectedVsyncTime ? "" : " (adjusted)");
+
+    const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period();
+    const FenceTimePtr& previousPresentFence = getPreviousPresentFence(frameTime, vsyncPeriod);
+
+    // When backpressure propagation is enabled, we want to give a small grace period of 1ms
+    // for the present fence to fire instead of just giving up on this frame to handle cases
+    // where present fence is just about to get signaled.
+    const int graceTimeForPresentFenceMs = static_cast<int>(
+            mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu));
+
+    // Pending frames may trigger backpressure propagation.
+    const TracedOrdinal<bool> framePending = {"PrevFramePending",
+                                              isFencePending(previousPresentFence,
+                                                             graceTimeForPresentFenceMs)};
+
+    // Frame missed counts for metrics tracking.
+    // A frame is missed if the prior frame is still pending. If no longer pending,
+    // then we still count the frame as missed if the predicted present time
+    // was further in the past than when the fence actually fired.
+
+    // Add some slop to correct for drift. This should generally be
+    // smaller than a typical frame duration, but should not be so small
+    // that it reports reasonable drift as a missed frame.
+    const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
+    const nsecs_t previousPresentTime = previousPresentFence->getSignalTime();
+    const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
+                                             framePending ||
+                                                     (previousPresentTime >= 0 &&
+                                                      (lastScheduledPresentTime.ns() <
+                                                       previousPresentTime - frameMissedSlop))};
+    const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed",
+                                                frameMissed &&
+                                                        mCompositionCoverage.test(
+                                                                CompositionCoverage::Hwc)};
+
+    const TracedOrdinal<bool> gpuFrameMissed = {"PrevGpuFrameMissed",
+                                                frameMissed &&
+                                                        mCompositionCoverage.test(
+                                                                CompositionCoverage::Gpu)};
+
+    if (frameMissed) {
+        mFrameMissedCount++;
         mTimeStats->incrementMissedFrames();
     }
 
+    if (hwcFrameMissed) {
+        mHwcFrameMissedCount++;
+    }
+
+    if (gpuFrameMissed) {
+        mGpuFrameMissedCount++;
+    }
+
     if (mTracingEnabledChanged) {
         mLayerTracingEnabled = mLayerTracing.isEnabled();
         mTracingEnabledChanged = false;
@@ -2332,7 +2429,7 @@
     // If we are in the middle of a mode change and the fence hasn't
     // fired yet just wait for the next commit.
     if (mSetActiveModePending) {
-        if (pacesetterFrameTarget.isFramePending()) {
+        if (framePending) {
             mScheduler->scheduleFrame();
             return false;
         }
@@ -2346,29 +2443,26 @@
         }
     }
 
-    if (pacesetterFrameTarget.isFramePending()) {
-        if (mBackpressureGpuComposition || pacesetterFrameTarget.didMissHwcFrame()) {
+    if (framePending) {
+        if (mBackpressureGpuComposition || (hwcFrameMissed && !gpuFrameMissed)) {
             scheduleCommit(FrameHint::kNone);
             return false;
         }
     }
 
-    const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period();
-
     // Save this once per commit + composite to ensure consistency
     // TODO (b/240619471): consider removing active display check once AOD is fixed
     const auto activeDisplay = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayId));
     mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay &&
             activeDisplay->getPowerMode() == hal::PowerMode::ON;
     if (mPowerHintSessionEnabled) {
-        mPowerAdvisor->setCommitStart(pacesetterFrameTarget.frameBeginTime());
-        mPowerAdvisor->setExpectedPresentTime(pacesetterFrameTarget.expectedPresentTime());
+        mPowerAdvisor->setCommitStart(frameTime);
+        mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime);
 
         // Frame delay is how long we should have minus how long we actually have.
         const Duration idealSfWorkDuration =
                 mScheduler->vsyncModulator().getVsyncConfig().sfWorkDuration;
-        const Duration frameDelay =
-                idealSfWorkDuration - pacesetterFrameTarget.expectedFrameDuration();
+        const Duration frameDelay = idealSfWorkDuration - (mExpectedPresentTime - frameTime);
 
         mPowerAdvisor->setFrameDelay(frameDelay);
         mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration);
@@ -2385,8 +2479,7 @@
     // Composite if transactions were committed, or if requested by HWC.
     bool mustComposite = mMustComposite.exchange(false);
     {
-        mFrameTimeline->setSfWakeUp(ftl::to_underlying(vsyncId),
-                                    pacesetterFrameTarget.frameBeginTime().ns(),
+        mFrameTimeline->setSfWakeUp(ftl::to_underlying(vsyncId), frameTime.ns(),
                                     Fps::fromPeriodNsecs(vsyncPeriod.ns()));
 
         const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
@@ -2394,11 +2487,10 @@
         if (flushTransactions) {
             updates = flushLifecycleUpdates();
             if (mTransactionTracing) {
-                mTransactionTracing
-                        ->addCommittedTransactions(ftl::to_underlying(vsyncId),
-                                                   pacesetterFrameTarget.frameBeginTime().ns(),
-                                                   updates, mFrontEndDisplayInfos,
-                                                   mFrontEndDisplayInfosChanged);
+                mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId),
+                                                              frameTime.ns(), updates,
+                                                              mFrontEndDisplayInfos,
+                                                              mFrontEndDisplayInfosChanged);
             }
         }
         bool transactionsAreEmpty;
@@ -2441,7 +2533,7 @@
 
     if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
         // This will block and tracing should only be enabled for debugging.
-        addToLayerTracing(mVisibleRegionsDirty, pacesetterFrameTarget.frameBeginTime(), vsyncId);
+        addToLayerTracing(mVisibleRegionsDirty, frameTime, vsyncId);
     }
     mLastCommittedVsyncId = vsyncId;
 
@@ -2450,11 +2542,8 @@
     return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
 }
 
-CompositeResult SurfaceFlinger::composite(scheduler::FrameTargeter& pacesetterFrameTargeter)
+void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId)
         FTL_FAKE_GUARD(kMainThreadContext) {
-    const scheduler::FrameTarget& pacesetterFrameTarget = pacesetterFrameTargeter.target();
-
-    const VsyncId vsyncId = pacesetterFrameTarget.vsyncId();
     ATRACE_NAME(ftl::Concat(__func__, ' ', ftl::to_underlying(vsyncId)).c_str());
 
     compositionengine::CompositionRefreshArgs refreshArgs;
@@ -2462,18 +2551,17 @@
     refreshArgs.outputs.reserve(displays.size());
     std::vector<DisplayId> displayIds;
     for (const auto& [_, display] : displays) {
-        displayIds.push_back(display->getId());
-        display->tracePowerMode();
-
+        bool dropFrame = false;
         if (display->isVirtual()) {
-            const Fps refreshRate = display->getAdjustedRefreshRate();
-            if (refreshRate.isValid() &&
-                !mScheduler->isVsyncInPhase(pacesetterFrameTarget.frameBeginTime(), refreshRate)) {
-                continue;
-            }
+            Fps refreshRate = display->getAdjustedRefreshRate();
+            using fps_approx_ops::operator>;
+            dropFrame = (refreshRate > 0_Hz) && !mScheduler->isVsyncInPhase(frameTime, refreshRate);
         }
-
-        refreshArgs.outputs.push_back(display->getCompositionDisplay());
+        if (!dropFrame) {
+            refreshArgs.outputs.push_back(display->getCompositionDisplay());
+        }
+        display->tracePowerMode();
+        displayIds.push_back(display->getId());
     }
     mPowerAdvisor->setDisplays(displayIds);
 
@@ -2533,15 +2621,15 @@
 
     if (!getHwComposer().getComposer()->isSupported(
                 Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
-        pacesetterFrameTarget.wouldPresentEarly(vsyncPeriod)) {
+        wouldPresentEarly(frameTime, vsyncPeriod)) {
+        const auto prevVsyncTime = mExpectedPresentTime - vsyncPeriod;
         const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
 
-        refreshArgs.earliestPresentTime =
-                pacesetterFrameTarget.pastVsyncTime(vsyncPeriod) - hwcMinWorkDuration;
+        refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration;
     }
 
     refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
-    refreshArgs.expectedPresentTime = pacesetterFrameTarget.expectedPresentTime().ns();
+    refreshArgs.expectedPresentTime = mExpectedPresentTime.ns();
     refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0;
 
     // Store the present time just before calling to the composition engine so we could notify
@@ -2567,12 +2655,13 @@
         }
     }
 
-    mTimeStats->recordFrameDuration(pacesetterFrameTarget.frameBeginTime().ns(), systemTime());
+    mTimeStats->recordFrameDuration(frameTime.ns(), systemTime());
 
     // Send a power hint hint after presentation is finished
     if (mPowerHintSessionEnabled) {
         const nsecs_t pastPresentTime =
-                pacesetterFrameTarget.presentFenceForPastVsync(vsyncPeriod)->getSignalTime();
+                getPreviousPresentFence(frameTime, vsyncPeriod)->getSignalTime();
+
         mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(pastPresentTime), TimePoint::now());
         mPowerAdvisor->reportActualWorkDuration();
     }
@@ -2581,7 +2670,7 @@
         scheduleComposite(FrameHint::kNone);
     }
 
-    postComposition(pacesetterFrameTargeter, presentTime);
+    postComposition(presentTime);
 
     const bool hadGpuComposited = mCompositionCoverage.test(CompositionCoverage::Gpu);
     mCompositionCoverage.clear();
@@ -2624,7 +2713,7 @@
     mLayersWithQueuedFrames.clear();
     if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
         // This will block and should only be used for debugging.
-        addToLayerTracing(mVisibleRegionsDirty, pacesetterFrameTarget.frameBeginTime(), vsyncId);
+        addToLayerTracing(mVisibleRegionsDirty, frameTime, vsyncId);
     }
 
     if (mVisibleRegionsDirty) mHdrLayerInfoChanged = true;
@@ -2637,8 +2726,6 @@
     if (mPowerHintSessionEnabled) {
         mPowerAdvisor->setCompositeEnd(TimePoint::now());
     }
-
-    return {mCompositionCoverage};
 }
 
 void SurfaceFlinger::updateLayerGeometry() {
@@ -2722,8 +2809,7 @@
     return ui::ROTATION_0;
 }
 
-void SurfaceFlinger::postComposition(scheduler::FrameTargeter& pacesetterFrameTargeter,
-                                     nsecs_t presentStartTime) {
+void SurfaceFlinger::postComposition(nsecs_t callTime) {
     ATRACE_CALL();
     ALOGV(__func__);
 
@@ -2740,11 +2826,15 @@
         glCompositionDoneFenceTime = FenceTime::NO_FENCE;
     }
 
+    mPreviousPresentFences[1] = mPreviousPresentFences[0];
+
     auto presentFence = defaultDisplay
             ? getHwComposer().getPresentFence(defaultDisplay->getPhysicalId())
             : Fence::NO_FENCE;
 
-    auto presentFenceTime = pacesetterFrameTargeter.setPresentFence(presentFence);
+    auto presentFenceTime = std::make_shared<FenceTime>(presentFence);
+    mPreviousPresentFences[0] = {presentFence, presentFenceTime};
+
     const TimePoint presentTime = TimePoint::now();
 
     // Set presentation information before calling Layer::releasePendingBuffer, such that jank
@@ -2927,7 +3017,7 @@
             if (!layer->hasTrustedPresentationListener()) {
                 return;
             }
-            const frontend::LayerSnapshot* snapshot = mLayerLifecycleManagerEnabled
+            const frontend::LayerSnapshot* snapshot = (mLayerLifecycleManagerEnabled)
                     ? mLayerSnapshotBuilder.getSnapshot(layer->sequence)
                     : layer->getLayerSnapshot();
             std::optional<const DisplayDevice*> displayOpt = std::nullopt;
@@ -2936,8 +3026,7 @@
             }
             const DisplayDevice* display = displayOpt.value_or(nullptr);
             layer->updateTrustedPresentationState(display, snapshot,
-                                                  nanoseconds_to_milliseconds(presentStartTime),
-                                                  false);
+                                                  nanoseconds_to_milliseconds(callTime), false);
         });
     }
 
@@ -3849,9 +3938,6 @@
     if (display->refreshRateSelector().kernelIdleTimerController()) {
         features |= Feature::kKernelIdleTimer;
     }
-    if (mBackpressureGpuComposition) {
-        features |= Feature::kBackpressureGpuComposition;
-    }
 
     auto modulatorPtr = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
 
@@ -4169,38 +4255,33 @@
 
 TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelineCheck(
         const TransactionHandler::TransactionFlushState& flushState) {
-    const auto& transaction = *flushState.transaction;
-
-    const TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
-    const TimePoint expectedPresentTime = mScheduler->pacesetterFrameTarget().expectedPresentTime();
-
     using TransactionReadiness = TransactionHandler::TransactionReadiness;
-
+    const auto& transaction = *flushState.transaction;
+    TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
     // 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 (!transaction.isAutoTimestamp && desiredPresentTime >= expectedPresentTime &&
-        desiredPresentTime < expectedPresentTime + 1s) {
+    if (!transaction.isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
+        desiredPresentTime < mExpectedPresentTime + 1s) {
         ATRACE_FORMAT("not current desiredPresentTime: %" PRId64 " expectedPresentTime: %" PRId64,
-                      desiredPresentTime, expectedPresentTime);
+                      desiredPresentTime, mExpectedPresentTime);
         return TransactionReadiness::NotReady;
     }
 
-    if (!mScheduler->isVsyncValid(expectedPresentTime, transaction.originUid)) {
-        ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d", expectedPresentTime,
-                      transaction.originUid);
+    if (!mScheduler->isVsyncValid(mExpectedPresentTime, transaction.originUid)) {
+        ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d",
+                      mExpectedPresentTime, transaction.originUid);
         return TransactionReadiness::NotReady;
     }
 
     // If the client didn't specify desiredPresentTime, use the vsyncId to determine the
     // expected present time of this transaction.
     if (transaction.isAutoTimestamp &&
-        frameIsEarly(expectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) {
+        frameIsEarly(mExpectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) {
         ATRACE_FORMAT("frameIsEarly vsyncId: %" PRId64 " expectedPresentTime: %" PRId64,
-                      transaction.frameTimelineInfo.vsyncId, expectedPresentTime);
+                      transaction.frameTimelineInfo.vsyncId, mExpectedPresentTime);
         return TransactionReadiness::NotReady;
     }
-
     return TransactionReadiness::Ready;
 }
 
@@ -5944,6 +6025,10 @@
     dumpVsync(result);
     result.append("\n");
 
+    StringAppendF(&result, "Total missed frame count: %u\n", mFrameMissedCount.load());
+    StringAppendF(&result, "HWC missed frame count: %u\n", mHwcFrameMissedCount.load());
+    StringAppendF(&result, "GPU missed frame count: %u\n\n", mGpuFrameMissedCount.load());
+
     /*
      * Dump the visible layer list
      */
@@ -6901,7 +6986,8 @@
 
     const auto dataspaceForColorMode = ui::pickDataspaceFor(state.colorMode);
 
-    if (capturingHdrLayers && !hintForSeamlessTransition) {
+    // TODO: Enable once HDR screenshots are ready.
+    if constexpr (/* DISABLES CODE */ (false)) {
         // For now since we only support 8-bit screenshots, just use HLG and
         // assume that 1.0 >= display max luminance. This isn't quite as future
         // proof as PQ is, but is good enough.
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 5a6f22c..6b9ba8c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -632,8 +632,8 @@
 
     // ICompositor overrides:
     void configure() override;
-    bool commit(const scheduler::FrameTarget&) override;
-    CompositeResult composite(scheduler::FrameTargeter&) override;
+    bool commit(TimePoint frameTime, VsyncId, TimePoint expectedVsyncTime) override;
+    void composite(TimePoint frameTime, VsyncId) override;
     void sample() override;
 
     // ISchedulerCallback overrides:
@@ -952,8 +952,7 @@
     /*
      * Compositing
      */
-    void postComposition(scheduler::FrameTargeter&, nsecs_t presentStartTime)
-            REQUIRES(kMainThreadContext);
+    void postComposition(nsecs_t callTime) REQUIRES(kMainThreadContext);
 
     /*
      * Display management
@@ -994,6 +993,20 @@
      */
     nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
 
+    using FenceTimePtr = std::shared_ptr<FenceTime>;
+
+    bool wouldPresentEarly(TimePoint frameTime, Period) const REQUIRES(kMainThreadContext);
+
+    const FenceTimePtr& getPreviousPresentFence(TimePoint frameTime, Period) const
+            REQUIRES(kMainThreadContext);
+
+    // Blocks the thread waiting for up to graceTimeMs in case the fence is about to signal.
+    static bool isFencePending(const FenceTimePtr&, int graceTimeMs);
+
+    // Calculates the expected present time for this frame. For negative offsets, performs a
+    // correction using the predicted vsync for the next frame instead.
+    TimePoint calculateExpectedPresentTime(TimePoint frameTime) const;
+
     /*
      * Display identification
      */
@@ -1242,6 +1255,9 @@
 
     // If blurs should be enabled on this device.
     bool mSupportsBlur = false;
+    std::atomic<uint32_t> mFrameMissedCount = 0;
+    std::atomic<uint32_t> mHwcFrameMissedCount = 0;
+    std::atomic<uint32_t> mGpuFrameMissedCount = 0;
 
     TransactionCallbackInvoker mTransactionCallbackInvoker;
 
@@ -1309,6 +1325,15 @@
     std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
     scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext);
 
+    struct FenceWithFenceTime {
+        sp<Fence> fence = Fence::NO_FENCE;
+        FenceTimePtr fenceTime = FenceTime::NO_FENCE;
+    };
+    std::array<FenceWithFenceTime, 2> mPreviousPresentFences;
+
+    TimePoint mScheduledPresentTime GUARDED_BY(kMainThreadContext);
+    TimePoint mExpectedPresentTime GUARDED_BY(kMainThreadContext);
+
     // below flags are set by main thread only
     bool mSetActiveModePending = false;
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 42b3879..534a8f3 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -286,8 +286,8 @@
 private:
     // ICompositor overrides:
     void configure() override {}
-    bool commit(const scheduler::FrameTarget&) override { return false; }
-    CompositeResult composite(scheduler::FrameTargeter&) override { return {}; }
+    bool commit(TimePoint, VsyncId, TimePoint) override { return false; }
+    void composite(TimePoint, VsyncId) override {}
     void sample() override {}
 
     // MessageQueue overrides:
@@ -604,9 +604,7 @@
 
             mFlinger->commitTransactions();
             mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp));
-
-            scheduler::FrameTargeter frameTargeter(mFdp.ConsumeBool());
-            mFlinger->postComposition(frameTargeter, mFdp.ConsumeIntegral<nsecs_t>());
+            mFlinger->postComposition(systemTime());
         }
 
         mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
@@ -624,6 +622,8 @@
 
         mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mFdp.ConsumeIntegral<uid_t>());
 
+        mFlinger->calculateExpectedPresentTime({});
+
         mFlinger->enableHalVirtualDisplays(mFdp.ConsumeBool());
 
         fuzzDumpsysAndDebug(&mFdp);
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index b1fd06f..f17d2e1 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -19,7 +19,6 @@
 #include <fuzzer/FuzzedDataProvider.h>
 #include <processgroup/sched_policy.h>
 
-#include <scheduler/IVsyncSource.h>
 #include <scheduler/PresentLatencyTracker.h>
 
 #include "Scheduler/OneShotTimer.h"
@@ -43,7 +42,6 @@
                                      (120_Hz).getPeriodNsecs()};
 
 constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateSelector::LayerVoteType>();
-constexpr auto kCompositionCoverage = ftl::enum_range<CompositionCoverage>();
 
 constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF,
                                      PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND};
@@ -58,10 +56,6 @@
     component->dump(res);
 }
 
-inline sp<Fence> makeFakeFence() {
-    return sp<Fence>::make(memfd_create("fd", MFD_ALLOW_SEALING));
-}
-
 class SchedulerFuzzer {
 public:
     SchedulerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
@@ -71,7 +65,6 @@
     void fuzzRefreshRateSelection();
     void fuzzRefreshRateSelector();
     void fuzzPresentLatencyTracker();
-    void fuzzFrameTargeter();
     void fuzzVSyncModulator();
     void fuzzVSyncPredictor();
     void fuzzVSyncReactor();
@@ -263,13 +256,13 @@
     reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed);
     reactor.addHwVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>() /*newPeriod*/, std::nullopt,
                                 &periodFlushed);
-
-    const auto fence = std::make_shared<FenceTime>(makeFakeFence());
+    sp<Fence> fence = sp<Fence>::make(memfd_create("fd", MFD_ALLOW_SEALING));
+    std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
     vSyncTracker->addVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>());
     FenceTime::Snapshot snap(mFdp.ConsumeIntegral<nsecs_t>());
-    fence->applyTrustedSnapshot(snap);
+    ft->applyTrustedSnapshot(snap);
     reactor.setIgnorePresentFences(mFdp.ConsumeBool());
-    reactor.addPresentFence(fence);
+    reactor.addPresentFence(ft);
     dump<scheduler::VSyncReactor>(&reactor, &mFdp);
 }
 
@@ -399,45 +392,14 @@
 
 void SchedulerFuzzer::fuzzPresentLatencyTracker() {
     scheduler::PresentLatencyTracker tracker;
-
-    int i = 5;
-    while (i-- > 0) {
-        tracker.trackPendingFrame(getFuzzedTimePoint(mFdp),
-                                  std::make_shared<FenceTime>(makeFakeFence()));
-    }
-}
-
-void SchedulerFuzzer::fuzzFrameTargeter() {
-    scheduler::FrameTargeter frameTargeter(mFdp.ConsumeBool());
-
-    const struct VsyncSource final : scheduler::IVsyncSource {
-        explicit VsyncSource(FuzzedDataProvider& fuzzer) : fuzzer(fuzzer) {}
-        FuzzedDataProvider& fuzzer;
-
-        Period period() const { return getFuzzedDuration(fuzzer); }
-        TimePoint vsyncDeadlineAfter(TimePoint) const { return getFuzzedTimePoint(fuzzer); }
-    } vsyncSource{mFdp};
-
-    int i = 10;
-    while (i-- > 0) {
-        frameTargeter.beginFrame({.frameBeginTime = getFuzzedTimePoint(mFdp),
-                                  .vsyncId = getFuzzedVsyncId(mFdp),
-                                  .expectedVsyncTime = getFuzzedTimePoint(mFdp),
-                                  .sfWorkDuration = getFuzzedDuration(mFdp)},
-                                 vsyncSource);
-
-        frameTargeter.setPresentFence(makeFakeFence());
-
-        frameTargeter.endFrame(
-                {.compositionCoverage = mFdp.PickValueInArray(kCompositionCoverage.values)});
-    }
+    tracker.trackPendingFrame(TimePoint::fromNs(mFdp.ConsumeIntegral<nsecs_t>()),
+                              FenceTime::NO_FENCE);
 }
 
 void SchedulerFuzzer::process() {
     fuzzRefreshRateSelection();
     fuzzRefreshRateSelector();
     fuzzPresentLatencyTracker();
-    fuzzFrameTargeter();
     fuzzVSyncModulator();
     fuzzVSyncPredictor();
     fuzzVSyncReactor();
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 359e2ab..91875cc 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -20,10 +20,9 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include <scheduler/interface/ICompositor.h>
-
 #include "FrameTimeline.h"
 #include "Scheduler/MessageQueue.h"
+#include "SurfaceFlinger.h"
 #include "mock/MockVSyncDispatch.h"
 
 namespace android {
@@ -35,8 +34,8 @@
 
 struct NoOpCompositor final : ICompositor {
     void configure() override {}
-    bool commit(const scheduler::FrameTarget&) override { return false; }
-    CompositeResult composite(scheduler::FrameTargeter&) override { return {}; }
+    bool commit(TimePoint, VsyncId, TimePoint) override { return false; }
+    void composite(TimePoint, VsyncId) override {}
     void sample() override {}
 } gNoOpCompositor;
 
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index aac11c0..a30f7e0 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -173,8 +173,8 @@
 private:
     // ICompositor overrides:
     void configure() override {}
-    bool commit(const scheduler::FrameTarget&) override { return false; }
-    CompositeResult composite(scheduler::FrameTargeter&) override { return {}; }
+    bool commit(TimePoint, VsyncId, TimePoint) override { return false; }
+    void composite(TimePoint, VsyncId) override {}
     void sample() override {}
 };
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 833984f..945e488 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -20,11 +20,6 @@
 #include <chrono>
 #include <variant>
 
-#include <ftl/fake_guard.h>
-#include <ftl/match.h>
-#include <gui/ScreenCaptureResults.h>
-#include <ui/DynamicDisplayInfo.h>
-
 #include <compositionengine/Display.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
@@ -32,7 +27,11 @@
 #include <compositionengine/impl/Display.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/mock/DisplaySurface.h>
+#include <ftl/fake_guard.h>
+#include <ftl/match.h>
+#include <gui/ScreenCaptureResults.h>
 
+#include <ui/DynamicDisplayInfo.h>
 #include "DisplayDevice.h"
 #include "FakeVsyncConfiguration.h"
 #include "FrameTracer/FrameTracer.h"
@@ -45,6 +44,7 @@
 #include "Scheduler/RefreshRateSelector.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlinger.h"
+#include "SurfaceFlingerDefaultFactory.h"
 #include "TestableScheduler.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockDisplayMode.h"
@@ -360,42 +360,25 @@
         commitTransactionsLocked(eDisplayTransactionNeeded);
     }
 
-    void commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime,
-                bool composite = false) {
-        constexpr bool kBackpressureGpuComposition = true;
-        scheduler::FrameTargeter frameTargeter(kBackpressureGpuComposition);
-
-        frameTargeter.beginFrame({.frameBeginTime = frameTime,
-                                  .vsyncId = vsyncId,
-                                  .expectedVsyncTime = expectedVsyncTime,
-                                  .sfWorkDuration = 10ms},
-                                 *mScheduler->getVsyncSchedule());
-
-        mFlinger->commit(frameTargeter.target());
-
-        if (composite) {
-            mFlinger->composite(frameTargeter);
-        }
+    TimePoint commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime) {
+        mFlinger->commit(frameTime, vsyncId, expectedVsyncTime);
+        return frameTime;
     }
 
-    void commit(TimePoint frameTime, VsyncId vsyncId, bool composite = false) {
-        return commit(frameTime, vsyncId, frameTime + Period(10ms), composite);
+    TimePoint commit(TimePoint frameTime, VsyncId vsyncId) {
+        return commit(frameTime, vsyncId, frameTime + Period(10ms));
     }
 
-    void commit(bool composite = false) {
+    TimePoint commit() {
         const TimePoint frameTime = scheduler::SchedulerClock::now();
-        commit(frameTime, kVsyncId, composite);
+        return commit(frameTime, kVsyncId);
     }
 
     void commitAndComposite(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime) {
-        constexpr bool kComposite = true;
-        commit(frameTime, vsyncId, expectedVsyncTime, kComposite);
+        mFlinger->composite(commit(frameTime, vsyncId, expectedVsyncTime), vsyncId);
     }
 
-    void commitAndComposite() {
-        constexpr bool kComposite = true;
-        commit(kComposite);
-    }
+    void commitAndComposite() { mFlinger->composite(commit(), kVsyncId); }
 
     auto createDisplay(const String8& displayName, bool secure, float requestedRefreshRate = 0.0f) {
         return mFlinger->createDisplay(displayName, secure, requestedRefreshRate);