Add FenceTime::wasPendingAt()

FenceTime::wasPendingAt() takes a time as an argument, and returns a
bool indicating if the fence would have been pending at that time.

Time specified can only be in the past. If the fence is currently
pending, it is assumed that fence is pending at any given time.

Flag: EXEMPT PURE_REFACTOR
Bug: 409682021
Test: New FenceTime_test
Change-Id: I206a5979f0b26a2c11f633b5d1894a0ee2db448e
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
index 81afe9e..5a3e49e 100644
--- a/libs/ui/FenceTime.cpp
+++ b/libs/ui/FenceTime.cpp
@@ -220,6 +220,16 @@
     mSignalTime.store(signalTime, std::memory_order_relaxed);
 }
 
+bool FenceTime::wasPendingAt(nsecs_t time) {
+    const nsecs_t signalTime = getSignalTime();
+    // If the fence is currently pending, assume that fence is pending at `time`.
+    if (signalTime == Fence::SIGNAL_TIME_PENDING) {
+        return true;
+    }
+    // A fence fired after `time` should be considered pending at `time`.
+    return Fence::isValidTimestamp(signalTime) && signalTime >= time;
+}
+
 // ============================================================================
 // FenceTime::Snapshot
 // ============================================================================
diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h
index 3560d57..5692448 100644
--- a/libs/ui/include/ui/FenceTime.h
+++ b/libs/ui/include/ui/FenceTime.h
@@ -34,7 +34,7 @@
 class FenceTime;
 using FenceTimePtr = std::shared_ptr<FenceTime>;
 
-// A wrapper around fence that only implements isValid and getSignalTime.
+// A wrapper around fence that only implements isValid, getSignalTime, and wasPendingAt.
 // It automatically closes the fence in a thread-safe manner once the signal
 // time is known.
 class FenceTime {
@@ -126,6 +126,10 @@
     // indefinitely for the fence to signal.
     status_t wait(int timeout);
 
+    // Returns true if the fence would have been pending at `time`. `time` cannot be in the future.
+    // Assumes a fence is pending at any `time` if the fence is currently pending.
+    bool wasPendingAt(nsecs_t time);
+
     void signalForTest(nsecs_t signalTime);
 
 private:
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index ae2366a..490ccac 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -45,6 +45,17 @@
 }
 
 cc_test {
+    name: "FenceTime_test",
+    shared_libs: ["libui"],
+    static_libs: ["libgmock"],
+    srcs: ["FenceTime_test.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
+
+cc_test {
     name: "FlattenableHelpers_test",
     shared_libs: ["libui"],
     srcs: ["FlattenableHelpers_test.cpp"],
diff --git a/libs/ui/tests/FenceTime_test.cpp b/libs/ui/tests/FenceTime_test.cpp
new file mode 100644
index 0000000..a6ec003
--- /dev/null
+++ b/libs/ui/tests/FenceTime_test.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2025 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 <gtest/gtest.h>
+#include <ui/Fence.h>
+#include <ui/FenceTime.h>
+#include <ui/MockFence.h>
+
+namespace android::ui {
+namespace {
+
+using ::testing::Return;
+
+TEST(FenceTimeTest, WasPendingAtViaTestHelperEquals) {
+    FenceTime fenceTime(sp<mock::MockFence>::make());
+    fenceTime.signalForTest(123);
+    EXPECT_TRUE(fenceTime.wasPendingAt(123));
+}
+
+TEST(FenceTimeTest, WasPendingAtViaTestHelperBefore) {
+    FenceTime fenceTime(sp<mock::MockFence>::make());
+    fenceTime.signalForTest(123);
+    EXPECT_TRUE(fenceTime.wasPendingAt(100));
+}
+
+TEST(FenceTimeTest, WasPendingAtViaTestHelperAfter) {
+    FenceTime fenceTime(sp<mock::MockFence>::make());
+    fenceTime.signalForTest(123);
+    EXPECT_FALSE(fenceTime.wasPendingAt(200));
+}
+
+TEST(FenceTimeTest, WasPendingAtViaTestHelperPending) {
+    FenceTime fenceTime(sp<mock::MockFence>::make());
+    fenceTime.signalForTest(Fence::SIGNAL_TIME_PENDING);
+    EXPECT_TRUE(fenceTime.wasPendingAt(123));
+}
+} // namespace
+} // namespace android::ui
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index c488598..15e7f8c 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -21,21 +21,8 @@
 #include <utils/Log.h>
 
 namespace android::scheduler {
-namespace {
 using namespace std::chrono_literals;
 
-// TODO: b/416629136 - Move this into android::Fence.
-// Returns true if the `fence` hasn't fired before `time`.
-bool isFencePendingAt(const FenceTimePtr& fenceTime, TimePoint time) {
-    const nsecs_t signalTime = fenceTime->getSignalTime();
-    if (signalTime == Fence::SIGNAL_TIME_PENDING) {
-        return true;
-    }
-    // A fence fired after `time` should be considered pending at `time`.
-    return Fence::isValidTimestamp(signalTime) && signalTime >= time.ns();
-}
-} // namespace
-
 FrameTarget::FrameTarget(const std::string& displayLabel)
       : mFramePending("PrevFramePending " + displayLabel, false),
         mFrameMissed("PrevFrameMissed " + displayLabel, false),
@@ -98,7 +85,7 @@
 size_t FrameTargeter::countPresentFencesPendingAt(TimePoint time) const {
     size_t pendingFenceCount = 0;
     for (ssize_t i = static_cast<ssize_t>(mPresentFences.size() - 1); i >= 0; --i) {
-        if (isFencePendingAt(mPresentFences[static_cast<size_t>(i)].fenceTime, time)) {
+        if (mPresentFences[static_cast<size_t>(i)].fenceTime->wasPendingAt(time.ns())) {
             pendingFenceCount++;
         }
     }