Merge "Refactor SkiaVkRenderEngine's VulkanInterface and DestroySemaphoreInfo" into main
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 8d37aac..826a8db 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -3280,6 +3280,12 @@
// duration is logged into MYLOG instead.
PrintHeader();
+ bool system_trace_exists = access(SYSTEM_TRACE_SNAPSHOT, F_OK) == 0;
+ if (options_->use_predumped_ui_data && !system_trace_exists) {
+ MYLOGW("Ignoring 'use predumped data' flag because no predumped data is available");
+ options_->use_predumped_ui_data = false;
+ }
+
std::future<std::string> snapshot_system_trace;
bool is_dumpstate_restricted =
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index 28bd793..ae7d8e0 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -50,6 +50,12 @@
exit 1
fi
+if pm art on-ota-staged --slot "$TARGET_SLOT_SUFFIX"; then
+ # Handled by Pre-reboot Dexopt.
+ exit 0
+fi
+echo "Pre-reboot Dexopt not enabled. Fall back to otapreopt."
+
if [ "$(/system/bin/otapreopt_chroot --version)" != 2 ]; then
# We require an updated chroot wrapper that reads dexopt commands from stdin.
# Even if we kept compat with the old binary, the OTA preopt wouldn't work due
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 8b2842a..da232a5 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -287,6 +287,12 @@
}
prebuilt_etc {
+ name: "android.hardware.telephony.data.prebuilt.xml",
+ src: "android.hardware.telephony.data.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.telephony.gsm.prebuilt.xml",
src: "android.hardware.telephony.gsm.xml",
defaults: ["frameworks_native_data_etc_defaults"],
@@ -329,12 +335,24 @@
}
prebuilt_etc {
+ name: "android.hardware.vulkan.level-1.prebuilt.xml",
+ src: "android.hardware.vulkan.level-1.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.vulkan.version-1_0_3.prebuilt.xml",
src: "android.hardware.vulkan.version-1_0_3.xml",
defaults: ["frameworks_native_data_etc_defaults"],
}
prebuilt_etc {
+ name: "android.hardware.vulkan.version-1_3.prebuilt.xml",
+ src: "android.hardware.vulkan.version-1_3.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.wifi.prebuilt.xml",
src: "android.hardware.wifi.xml",
defaults: ["frameworks_native_data_etc_defaults"],
@@ -353,6 +371,12 @@
}
prebuilt_etc {
+ name: "android.software.contextualsearch.prebuilt.xml",
+ src: "android.software.contextualsearch.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.software.device_id_attestation.prebuilt.xml",
src: "android.software.device_id_attestation.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 321737e..bf1f2e9 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -532,7 +532,7 @@
* using this API for formats that encode an HDR/SDR ratio as part of generating the buffer.
*
* @param surface_control The layer whose extended range brightness is being specified
- * @param currentBufferRatio The current hdr/sdr ratio of the current buffer as represented as
+ * @param currentBufferRatio The current HDR/SDR ratio of the current buffer as represented as
* peakHdrBrightnessInNits / targetSdrWhitePointInNits. For example if the
* buffer was rendered with a target SDR whitepoint of 100nits and a max
* display brightness of 200nits, this should be set to 2.0f.
@@ -546,7 +546,7 @@
*
* Must be finite && >= 1.0f
*
- * @param desiredRatio The desired hdr/sdr ratio as represented as peakHdrBrightnessInNits /
+ * @param desiredRatio The desired HDR/SDR ratio as represented as peakHdrBrightnessInNits /
* targetSdrWhitePointInNits. This can be used to communicate the max desired
* brightness range. This is similar to the "max luminance" value in other
* HDR metadata formats, but represented as a ratio of the target SDR whitepoint
@@ -579,13 +579,13 @@
float desiredRatio) __INTRODUCED_IN(__ANDROID_API_U__);
/**
- * Sets the desired hdr headroom for the layer. See: ASurfaceTransaction_setExtendedRangeBrightness,
+ * Sets the desired HDR headroom for the layer. See: ASurfaceTransaction_setExtendedRangeBrightness,
* prefer using this API for formats that conform to HDR standards like HLG or HDR10, that do not
* communicate a HDR/SDR ratio as part of generating the buffer.
*
- * @param surface_control The layer whose desired hdr headroom is being specified
+ * @param surface_control The layer whose desired HDR headroom is being specified
*
- * @param desiredHeadroom The desired hdr/sdr ratio as represented as peakHdrBrightnessInNits /
+ * @param desiredHeadroom The desired HDR/SDR ratio as represented as peakHdrBrightnessInNits /
* targetSdrWhitePointInNits. This can be used to communicate the max
* desired brightness range of the panel. The system may not be able to, or
* may choose not to, deliver the requested range.
diff --git a/include/input/Input.h b/include/input/Input.h
index 374254f..ddc3768 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -662,10 +662,6 @@
inline void setActionButton(int32_t button) { mActionButton = button; }
- inline float getXOffset() const { return mTransform.tx(); }
-
- inline float getYOffset() const { return mTransform.ty(); }
-
inline const ui::Transform& getTransform() const { return mTransform; }
std::optional<ui::Rotation> getSurfaceRotation() const;
@@ -880,6 +876,22 @@
void offsetLocation(float xOffset, float yOffset);
+ /**
+ * Get the X offset of this motion event relative to the origin of the raw coordinate space.
+ *
+ * In practice, this is the delta that was added to the raw screen coordinates (i.e. in logical
+ * display space) to adjust for the absolute position of the containing windows and views.
+ */
+ float getRawXOffset() const;
+
+ /**
+ * Get the Y offset of this motion event relative to the origin of the raw coordinate space.
+ *
+ * In practice, this is the delta that was added to the raw screen coordinates (i.e. in logical
+ * display space) to adjust for the absolute position of the containing windows and views.
+ */
+ float getRawYOffset() const;
+
void scale(float globalScaleFactor);
// Set 3x3 perspective matrix transformation.
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 42dcd3c..aca4b62 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -670,13 +670,6 @@
status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled);
static void rewriteMessage(TouchState& state, InputMessage& msg);
- static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg);
- static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
- static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg);
- static void initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg);
- static void initializeDragEvent(DragEvent* event, const InputMessage* msg);
- static void initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg);
- static void addSample(MotionEvent* event, const InputMessage* msg);
static bool canAddSample(const Batch& batch, const InputMessage* msg);
static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index c1770b3..35cea81 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -256,7 +256,7 @@
if (const auto* rpcFields = maybeRpcFields()) {
if (binder) {
- status_t status = writeInt32(1); // non-null
+ status_t status = writeInt32(RpcFields::TYPE_BINDER); // non-null
if (status != OK) return status;
uint64_t address;
// TODO(b/167966510): need to undo this if the Parcel is not sent
@@ -266,7 +266,7 @@
status = writeUint64(address);
if (status != OK) return status;
} else {
- status_t status = writeInt32(0); // null
+ status_t status = writeInt32(RpcFields::TYPE_BINDER_NULL); // null
if (status != OK) return status;
}
return finishFlattenBinder(binder);
@@ -740,6 +740,12 @@
return kernelFields->mHasFds;
}
+status_t Parcel::hasBinders(bool* result) const {
+ status_t status = hasBindersInRange(0, dataSize(), result);
+ ALOGE_IF(status != NO_ERROR, "Error %d calling hasBindersInRange()", status);
+ return status;
+}
+
std::vector<sp<IBinder>> Parcel::debugReadAllStrongBinders() const {
std::vector<sp<IBinder>> ret;
@@ -799,6 +805,46 @@
return ret;
}
+status_t Parcel::hasBindersInRange(size_t offset, size_t len, bool* result) const {
+ if (len > INT32_MAX || offset > INT32_MAX) {
+ // Don't accept size_t values which may have come from an inadvertent conversion from a
+ // negative int.
+ return BAD_VALUE;
+ }
+ size_t limit;
+ if (__builtin_add_overflow(offset, len, &limit) || limit > mDataSize) {
+ return BAD_VALUE;
+ }
+ *result = false;
+ if (const auto* kernelFields = maybeKernelFields()) {
+#ifdef BINDER_WITH_KERNEL_IPC
+ for (size_t i = 0; i < kernelFields->mObjectsSize; i++) {
+ size_t pos = kernelFields->mObjects[i];
+ if (pos < offset) continue;
+ if (pos + sizeof(flat_binder_object) > offset + len) {
+ if (kernelFields->mObjectsSorted) {
+ break;
+ } else {
+ continue;
+ }
+ }
+ const flat_binder_object* flat =
+ reinterpret_cast<const flat_binder_object*>(mData + pos);
+ if (flat->hdr.type == BINDER_TYPE_BINDER || flat->hdr.type == BINDER_TYPE_HANDLE) {
+ *result = true;
+ break;
+ }
+ }
+#else
+ LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
+ return INVALID_OPERATION;
+#endif // BINDER_WITH_KERNEL_IPC
+ } else if (const auto* rpcFields = maybeRpcFields()) {
+ return INVALID_OPERATION;
+ }
+ return NO_ERROR;
+}
+
status_t Parcel::hasFileDescriptorsInRange(size_t offset, size_t len, bool* result) const {
if (len > INT32_MAX || offset > INT32_MAX) {
// Don't accept size_t values which may have come from an inadvertent conversion from a
@@ -2930,14 +2976,14 @@
return continueWrite(desired);
}
+ releaseObjects();
+
uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
if (!data && desired > mDataCapacity) {
mError = NO_MEMORY;
return NO_MEMORY;
}
- releaseObjects();
-
if (data || desired == 0) {
LOG_ALLOC("Parcel %p: restart from %zu to %zu capacity", this, mDataCapacity, desired);
if (mDataCapacity > desired) {
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index d7096d8..5e18b91 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -101,7 +101,9 @@
void restoreAllowFds(bool lastValue);
bool hasFileDescriptors() const;
+ status_t hasBinders(bool* result) const;
status_t hasFileDescriptorsInRange(size_t offset, size_t length, bool* result) const;
+ status_t hasBindersInRange(size_t offset, size_t length, bool* result) const;
// returns all binder objects in the Parcel
std::vector<sp<IBinder>> debugReadAllStrongBinders() const;
@@ -647,6 +649,8 @@
void freeDataNoInit();
void initState();
void scanForFds() const;
+ status_t scanForBinders(bool* result) const;
+
status_t validateReadData(size_t len) const;
void updateWorkSourceRequestHeaderPosition() const;
diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
index 34fc43f..32a70e5 100644
--- a/libs/binder/tests/binderParcelUnitTest.cpp
+++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -23,6 +23,7 @@
using android::BBinder;
using android::IBinder;
using android::IPCThreadState;
+using android::NO_ERROR;
using android::OK;
using android::Parcel;
using android::sp;
@@ -164,6 +165,45 @@
ASSERT_EQ(2, p2.readInt32());
}
+TEST(Parcel, HasBinders) {
+ sp<IBinder> b1 = sp<BBinder>::make();
+
+ Parcel p1;
+ p1.writeInt32(1);
+ p1.writeStrongBinder(b1);
+
+ bool result = false;
+ ASSERT_EQ(NO_ERROR, p1.hasBinders(&result));
+ ASSERT_EQ(true, result);
+
+ p1.setDataSize(0); // clear data
+ result = false;
+ ASSERT_EQ(NO_ERROR, p1.hasBinders(&result));
+ ASSERT_EQ(false, result);
+ p1.writeStrongBinder(b1); // reset with binder data
+ result = false;
+ ASSERT_EQ(NO_ERROR, p1.hasBinders(&result));
+ ASSERT_EQ(true, result);
+
+ Parcel p3;
+ p3.appendFrom(&p1, 0, p1.dataSize());
+ result = false;
+ ASSERT_EQ(NO_ERROR, p1.hasBinders(&result));
+ ASSERT_EQ(true, result);
+}
+
+TEST(Parcel, HasBindersInRange) {
+ sp<IBinder> b1 = sp<BBinder>::make();
+ Parcel p1;
+ p1.writeStrongBinder(b1);
+ bool result = false;
+ ASSERT_EQ(NO_ERROR, p1.hasBindersInRange(0, p1.dataSize(), &result));
+ ASSERT_EQ(true, result);
+ result = false;
+ ASSERT_EQ(NO_ERROR, p1.hasBinders(&result));
+ ASSERT_EQ(true, result);
+}
+
TEST(Parcel, AppendWithBinder) {
sp<IBinder> b1 = sp<BBinder>::make();
sp<IBinder> b2 = sp<BBinder>::make();
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 08fe071..e378b86 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -115,6 +115,14 @@
p.setDataPosition(pos);
FUZZ_LOG() << "setDataPosition done";
},
+ [] (const ::android::Parcel& p, FuzzedDataProvider& provider) {
+ size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
+ std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len);
+ FUZZ_LOG() << "about to setData: " <<(bytes.data() ? HexString(bytes.data(), bytes.size()) : "null");
+ // TODO: allow all read and write operations
+ (*const_cast<::android::Parcel*>(&p)).setData(bytes.data(), bytes.size());
+ FUZZ_LOG() << "setData done";
+ },
PARCEL_READ_NO_STATUS(size_t, allowFds),
PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors),
PARCEL_READ_NO_STATUS(std::vector<android::sp<android::IBinder>>, debugReadAllStrongBinders),
@@ -353,6 +361,20 @@
FUZZ_LOG() << " status: " << status << " result: " << result;
},
[] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to call hasBinders() with status";
+ bool result;
+ status_t status = p.hasBinders(&result);
+ FUZZ_LOG() << " status: " << status << " result: " << result;
+ },
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
+ FUZZ_LOG() << "about to call hasBindersInRange() with status";
+ size_t offset = p.readUint32();
+ size_t length = p.readUint32();
+ bool result;
+ status_t status = p.hasBindersInRange(offset, length, &result);
+ FUZZ_LOG() << " status: " << status << " result: " << result;
+ },
+ [] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call compareDataInRange() with status";
size_t thisOffset = p.readUint32();
size_t otherOffset = p.readUint32();
diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp
index 4518b67..54290cd 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -143,9 +143,9 @@
void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
AChoreographer_frameCallback64 cb64,
AChoreographer_vsyncCallback vsyncCallback, void* data,
- nsecs_t delay) {
+ nsecs_t delay, CallbackType callbackType) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay};
+ FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay, callbackType};
{
std::lock_guard<std::mutex> _l{mLock};
mFrameCallbacks.push(callback);
@@ -285,18 +285,8 @@
}
}
-void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
- VsyncEventData vsyncEventData) {
- std::vector<FrameCallback> callbacks{};
- {
- std::lock_guard<std::mutex> _l{mLock};
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
- callbacks.push_back(mFrameCallbacks.top());
- mFrameCallbacks.pop();
- }
- }
- mLastVsyncEventData = vsyncEventData;
+void Choreographer::dispatchCallbacks(const std::vector<FrameCallback>& callbacks,
+ VsyncEventData vsyncEventData, nsecs_t timestamp) {
for (const auto& cb : callbacks) {
if (cb.vsyncCallback != nullptr) {
ATRACE_FORMAT("AChoreographer_vsyncCallback %" PRId64,
@@ -319,6 +309,34 @@
}
}
+void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
+ VsyncEventData vsyncEventData) {
+ std::vector<FrameCallback> animationCallbacks{};
+ std::vector<FrameCallback> inputCallbacks{};
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
+ if (mFrameCallbacks.top().callbackType == CALLBACK_INPUT) {
+ inputCallbacks.push_back(mFrameCallbacks.top());
+ } else {
+ animationCallbacks.push_back(mFrameCallbacks.top());
+ }
+ mFrameCallbacks.pop();
+ }
+ }
+ mLastVsyncEventData = vsyncEventData;
+ // Callbacks with type CALLBACK_INPUT should always run first
+ {
+ ATRACE_FORMAT("CALLBACK_INPUT");
+ dispatchCallbacks(inputCallbacks, vsyncEventData, timestamp);
+ }
+ {
+ ATRACE_FORMAT("CALLBACK_ANIMATION");
+ dispatchCallbacks(animationCallbacks, vsyncEventData, timestamp);
+ }
+}
+
void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this,
to_string(displayId).c_str(), toString(connected));
diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h
index 55a7aa7..fc79b03 100644
--- a/libs/gui/include/gui/Choreographer.h
+++ b/libs/gui/include/gui/Choreographer.h
@@ -28,12 +28,18 @@
namespace android {
using gui::VsyncEventData;
+enum CallbackType : int8_t {
+ CALLBACK_INPUT,
+ CALLBACK_ANIMATION,
+};
+
struct FrameCallback {
AChoreographer_frameCallback callback;
AChoreographer_frameCallback64 callback64;
AChoreographer_vsyncCallback vsyncCallback;
void* data;
nsecs_t dueTime;
+ CallbackType callbackType;
inline bool operator<(const FrameCallback& rhs) const {
// Note that this is intentionally flipped because we want callbacks due sooner to be at
@@ -78,7 +84,7 @@
void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
AChoreographer_frameCallback64 cb64,
AChoreographer_vsyncCallback vsyncCallback, void* data,
- nsecs_t delay);
+ nsecs_t delay, CallbackType callbackType);
void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
EXCLUDES(gChoreographers.lock);
void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
@@ -109,6 +115,8 @@
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
VsyncEventData vsyncEventData) override;
+ void dispatchCallbacks(const std::vector<FrameCallback>&, VsyncEventData vsyncEventData,
+ nsecs_t timestamp);
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
void dispatchHotplugConnectionError(nsecs_t timestamp, int32_t connectionError) override;
void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index e606b99..0f16f71 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -30,6 +30,7 @@
"BLASTBufferQueue_test.cpp",
"BufferItemConsumer_test.cpp",
"BufferQueue_test.cpp",
+ "Choreographer_test.cpp",
"CompositorTiming_test.cpp",
"CpuConsumer_test.cpp",
"EndToEndNativeInputTest.cpp",
@@ -61,6 +62,7 @@
"libSurfaceFlingerProp",
"libGLESv1_CM",
"libinput",
+ "libnativedisplay",
],
static_libs: [
diff --git a/libs/gui/tests/Choreographer_test.cpp b/libs/gui/tests/Choreographer_test.cpp
new file mode 100644
index 0000000..2ac2550
--- /dev/null
+++ b/libs/gui/tests/Choreographer_test.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#define LOG_TAG "Choreographer_test"
+
+#include <android-base/stringprintf.h>
+#include <android/choreographer.h>
+#include <gtest/gtest.h>
+#include <gui/Choreographer.h>
+#include <utils/Looper.h>
+#include <chrono>
+#include <future>
+#include <string>
+
+namespace android {
+class ChoreographerTest : public ::testing::Test {};
+
+struct VsyncCallback {
+ std::atomic<bool> completePromise{false};
+ std::chrono::nanoseconds frameTime{0LL};
+ std::chrono::nanoseconds receivedCallbackTime{0LL};
+
+ void onVsyncCallback(const AChoreographerFrameCallbackData* callbackData) {
+ frameTime = std::chrono::nanoseconds{
+ AChoreographerFrameCallbackData_getFrameTimeNanos(callbackData)};
+ receivedCallbackTime = std::chrono::nanoseconds{systemTime(SYSTEM_TIME_MONOTONIC)};
+ completePromise.store(true);
+ }
+
+ bool callbackReceived() { return completePromise.load(); }
+};
+
+static void vsyncCallback(const AChoreographerFrameCallbackData* callbackData, void* data) {
+ VsyncCallback* cb = static_cast<VsyncCallback*>(data);
+ cb->onVsyncCallback(callbackData);
+}
+
+TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) {
+ sp<Looper> looper = Looper::prepare(0);
+ Choreographer* choreographer = Choreographer::getForThread();
+ VsyncCallback animationCb;
+ VsyncCallback inputCb;
+
+ choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0,
+ CALLBACK_ANIMATION);
+ choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &inputCb, 0,
+ CALLBACK_INPUT);
+
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ nsecs_t currTime;
+ int pollResult;
+ do {
+ pollResult = looper->pollOnce(16);
+ currTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ } while (!(inputCb.callbackReceived() && animationCb.callbackReceived()) &&
+ (pollResult != Looper::POLL_TIMEOUT && pollResult != Looper::POLL_ERROR) &&
+ (currTime - startTime < 3000));
+
+ ASSERT_TRUE(inputCb.callbackReceived()) << "did not receive input callback";
+ ASSERT_TRUE(animationCb.callbackReceived()) << "did not receive animation callback";
+
+ ASSERT_EQ(inputCb.frameTime, animationCb.frameTime)
+ << android::base::StringPrintf("input and animation callback frame times don't match. "
+ "inputFrameTime=%lld animationFrameTime=%lld",
+ inputCb.frameTime.count(),
+ animationCb.frameTime.count());
+
+ ASSERT_LT(inputCb.receivedCallbackTime, animationCb.receivedCallbackTime)
+ << android::base::StringPrintf("input callback was not called first. "
+ "inputCallbackTime=%lld animationCallbackTime=%lld",
+ inputCb.frameTime.count(),
+ animationCb.frameTime.count());
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index d58fb42..ff9d9a9 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -751,6 +751,18 @@
mTransform.set(currXOffset + xOffset, currYOffset + yOffset);
}
+float MotionEvent::getRawXOffset() const {
+ // This is equivalent to the x-coordinate of the point that the origin of the raw coordinate
+ // space maps to.
+ return (mTransform * mRawTransform.inverse()).tx();
+}
+
+float MotionEvent::getRawYOffset() const {
+ // This is equivalent to the y-coordinate of the point that the origin of the raw coordinate
+ // space maps to.
+ return (mTransform * mRawTransform.inverse()).ty();
+}
+
void MotionEvent::scale(float globalScaleFactor) {
mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor);
mRawTransform.set(mRawTransform.tx() * globalScaleFactor,
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index e49f4eb..b3a36eb 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -26,10 +26,13 @@
#include <com_android_input_flags.h>
#include <input/InputTransport.h>
+#include <input/PrintTools.h>
#include <input/TraceTools.h>
namespace input_flags = com::android::input::flags;
+namespace android {
+
namespace {
/**
@@ -110,21 +113,76 @@
return newFd;
}
-} // namespace
+void initializeKeyEvent(KeyEvent& event, const InputMessage& msg) {
+ event.initialize(msg.body.key.eventId, msg.body.key.deviceId, msg.body.key.source,
+ msg.body.key.displayId, msg.body.key.hmac, msg.body.key.action,
+ msg.body.key.flags, msg.body.key.keyCode, msg.body.key.scanCode,
+ msg.body.key.metaState, msg.body.key.repeatCount, msg.body.key.downTime,
+ msg.body.key.eventTime);
+}
-using android::base::Result;
-using android::base::StringPrintf;
+void initializeFocusEvent(FocusEvent& event, const InputMessage& msg) {
+ event.initialize(msg.body.focus.eventId, msg.body.focus.hasFocus);
+}
-namespace android {
+void initializeCaptureEvent(CaptureEvent& event, const InputMessage& msg) {
+ event.initialize(msg.body.capture.eventId, msg.body.capture.pointerCaptureEnabled);
+}
+
+void initializeDragEvent(DragEvent& event, const InputMessage& msg) {
+ event.initialize(msg.body.drag.eventId, msg.body.drag.x, msg.body.drag.y,
+ msg.body.drag.isExiting);
+}
+
+void initializeMotionEvent(MotionEvent& event, const InputMessage& msg) {
+ uint32_t pointerCount = msg.body.motion.pointerCount;
+ PointerProperties pointerProperties[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ pointerProperties[i] = msg.body.motion.pointers[i].properties;
+ pointerCoords[i] = msg.body.motion.pointers[i].coords;
+ }
+
+ ui::Transform transform;
+ transform.set({msg.body.motion.dsdx, msg.body.motion.dtdx, msg.body.motion.tx,
+ msg.body.motion.dtdy, msg.body.motion.dsdy, msg.body.motion.ty, 0, 0, 1});
+ ui::Transform displayTransform;
+ displayTransform.set({msg.body.motion.dsdxRaw, msg.body.motion.dtdxRaw, msg.body.motion.txRaw,
+ msg.body.motion.dtdyRaw, msg.body.motion.dsdyRaw, msg.body.motion.tyRaw,
+ 0, 0, 1});
+ event.initialize(msg.body.motion.eventId, msg.body.motion.deviceId, msg.body.motion.source,
+ msg.body.motion.displayId, msg.body.motion.hmac, msg.body.motion.action,
+ msg.body.motion.actionButton, msg.body.motion.flags, msg.body.motion.edgeFlags,
+ msg.body.motion.metaState, msg.body.motion.buttonState,
+ msg.body.motion.classification, transform, msg.body.motion.xPrecision,
+ msg.body.motion.yPrecision, msg.body.motion.xCursorPosition,
+ msg.body.motion.yCursorPosition, displayTransform, msg.body.motion.downTime,
+ msg.body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
+}
+
+void addSample(MotionEvent& event, const InputMessage& msg) {
+ uint32_t pointerCount = msg.body.motion.pointerCount;
+ PointerCoords pointerCoords[pointerCount];
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ pointerCoords[i] = msg.body.motion.pointers[i].coords;
+ }
+
+ event.setMetaState(event.getMetaState() | msg.body.motion.metaState);
+ event.addSample(msg.body.motion.eventTime, pointerCoords);
+}
+
+void initializeTouchModeEvent(TouchModeEvent& event, const InputMessage& msg) {
+ event.initialize(msg.body.touchMode.eventId, msg.body.touchMode.isInTouchMode);
+}
// Socket buffer size. The default is typically about 128KB, which is much larger than
// we really need. So we make it smaller. It just needs to be big enough to hold
// a few dozen large multi-finger motion events in the case where an application gets
// behind processing touches.
-static const size_t SOCKET_BUFFER_SIZE = 32 * 1024;
+static constexpr size_t SOCKET_BUFFER_SIZE = 32 * 1024;
// Nanoseconds per milliseconds.
-static const nsecs_t NANOS_PER_MS = 1000000;
+static constexpr nsecs_t NANOS_PER_MS = 1000000;
// Latency added during resampling. A few milliseconds doesn't hurt much but
// reduces the impact of mispredicted touch positions.
@@ -157,32 +215,28 @@
* Crash if the events that are getting sent to the InputPublisher are inconsistent.
* Enable this via "adb shell setprop log.tag.InputTransportVerifyEvents DEBUG"
*/
-static bool verifyEvents() {
+bool verifyEvents() {
return input_flags::enable_outbound_event_verification() ||
__android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "VerifyEvents", ANDROID_LOG_INFO);
}
-template<typename T>
-inline static T min(const T& a, const T& b) {
- return a < b ? a : b;
-}
-
-inline static float lerp(float a, float b, float alpha) {
+inline float lerp(float a, float b, float alpha) {
return a + alpha * (b - a);
}
-inline static bool isPointerEvent(int32_t source) {
+inline bool isPointerEvent(int32_t source) {
return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER;
}
-inline static const char* toString(bool value) {
- return value ? "true" : "false";
-}
-
-static bool shouldResampleTool(ToolType toolType) {
+bool shouldResampleTool(ToolType toolType) {
return toolType == ToolType::FINGER || toolType == ToolType::UNKNOWN;
}
+} // namespace
+
+using android::base::Result;
+using android::base::StringPrintf;
+
// --- InputMessage ---
bool InputMessage::isValid(size_t actualSize) const {
@@ -902,7 +956,7 @@
KeyEvent* keyEvent = factory->createKeyEvent();
if (!keyEvent) return NO_MEMORY;
- initializeKeyEvent(keyEvent, &mMsg);
+ initializeKeyEvent(*keyEvent, mMsg);
*outSeq = mMsg.header.seq;
*outEvent = keyEvent;
ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
@@ -965,7 +1019,7 @@
if (!motionEvent) return NO_MEMORY;
updateTouchState(mMsg);
- initializeMotionEvent(motionEvent, &mMsg);
+ initializeMotionEvent(*motionEvent, mMsg);
*outSeq = mMsg.header.seq;
*outEvent = motionEvent;
@@ -987,7 +1041,7 @@
FocusEvent* focusEvent = factory->createFocusEvent();
if (!focusEvent) return NO_MEMORY;
- initializeFocusEvent(focusEvent, &mMsg);
+ initializeFocusEvent(*focusEvent, mMsg);
*outSeq = mMsg.header.seq;
*outEvent = focusEvent;
break;
@@ -997,7 +1051,7 @@
CaptureEvent* captureEvent = factory->createCaptureEvent();
if (!captureEvent) return NO_MEMORY;
- initializeCaptureEvent(captureEvent, &mMsg);
+ initializeCaptureEvent(*captureEvent, mMsg);
*outSeq = mMsg.header.seq;
*outEvent = captureEvent;
break;
@@ -1007,7 +1061,7 @@
DragEvent* dragEvent = factory->createDragEvent();
if (!dragEvent) return NO_MEMORY;
- initializeDragEvent(dragEvent, &mMsg);
+ initializeDragEvent(*dragEvent, mMsg);
*outSeq = mMsg.header.seq;
*outEvent = dragEvent;
break;
@@ -1017,7 +1071,7 @@
TouchModeEvent* touchModeEvent = factory->createTouchModeEvent();
if (!touchModeEvent) return NO_MEMORY;
- initializeTouchModeEvent(touchModeEvent, &mMsg);
+ initializeTouchModeEvent(*touchModeEvent, mMsg);
*outSeq = mMsg.header.seq;
*outEvent = touchModeEvent;
break;
@@ -1079,9 +1133,9 @@
seqChain.seq = msg.header.seq;
seqChain.chain = chain;
mSeqChains.push_back(seqChain);
- addSample(motionEvent, &msg);
+ addSample(*motionEvent, msg);
} else {
- initializeMotionEvent(motionEvent, &msg);
+ initializeMotionEvent(*motionEvent, msg);
}
chain = msg.header.seq;
}
@@ -1262,7 +1316,7 @@
delta);
return;
}
- nsecs_t maxPredict = current->eventTime + min(delta / 2, RESAMPLE_MAX_PREDICTION);
+ nsecs_t maxPredict = current->eventTime + std::min(delta / 2, RESAMPLE_MAX_PREDICTION);
if (sampleTime > maxPredict) {
ALOGD_IF(debugResampling(),
"Sample time is too far in the future, adjusting prediction "
@@ -1465,69 +1519,6 @@
return -1;
}
-void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) {
- event->initialize(msg->body.key.eventId, msg->body.key.deviceId, msg->body.key.source,
- msg->body.key.displayId, msg->body.key.hmac, msg->body.key.action,
- msg->body.key.flags, msg->body.key.keyCode, msg->body.key.scanCode,
- msg->body.key.metaState, msg->body.key.repeatCount, msg->body.key.downTime,
- msg->body.key.eventTime);
-}
-
-void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
- event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus);
-}
-
-void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg) {
- event->initialize(msg->body.capture.eventId, msg->body.capture.pointerCaptureEnabled);
-}
-
-void InputConsumer::initializeDragEvent(DragEvent* event, const InputMessage* msg) {
- event->initialize(msg->body.drag.eventId, msg->body.drag.x, msg->body.drag.y,
- msg->body.drag.isExiting);
-}
-
-void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
- uint32_t pointerCount = msg->body.motion.pointerCount;
- PointerProperties pointerProperties[pointerCount];
- PointerCoords pointerCoords[pointerCount];
- for (uint32_t i = 0; i < pointerCount; i++) {
- pointerProperties[i] = msg->body.motion.pointers[i].properties;
- pointerCoords[i] = msg->body.motion.pointers[i].coords;
- }
-
- ui::Transform transform;
- transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx,
- msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1});
- ui::Transform displayTransform;
- displayTransform.set({msg->body.motion.dsdxRaw, msg->body.motion.dtdxRaw,
- msg->body.motion.txRaw, msg->body.motion.dtdyRaw,
- msg->body.motion.dsdyRaw, msg->body.motion.tyRaw, 0, 0, 1});
- event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,
- msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
- msg->body.motion.actionButton, msg->body.motion.flags,
- msg->body.motion.edgeFlags, msg->body.motion.metaState,
- msg->body.motion.buttonState, msg->body.motion.classification, transform,
- msg->body.motion.xPrecision, msg->body.motion.yPrecision,
- msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
- displayTransform, msg->body.motion.downTime, msg->body.motion.eventTime,
- pointerCount, pointerProperties, pointerCoords);
-}
-
-void InputConsumer::initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg) {
- event->initialize(msg->body.touchMode.eventId, msg->body.touchMode.isInTouchMode);
-}
-
-void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
- uint32_t pointerCount = msg->body.motion.pointerCount;
- PointerCoords pointerCoords[pointerCount];
- for (uint32_t i = 0; i < pointerCount; i++) {
- pointerCoords[i] = msg->body.motion.pointers[i].coords;
- }
-
- event->setMetaState(event->getMetaState() | msg->body.motion.metaState);
- event->addSample(msg->body.motion.eventTime, pointerCoords);
-}
-
bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) {
const InputMessage& head = batch.samples[0];
uint32_t pointerCount = msg->body.motion.pointerCount;
diff --git a/libs/input/MotionPredictorMetricsManager.cpp b/libs/input/MotionPredictorMetricsManager.cpp
index 0412d08..6872af2 100644
--- a/libs/input/MotionPredictorMetricsManager.cpp
+++ b/libs/input/MotionPredictorMetricsManager.cpp
@@ -113,7 +113,12 @@
// Adds new predictions to mRecentPredictions and maintains the invariant that elements are
// sorted in ascending order of targetTimestamp.
void MotionPredictorMetricsManager::onPredict(const MotionEvent& predictionEvent) {
- for (size_t i = 0; i < predictionEvent.getHistorySize() + 1; ++i) {
+ const size_t numPredictions = predictionEvent.getHistorySize() + 1;
+ if (numPredictions > mMaxNumPredictions) {
+ LOG(WARNING) << "numPredictions (" << numPredictions << ") > mMaxNumPredictions ("
+ << mMaxNumPredictions << "). Ignoring extra predictions in metrics.";
+ }
+ for (size_t i = 0; (i < numPredictions) && (i < mMaxNumPredictions); ++i) {
// Convert MotionEvent to PredictionPoint.
const PointerCoords* coords =
predictionEvent.getHistoricalRawPointerCoords(/*pointerIndex=*/0, i);
@@ -325,42 +330,44 @@
mAtomFields[i].highVelocityOffTrajectoryRmse =
static_cast<int>(offTrajectoryRmse * 1000);
}
+ }
- // Scale-invariant errors: reported only for the last time bucket, where the values
- // represent an average across all time buckets.
- if (i + 1 == mMaxNumPredictions) {
- // Compute error averages.
- float alongTrajectoryRmseSum = 0;
- float offTrajectoryRmseSum = 0;
- for (size_t j = 0; j < mAggregatedMetrics.size(); ++j) {
- // If we have general errors (checked above), we should always also have
- // scale-invariant errors.
- LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantErrorsCount == 0,
- "mAggregatedMetrics[%zu].scaleInvariantErrorsCount is 0", j);
-
- LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse < 0,
- "mAggregatedMetrics[%zu].scaleInvariantAlongTrajectorySse = %f "
- "should not be negative",
- j, mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse);
- alongTrajectoryRmseSum +=
- std::sqrt(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse /
- mAggregatedMetrics[j].scaleInvariantErrorsCount);
-
- LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse < 0,
- "mAggregatedMetrics[%zu].scaleInvariantOffTrajectorySse = %f "
- "should not be negative",
- j, mAggregatedMetrics[j].scaleInvariantOffTrajectorySse);
- offTrajectoryRmseSum +=
- std::sqrt(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse /
- mAggregatedMetrics[j].scaleInvariantErrorsCount);
+ // Scale-invariant errors: the average scale-invariant error across all time buckets
+ // is reported in the last time bucket.
+ {
+ // Compute error averages.
+ float alongTrajectoryRmseSum = 0;
+ float offTrajectoryRmseSum = 0;
+ int bucket_count = 0;
+ for (size_t j = 0; j < mAggregatedMetrics.size(); ++j) {
+ if (mAggregatedMetrics[j].scaleInvariantErrorsCount == 0) {
+ continue;
}
- const float averageAlongTrajectoryRmse =
- alongTrajectoryRmseSum / mAggregatedMetrics.size();
+ LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse < 0,
+ "mAggregatedMetrics[%zu].scaleInvariantAlongTrajectorySse = %f "
+ "should not be negative",
+ j, mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse);
+ alongTrajectoryRmseSum +=
+ std::sqrt(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse /
+ mAggregatedMetrics[j].scaleInvariantErrorsCount);
+
+ LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse < 0,
+ "mAggregatedMetrics[%zu].scaleInvariantOffTrajectorySse = %f "
+ "should not be negative",
+ j, mAggregatedMetrics[j].scaleInvariantOffTrajectorySse);
+ offTrajectoryRmseSum += std::sqrt(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse /
+ mAggregatedMetrics[j].scaleInvariantErrorsCount);
+
+ ++bucket_count;
+ }
+
+ if (bucket_count > 0) {
+ const float averageAlongTrajectoryRmse = alongTrajectoryRmseSum / bucket_count;
mAtomFields.back().scaleInvariantAlongTrajectoryRmse =
static_cast<int>(averageAlongTrajectoryRmse * 1000);
- const float averageOffTrajectoryRmse = offTrajectoryRmseSum / mAggregatedMetrics.size();
+ const float averageOffTrajectoryRmse = offTrajectoryRmseSum / bucket_count;
mAtomFields.back().scaleInvariantOffTrajectoryRmse =
static_cast<int>(averageOffTrajectoryRmse * 1000);
}
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index 60feb53..02d4c07 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -16,8 +16,6 @@
#include <array>
-#include "TestHelpers.h"
-
#include <unistd.h>
#include <time.h>
#include <errno.h>
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 540766d..0df06b7 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -371,8 +371,10 @@
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
ASSERT_EQ(MotionClassification::NONE, event->getClassification());
EXPECT_EQ(mTransform, event->getTransform());
- ASSERT_EQ(X_OFFSET, event->getXOffset());
- ASSERT_EQ(Y_OFFSET, event->getYOffset());
+ ASSERT_NEAR((-RAW_X_OFFSET / RAW_X_SCALE) * X_SCALE + X_OFFSET, event->getRawXOffset(),
+ EPSILON);
+ ASSERT_NEAR((-RAW_Y_OFFSET / RAW_Y_SCALE) * Y_SCALE + Y_OFFSET, event->getRawYOffset(),
+ EPSILON);
ASSERT_EQ(2.0f, event->getXPrecision());
ASSERT_EQ(2.1f, event->getYPrecision());
ASSERT_EQ(ARBITRARY_DOWN_TIME, event->getDownTime());
@@ -709,22 +711,26 @@
TEST_F(MotionEventTest, OffsetLocation) {
MotionEvent event;
initializeEventWithHistory(&event);
+ const float xOffset = event.getRawXOffset();
+ const float yOffset = event.getRawYOffset();
event.offsetLocation(5.0f, -2.0f);
- ASSERT_EQ(X_OFFSET + 5.0f, event.getXOffset());
- ASSERT_EQ(Y_OFFSET - 2.0f, event.getYOffset());
+ ASSERT_EQ(xOffset + 5.0f, event.getRawXOffset());
+ ASSERT_EQ(yOffset - 2.0f, event.getRawYOffset());
}
TEST_F(MotionEventTest, Scale) {
MotionEvent event;
initializeEventWithHistory(&event);
const float unscaledOrientation = event.getOrientation(0);
+ const float unscaledXOffset = event.getRawXOffset();
+ const float unscaledYOffset = event.getRawYOffset();
event.scale(2.0f);
- ASSERT_EQ(X_OFFSET * 2, event.getXOffset());
- ASSERT_EQ(Y_OFFSET * 2, event.getYOffset());
+ ASSERT_EQ(unscaledXOffset * 2, event.getRawXOffset());
+ ASSERT_EQ(unscaledYOffset * 2, event.getRawYOffset());
ASSERT_NEAR((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0), EPSILON);
ASSERT_NEAR((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0), EPSILON);
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 3543020..b5fab49 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "TestHelpers.h"
-
#include <attestation/HmacKeyManager.h>
#include <gtest/gtest.h>
#include <gui/constants.h>
@@ -135,8 +133,10 @@
EXPECT_EQ(args.buttonState, motionEvent.getButtonState());
EXPECT_EQ(args.classification, motionEvent.getClassification());
EXPECT_EQ(args.transform, motionEvent.getTransform());
- EXPECT_EQ(args.xOffset, motionEvent.getXOffset());
- EXPECT_EQ(args.yOffset, motionEvent.getYOffset());
+ EXPECT_NEAR((-args.rawXOffset / args.rawXScale) * args.xScale + args.xOffset,
+ motionEvent.getRawXOffset(), EPSILON);
+ EXPECT_NEAR((-args.rawYOffset / args.rawYScale) * args.yScale + args.yOffset,
+ motionEvent.getRawYOffset(), EPSILON);
EXPECT_EQ(args.xPrecision, motionEvent.getXPrecision());
EXPECT_EQ(args.yPrecision, motionEvent.getYPrecision());
EXPECT_NEAR(args.xCursorPosition, motionEvent.getRawXCursorPosition(), EPSILON);
diff --git a/libs/input/tests/MotionPredictorMetricsManager_test.cpp b/libs/input/tests/MotionPredictorMetricsManager_test.cpp
index 31cc145..cc41eeb 100644
--- a/libs/input/tests/MotionPredictorMetricsManager_test.cpp
+++ b/libs/input/tests/MotionPredictorMetricsManager_test.cpp
@@ -238,14 +238,17 @@
// --- Ground-truth-generation helper functions. ---
+// Generates numPoints ground truth points with values equal to those of the given
+// GroundTruthPoint, and with consecutive timestamps separated by the given inputInterval.
std::vector<GroundTruthPoint> generateConstantGroundTruthPoints(
- const GroundTruthPoint& groundTruthPoint, size_t numPoints) {
+ const GroundTruthPoint& groundTruthPoint, size_t numPoints,
+ nsecs_t inputInterval = TEST_PREDICTION_INTERVAL_NANOS) {
std::vector<GroundTruthPoint> groundTruthPoints;
nsecs_t timestamp = groundTruthPoint.timestamp;
for (size_t i = 0; i < numPoints; ++i) {
groundTruthPoints.emplace_back(groundTruthPoint);
groundTruthPoints.back().timestamp = timestamp;
- timestamp += TEST_PREDICTION_INTERVAL_NANOS;
+ timestamp += inputInterval;
}
return groundTruthPoints;
}
@@ -280,7 +283,8 @@
const GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10, 20), .pressure = 0.3f},
.timestamp = TEST_INITIAL_TIMESTAMP};
const std::vector<GroundTruthPoint> groundTruthPoints =
- generateConstantGroundTruthPoints(groundTruthPoint, /*numPoints=*/3);
+ generateConstantGroundTruthPoints(groundTruthPoint, /*numPoints=*/3,
+ /*inputInterval=*/10);
ASSERT_EQ(3u, groundTruthPoints.size());
// First point.
@@ -290,11 +294,11 @@
// Second point.
EXPECT_EQ(groundTruthPoints[1].position, groundTruthPoint.position);
EXPECT_EQ(groundTruthPoints[1].pressure, groundTruthPoint.pressure);
- EXPECT_GT(groundTruthPoints[1].timestamp, groundTruthPoints[0].timestamp);
+ EXPECT_EQ(groundTruthPoints[1].timestamp, groundTruthPoint.timestamp + 10);
// Third point.
EXPECT_EQ(groundTruthPoints[2].position, groundTruthPoint.position);
EXPECT_EQ(groundTruthPoints[2].pressure, groundTruthPoint.pressure);
- EXPECT_GT(groundTruthPoints[2].timestamp, groundTruthPoints[1].timestamp);
+ EXPECT_EQ(groundTruthPoints[2].timestamp, groundTruthPoint.timestamp + 20);
}
TEST(GenerateCircularArcGroundTruthTest, StraightLineUpwards) {
@@ -333,16 +337,19 @@
// --- Prediction-generation helper functions. ---
-// Creates a sequence of predictions with values equal to those of the given GroundTruthPoint.
-std::vector<PredictionPoint> generateConstantPredictions(const GroundTruthPoint& groundTruthPoint) {
+// Generates TEST_MAX_NUM_PREDICTIONS predictions with values equal to those of the given
+// GroundTruthPoint, and with consecutive timestamps separated by the given predictionInterval.
+std::vector<PredictionPoint> generateConstantPredictions(
+ const GroundTruthPoint& groundTruthPoint,
+ nsecs_t predictionInterval = TEST_PREDICTION_INTERVAL_NANOS) {
std::vector<PredictionPoint> predictions;
- nsecs_t predictionTimestamp = groundTruthPoint.timestamp + TEST_PREDICTION_INTERVAL_NANOS;
+ nsecs_t predictionTimestamp = groundTruthPoint.timestamp + predictionInterval;
for (size_t j = 0; j < TEST_MAX_NUM_PREDICTIONS; ++j) {
predictions.push_back(PredictionPoint{{.position = groundTruthPoint.position,
.pressure = groundTruthPoint.pressure},
.originTimestamp = groundTruthPoint.timestamp,
.targetTimestamp = predictionTimestamp});
- predictionTimestamp += TEST_PREDICTION_INTERVAL_NANOS;
+ predictionTimestamp += predictionInterval;
}
return predictions;
}
@@ -375,8 +382,9 @@
TEST(GeneratePredictionsTest, GenerateConstantPredictions) {
const GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10, 20), .pressure = 0.3f},
.timestamp = TEST_INITIAL_TIMESTAMP};
+ const nsecs_t predictionInterval = 10;
const std::vector<PredictionPoint> predictionPoints =
- generateConstantPredictions(groundTruthPoint);
+ generateConstantPredictions(groundTruthPoint, predictionInterval);
ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, predictionPoints.size());
for (size_t i = 0; i < predictionPoints.size(); ++i) {
@@ -385,8 +393,7 @@
EXPECT_THAT(predictionPoints[i].pressure, FloatNear(groundTruthPoint.pressure, 1e-6));
EXPECT_EQ(predictionPoints[i].originTimestamp, groundTruthPoint.timestamp);
EXPECT_EQ(predictionPoints[i].targetTimestamp,
- groundTruthPoint.timestamp +
- static_cast<nsecs_t>(i + 1) * TEST_PREDICTION_INTERVAL_NANOS);
+ TEST_INITIAL_TIMESTAMP + static_cast<nsecs_t>(i + 1) * predictionInterval);
}
}
@@ -678,12 +685,9 @@
// • groundTruthPoints: chronologically-ordered ground truth points, with at least 2 elements.
// • predictionPoints: the first index points to a vector of predictions corresponding to the
// source ground truth point with the same index.
-// - The first element should be empty, because there are not expected to be predictions until
-// we have received 2 ground truth points.
-// - The last element may be empty, because there will be no future ground truth points to
-// associate with those predictions (if not empty, it will be ignored).
+// - For empty prediction vectors, MetricsManager::onPredict will not be called.
// - To test all prediction buckets, there should be at least TEST_MAX_NUM_PREDICTIONS non-empty
-// prediction sets (that is, excluding the first and last). Thus, groundTruthPoints and
+// prediction vectors (that is, excluding the first and last). Thus, groundTruthPoints and
// predictionPoints should have size at least TEST_MAX_NUM_PREDICTIONS + 2.
//
// When the function returns, outReportedAtomFields will contain the reported AtomFields.
@@ -697,19 +701,12 @@
createMockReportAtomFunction(
outReportedAtomFields));
- // Validate structure of groundTruthPoints and predictionPoints.
- ASSERT_EQ(predictionPoints.size(), groundTruthPoints.size());
ASSERT_GE(groundTruthPoints.size(), 2u);
- ASSERT_EQ(predictionPoints[0].size(), 0u);
- for (size_t i = 1; i + 1 < predictionPoints.size(); ++i) {
- SCOPED_TRACE(testing::Message() << "i = " << i);
- ASSERT_EQ(predictionPoints[i].size(), TEST_MAX_NUM_PREDICTIONS);
- }
+ ASSERT_EQ(predictionPoints.size(), groundTruthPoints.size());
- // Pass ground truth points and predictions (for all except first and last ground truth).
for (size_t i = 0; i < groundTruthPoints.size(); ++i) {
metricsManager.onRecord(makeMotionEvent(groundTruthPoints[i]));
- if ((i > 0) && (i + 1 < predictionPoints.size())) {
+ if (!predictionPoints[i].empty()) {
metricsManager.onPredict(makeMotionEvent(predictionPoints[i]));
}
}
@@ -738,7 +735,7 @@
// Perfect predictions test:
// • Input: constant input events, perfect predictions matching the input events.
// • Expectation: all error metrics should be zero, or NO_DATA_SENTINEL for "unreported" metrics.
-// (For example, scale-invariant errors are only reported for the final time bucket.)
+// (For example, scale-invariant errors are only reported for the last time bucket.)
TEST(MotionPredictorMetricsManagerTest, ConstantGroundTruthPerfectPredictions) {
GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10.0f, 20.0f), .pressure = 0.6f},
.timestamp = TEST_INITIAL_TIMESTAMP};
@@ -977,5 +974,35 @@
}
}
+// Robustness test:
+// • Input: input events separated by a significantly greater time interval than the interval
+// between predictions.
+// • Expectation: the MetricsManager should not crash in this case. (No assertions are made about
+// the resulting metrics.)
+//
+// In practice, this scenario could arise either if the input and prediction intervals are
+// mismatched, or if input events are missing (dropped or skipped for some reason).
+TEST(MotionPredictorMetricsManagerTest, MismatchedInputAndPredictionInterval) {
+ // Create two ground truth points separated by MAX_NUM_PREDICTIONS * PREDICTION_INTERVAL,
+ // so that the second ground truth point corresponds to the last prediction bucket. This
+ // ensures that the scale-invariant error codepath will be run, giving full code coverage.
+ GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(0.0f, 0.0f), .pressure = 0.5f},
+ .timestamp = TEST_INITIAL_TIMESTAMP};
+ const nsecs_t inputInterval = TEST_MAX_NUM_PREDICTIONS * TEST_PREDICTION_INTERVAL_NANOS;
+ const std::vector<GroundTruthPoint> groundTruthPoints =
+ generateConstantGroundTruthPoints(groundTruthPoint, /*numPoints=*/2, inputInterval);
+
+ // Create predictions separated by the prediction interval.
+ std::vector<std::vector<PredictionPoint>> predictionPoints;
+ for (size_t i = 0; i < groundTruthPoints.size(); ++i) {
+ predictionPoints.push_back(
+ generateConstantPredictions(groundTruthPoints[i], TEST_PREDICTION_INTERVAL_NANOS));
+ }
+
+ // Test that we can run the MetricsManager without crashing.
+ std::vector<AtomFields> reportedAtomFields;
+ runMetricsManager(groundTruthPoints, predictionPoints, reportedAtomFields);
+}
+
} // namespace
} // namespace android
diff --git a/libs/input/tests/TestHelpers.h b/libs/input/tests/TestHelpers.h
deleted file mode 100644
index 343d81f..0000000
--- a/libs/input/tests/TestHelpers.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TESTHELPERS_H
-#define TESTHELPERS_H
-
-#include <unistd.h>
-
-#include <utils/threads.h>
-
-namespace android {
-
-class Pipe {
-public:
- int sendFd;
- int receiveFd;
-
- Pipe() {
- int fds[2];
- ::pipe(fds);
-
- receiveFd = fds[0];
- sendFd = fds[1];
- }
-
- ~Pipe() {
- if (sendFd != -1) {
- ::close(sendFd);
- }
-
- if (receiveFd != -1) {
- ::close(receiveFd);
- }
- }
-
- status_t writeSignal() {
- ssize_t nWritten = ::write(sendFd, "*", 1);
- return nWritten == 1 ? 0 : -errno;
- }
-
- status_t readSignal() {
- char buf[1];
- ssize_t nRead = ::read(receiveFd, buf, 1);
- return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
- }
-};
-
-class DelayedTask : public Thread {
- int mDelayMillis;
-
-public:
- explicit DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { }
-
-protected:
- virtual ~DelayedTask() { }
-
- virtual void doTask() = 0;
-
- virtual bool threadLoop() {
- usleep(mDelayMillis * 1000);
- doTask();
- return false;
- }
-};
-
-} // namespace android
-
-#endif // TESTHELPERS_H
diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp
index 1cb7f7b..0b0bb63 100644
--- a/libs/input/tests/TouchResampling_test.cpp
+++ b/libs/input/tests/TouchResampling_test.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "TestHelpers.h"
-
#include <chrono>
#include <vector>
diff --git a/libs/math/include/math/mat4.h b/libs/math/include/math/mat4.h
index 6119ba7..c630d97 100644
--- a/libs/math/include/math/mat4.h
+++ b/libs/math/include/math/mat4.h
@@ -34,6 +34,14 @@
#define CONSTEXPR
#endif
+#ifdef _WIN32
+// windows.h contains obsolete defines of 'near' and 'far' for systems using
+// legacy 16 bit pointers. Undefine them to avoid conflicting with the usage of
+// 'near' and 'far' in this file.
+#undef near
+#undef far
+#endif
+
namespace android {
// -------------------------------------------------------------------------------------
namespace details {
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 8f005a5..bed31e2 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -148,29 +148,31 @@
void AChoreographer_postFrameCallback(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data) {
AChoreographer_to_Choreographer(choreographer)
- ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0);
+ ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0, CALLBACK_ANIMATION);
}
void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data,
long delayMillis) {
AChoreographer_to_Choreographer(choreographer)
- ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis));
+ ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis),
+ CALLBACK_ANIMATION);
}
void AChoreographer_postVsyncCallback(AChoreographer* choreographer,
AChoreographer_vsyncCallback callback, void* data) {
AChoreographer_to_Choreographer(choreographer)
- ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0);
+ ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0, CALLBACK_ANIMATION);
}
void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
AChoreographer_frameCallback64 callback, void* data) {
AChoreographer_to_Choreographer(choreographer)
- ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0);
+ ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0, CALLBACK_ANIMATION);
}
void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
AChoreographer_frameCallback64 callback, void* data,
uint32_t delayMillis) {
AChoreographer_to_Choreographer(choreographer)
- ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis));
+ ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis),
+ CALLBACK_ANIMATION);
}
void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index 241ee2c..f2f1b5d 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -25,6 +25,7 @@
#include <GrContextOptions.h>
#include <vk/GrVkExtensions.h>
#include <vk/GrVkTypes.h>
+#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
#include <android-base/stringprintf.h>
@@ -151,8 +152,7 @@
base::unique_fd fenceDup(dupedFd);
VkSemaphore waitSemaphore =
getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release());
- GrBackendSemaphore beSemaphore;
- beSemaphore.initVulkan(waitSemaphore);
+ GrBackendSemaphore beSemaphore = GrBackendSemaphores::MakeVk(waitSemaphore);
grContext->wait(1, &beSemaphore, true /* delete after wait */);
}
@@ -160,8 +160,7 @@
VulkanInterface& vi = getVulkanInterface(isProtected());
VkSemaphore semaphore = vi.createExportableSemaphore();
- GrBackendSemaphore backendSemaphore;
- backendSemaphore.initVulkan(semaphore);
+ GrBackendSemaphore backendSemaphore = GrBackendSemaphores::MakeVk(semaphore);
GrFlushInfo flushInfo;
DestroySemaphoreInfo* destroySemaphoreInfo = nullptr;
diff --git a/libs/sensorprivacy/Android.bp b/libs/sensorprivacy/Android.bp
index 1e7e707..00514c4 100644
--- a/libs/sensorprivacy/Android.bp
+++ b/libs/sensorprivacy/Android.bp
@@ -57,7 +57,6 @@
filegroup {
name: "libsensorprivacy_aidl",
srcs: [
- "aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl",
"aidl/android/hardware/ISensorPrivacyListener.aidl",
"aidl/android/hardware/ISensorPrivacyManager.aidl",
],
diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp
index fe93786..3f3ad93 100644
--- a/libs/sensorprivacy/SensorPrivacyManager.cpp
+++ b/libs/sensorprivacy/SensorPrivacyManager.cpp
@@ -155,10 +155,9 @@
return DISABLED;
}
-std::vector<hardware::CameraPrivacyAllowlistEntry>
- SensorPrivacyManager::getCameraPrivacyAllowlist(){
+std::vector<String16> SensorPrivacyManager::getCameraPrivacyAllowlist(){
sp<hardware::ISensorPrivacyManager> service = getService();
- std::vector<hardware::CameraPrivacyAllowlistEntry> result;
+ std::vector<String16> result;
if (service != nullptr) {
service->getCameraPrivacyAllowlist(&result);
return result;
diff --git a/libs/sensorprivacy/aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl b/libs/sensorprivacy/aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl
deleted file mode 100644
index 03e1537..0000000
--- a/libs/sensorprivacy/aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Copyright (c) 2024, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware;
-
-parcelable CameraPrivacyAllowlistEntry {
- String packageName;
- boolean isMandatory;
-}
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
index b6bd39e..f707187 100644
--- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
+++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
@@ -16,7 +16,6 @@
package android.hardware;
-import android.hardware.CameraPrivacyAllowlistEntry;
import android.hardware.ISensorPrivacyListener;
/** @hide */
@@ -43,7 +42,7 @@
void setToggleSensorPrivacyForProfileGroup(int userId, int source, int sensor, boolean enable);
- List<CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist();
+ List<String> getCameraPrivacyAllowlist();
int getToggleSensorPrivacyState(int toggleType, int sensor);
diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
index 9e97e16..8935b76 100644
--- a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
+++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
@@ -45,9 +45,7 @@
enum {
ENABLED = 1,
DISABLED = 2,
- AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS = 3,
- AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS = 4,
- AUTOMOTIVE_DRIVER_ASSISTANCE_APPS = 5
+ ENABLED_EXCEPT_ALLOWLISTED_APPS = 3
};
SensorPrivacyManager();
@@ -62,7 +60,7 @@
bool isToggleSensorPrivacyEnabled(int toggleType, int sensor);
status_t isToggleSensorPrivacyEnabled(int toggleType, int sensor, bool &result);
int getToggleSensorPrivacyState(int toggleType, int sensor);
- std::vector<hardware::CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist();
+ std::vector<String16> getCameraPrivacyAllowlist();
bool isCameraPrivacyEnabled(String16 packageName);
status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 3ac4285..9db3574 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -26,6 +26,7 @@
namespace android {
namespace {
+
bool isFromMouse(const NotifyMotionArgs& args) {
return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
args.pointerProperties[0].toolType == ToolType::MOUSE;
@@ -44,13 +45,23 @@
bool isStylusHoverEvent(const NotifyMotionArgs& args) {
return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action);
}
+
+inline void notifyPointerDisplayChange(std::optional<std::tuple<int32_t, FloatPoint>> change,
+ PointerChoreographerPolicyInterface& policy) {
+ if (!change) {
+ return;
+ }
+ const auto& [displayId, cursorPosition] = *change;
+ policy.notifyPointerDisplayIdChanged(displayId, cursorPosition);
+}
+
} // namespace
// --- PointerChoreographer ---
PointerChoreographer::PointerChoreographer(InputListenerInterface& listener,
PointerChoreographerPolicyInterface& policy)
- : mTouchControllerConstructor([this]() REQUIRES(mLock) {
+ : mTouchControllerConstructor([this]() {
return mPolicy.createPointerController(
PointerControllerInterface::ControllerType::TOUCH);
}),
@@ -62,10 +73,16 @@
mStylusPointerIconEnabled(false) {}
void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
- std::scoped_lock _l(mLock);
+ PointerDisplayChange pointerDisplayChange;
- mInputDeviceInfos = args.inputDeviceInfos;
- updatePointerControllersLocked();
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+
+ mInputDeviceInfos = args.inputDeviceInfos;
+ pointerDisplayChange = updatePointerControllersLocked();
+ } // release lock
+
+ notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
mNextListener.notify(args);
}
@@ -104,7 +121,7 @@
<< args.dump();
}
- auto [displayId, pc] = getDisplayIdAndMouseControllerLocked(args.displayId);
+ auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
@@ -124,7 +141,7 @@
}
NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMotionArgs& args) {
- auto [displayId, pc] = getDisplayIdAndMouseControllerLocked(args.displayId);
+ auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
NotifyMotionArgs newArgs(args);
newArgs.displayId = displayId;
@@ -308,17 +325,13 @@
return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId;
}
-std::pair<int32_t, PointerControllerInterface&>
-PointerChoreographer::getDisplayIdAndMouseControllerLocked(int32_t associatedDisplayId) {
+std::pair<int32_t, PointerControllerInterface&> PointerChoreographer::ensureMouseControllerLocked(
+ int32_t associatedDisplayId) {
const int32_t displayId = getTargetMouseDisplayLocked(associatedDisplayId);
- // Get the mouse pointer controller for the display, or create one if it doesn't exist.
- auto [it, emplaced] =
- mMousePointersByDisplay.try_emplace(displayId,
- getMouseControllerConstructor(displayId));
- if (emplaced) {
- notifyPointerDisplayIdChangedLocked();
- }
+ auto it = mMousePointersByDisplay.find(displayId);
+ LOG_ALWAYS_FATAL_IF(it == mMousePointersByDisplay.end(),
+ "There is no mouse controller created for display %d", displayId);
return {displayId, *it->second};
}
@@ -333,7 +346,7 @@
return mDisplaysWithPointersHidden.find(displayId) == mDisplaysWithPointersHidden.end();
}
-void PointerChoreographer::updatePointerControllersLocked() {
+PointerChoreographer::PointerDisplayChange PointerChoreographer::updatePointerControllersLocked() {
std::set<int32_t /*displayId*/> mouseDisplaysToKeep;
std::set<DeviceId> touchDevicesToKeep;
std::set<DeviceId> stylusDevicesToKeep;
@@ -382,11 +395,12 @@
mInputDeviceInfos.end();
});
- // Notify the policy if there's a change on the pointer display ID.
- notifyPointerDisplayIdChangedLocked();
+ // Check if we need to notify the policy if there's a change on the pointer display ID.
+ return calculatePointerDisplayChangeToNotify();
}
-void PointerChoreographer::notifyPointerDisplayIdChangedLocked() {
+PointerChoreographer::PointerDisplayChange
+PointerChoreographer::calculatePointerDisplayChangeToNotify() {
int32_t displayIdToNotify = ADISPLAY_ID_NONE;
FloatPoint cursorPosition = {0, 0};
if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
@@ -398,38 +412,49 @@
displayIdToNotify = pointerController->getDisplayId();
cursorPosition = pointerController->getPosition();
}
-
if (mNotifiedPointerDisplayId == displayIdToNotify) {
- return;
+ return {};
}
- mPolicy.notifyPointerDisplayIdChanged(displayIdToNotify, cursorPosition);
mNotifiedPointerDisplayId = displayIdToNotify;
+ return {{displayIdToNotify, cursorPosition}};
}
void PointerChoreographer::setDefaultMouseDisplayId(int32_t displayId) {
- std::scoped_lock _l(mLock);
+ PointerDisplayChange pointerDisplayChange;
- mDefaultMouseDisplayId = displayId;
- updatePointerControllersLocked();
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+
+ mDefaultMouseDisplayId = displayId;
+ pointerDisplayChange = updatePointerControllersLocked();
+ } // release lock
+
+ notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
}
void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
- std::scoped_lock _l(mLock);
- for (const auto& viewport : viewports) {
- const int32_t displayId = viewport.displayId;
- if (const auto it = mMousePointersByDisplay.find(displayId);
- it != mMousePointersByDisplay.end()) {
- it->second->setDisplayViewport(viewport);
- }
- for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
- const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
- if (info && info->getAssociatedDisplayId() == displayId) {
- stylusPointerController->setDisplayViewport(viewport);
+ PointerDisplayChange pointerDisplayChange;
+
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ for (const auto& viewport : viewports) {
+ const int32_t displayId = viewport.displayId;
+ if (const auto it = mMousePointersByDisplay.find(displayId);
+ it != mMousePointersByDisplay.end()) {
+ it->second->setDisplayViewport(viewport);
+ }
+ for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
+ const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
+ if (info && info->getAssociatedDisplayId() == displayId) {
+ stylusPointerController->setDisplayViewport(viewport);
+ }
}
}
- }
- mViewports = viewports;
- notifyPointerDisplayIdChangedLocked();
+ mViewports = viewports;
+ pointerDisplayChange = calculatePointerDisplayChangeToNotify();
+ } // release lock
+
+ notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
}
std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
@@ -453,21 +478,33 @@
}
void PointerChoreographer::setShowTouchesEnabled(bool enabled) {
- std::scoped_lock _l(mLock);
- if (mShowTouchesEnabled == enabled) {
- return;
- }
- mShowTouchesEnabled = enabled;
- updatePointerControllersLocked();
+ PointerDisplayChange pointerDisplayChange;
+
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ if (mShowTouchesEnabled == enabled) {
+ return;
+ }
+ mShowTouchesEnabled = enabled;
+ pointerDisplayChange = updatePointerControllersLocked();
+ } // release lock
+
+ notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
}
void PointerChoreographer::setStylusPointerIconEnabled(bool enabled) {
- std::scoped_lock _l(mLock);
- if (mStylusPointerIconEnabled == enabled) {
- return;
- }
- mStylusPointerIconEnabled = enabled;
- updatePointerControllersLocked();
+ PointerDisplayChange pointerDisplayChange;
+
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ if (mStylusPointerIconEnabled == enabled) {
+ return;
+ }
+ mStylusPointerIconEnabled = enabled;
+ pointerDisplayChange = updatePointerControllersLocked();
+ } // release lock
+
+ notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
}
bool PointerChoreographer::setPointerIcon(
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index 6aab3aa..db1488b 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -109,11 +109,13 @@
void dump(std::string& dump) override;
private:
- void updatePointerControllersLocked() REQUIRES(mLock);
- void notifyPointerDisplayIdChangedLocked() REQUIRES(mLock);
+ using PointerDisplayChange =
+ std::optional<std::tuple<int32_t /*displayId*/, FloatPoint /*cursorPosition*/>>;
+ [[nodiscard]] PointerDisplayChange updatePointerControllersLocked() REQUIRES(mLock);
+ [[nodiscard]] PointerDisplayChange calculatePointerDisplayChangeToNotify() REQUIRES(mLock);
const DisplayViewport* findViewportByIdLocked(int32_t displayId) const REQUIRES(mLock);
int32_t getTargetMouseDisplayLocked(int32_t associatedDisplayId) const REQUIRES(mLock);
- std::pair<int32_t, PointerControllerInterface&> getDisplayIdAndMouseControllerLocked(
+ std::pair<int32_t /*displayId*/, PointerControllerInterface&> ensureMouseControllerLocked(
int32_t associatedDisplayId) REQUIRES(mLock);
InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock);
bool canUnfadeOnDisplay(int32_t displayId) REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 1298b5d..06d5c7d 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -140,6 +140,7 @@
mutable InterceptKeyResult interceptKeyResult; // set based on the interception result
mutable nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
mutable int32_t flags;
+ // TODO(b/328618922): Refactor key repeat generation to make repeatCount non-mutable.
mutable int32_t repeatCount;
KeyEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime,
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 9c04a1d..56b0c8f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -103,6 +103,26 @@
}
}
+// Helper to get a trace tracker from a traced key or motion entry.
+const std::unique_ptr<trace::EventTrackerInterface>& getTraceTracker(const EventEntry& entry) {
+ switch (entry.type) {
+ case EventEntry::Type::MOTION: {
+ const auto& motion = static_cast<const MotionEntry&>(entry);
+ ensureEventTraced(motion);
+ return motion.traceTracker;
+ }
+ case EventEntry::Type::KEY: {
+ const auto& key = static_cast<const KeyEntry&>(entry);
+ ensureEventTraced(key);
+ return key.traceTracker;
+ }
+ default: {
+ const static std::unique_ptr<trace::EventTrackerInterface> kNullTracker;
+ return kNullTracker;
+ }
+ }
+}
+
// Temporarily releases a held mutex for the lifetime of the instance.
// Named to match std::scoped_lock
class scoped_unlock {
@@ -379,7 +399,8 @@
const InputTarget& inputTarget,
std::shared_ptr<const EventEntry> eventEntry,
ftl::Flags<InputTarget::Flags> inputTargetFlags,
- int64_t vsyncId) {
+ int64_t vsyncId,
+ trace::InputTracerInterface* tracer) {
const bool zeroCoords = inputTargetFlags.test(InputTarget::Flags::ZERO_COORDS);
const sp<WindowInfoHandle> win = inputTarget.windowHandle;
const std::optional<int32_t> windowId =
@@ -442,6 +463,10 @@
motionEntry.xCursorPosition, motionEntry.yCursorPosition,
motionEntry.downTime, motionEntry.pointerProperties,
pointerCoords);
+ if (tracer) {
+ combinedMotionEntry->traceTracker =
+ tracer->traceDerivedEvent(*combinedMotionEntry, *motionEntry.traceTracker);
+ }
std::unique_ptr<DispatchEntry> dispatchEntry =
std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
@@ -656,13 +681,13 @@
std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState,
const TouchState& newTouchState,
const MotionEntry& entry) {
- std::vector<TouchedWindow> out;
const int32_t maskedAction = MotionEvent::getActionMasked(entry.action);
if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
// ACTION_SCROLL events should not affect the hovering pointer dispatch
return {};
}
+ std::vector<TouchedWindow> out;
// We should consider all hovering pointers here. But for now, just use the first one
const PointerProperties& pointer = entry.pointerProperties[0];
@@ -1147,10 +1172,6 @@
dropReason = DropReason::BLOCKED;
}
done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
- if (done && mTracer) {
- ensureEventTraced(*keyEntry);
- mTracer->eventProcessingComplete(*keyEntry->traceTracker);
- }
break;
}
@@ -1176,10 +1197,6 @@
}
}
done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
- if (done && mTracer) {
- ensureEventTraced(*motionEntry);
- mTracer->eventProcessingComplete(*motionEntry->traceTracker);
- }
break;
}
@@ -1205,6 +1222,12 @@
}
mLastDropReason = dropReason;
+ if (mTracer) {
+ if (auto& traceTracker = getTraceTracker(*mPendingEvent); traceTracker != nullptr) {
+ mTracer->eventProcessingComplete(*traceTracker);
+ }
+ }
+
releasePendingEventLocked();
nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately
}
@@ -2633,19 +2656,14 @@
{
std::vector<TouchedWindow> hoveringWindows =
getHoveringWindowsLocked(oldState, tempTouchState, entry);
+ // Hardcode to single hovering pointer for now.
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
+ pointerIds.set(entry.pointerProperties[0].id);
for (const TouchedWindow& touchedWindow : hoveringWindows) {
- std::optional<InputTarget> target =
- createInputTargetLocked(touchedWindow.windowHandle, touchedWindow.dispatchMode,
- touchedWindow.targetFlags,
- touchedWindow.getDownTimeInTarget(entry.deviceId));
- if (!target) {
- continue;
- }
- // Hardcode to single hovering pointer for now.
- std::bitset<MAX_POINTER_ID + 1> pointerIds;
- pointerIds.set(entry.pointerProperties[0].id);
- target->addPointers(pointerIds, touchedWindow.windowHandle->getInfo()->transform);
- targets.push_back(*target);
+ addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.dispatchMode,
+ touchedWindow.targetFlags, pointerIds,
+ touchedWindow.getDownTimeInTarget(entry.deviceId),
+ targets);
}
}
@@ -3373,7 +3391,7 @@
// Enqueue a new dispatch entry onto the outbound queue for this connection.
std::unique_ptr<DispatchEntry> dispatchEntry =
createDispatchEntry(mIdGenerator, inputTarget, eventEntry, inputTarget.flags,
- mWindowInfosVsyncId);
+ mWindowInfosVsyncId, mTracer.get());
// Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
// different EventEntry than what was passed in.
@@ -3456,21 +3474,31 @@
usingCoords = pointerInfo->second;
}
}
- // Generate a new MotionEntry with a new eventId using the resolved action and
- // flags.
- resolvedMotion = std::make_shared<
- MotionEntry>(mIdGenerator.nextId(), motionEntry.injectionState,
- motionEntry.eventTime, motionEntry.deviceId,
- motionEntry.source, motionEntry.displayId,
- motionEntry.policyFlags, resolvedAction,
- motionEntry.actionButton, resolvedFlags,
- motionEntry.metaState, motionEntry.buttonState,
- motionEntry.classification, motionEntry.edgeFlags,
- motionEntry.xPrecision, motionEntry.yPrecision,
- motionEntry.xCursorPosition, motionEntry.yCursorPosition,
- motionEntry.downTime,
- usingProperties.value_or(motionEntry.pointerProperties),
- usingCoords.value_or(motionEntry.pointerCoords));
+ {
+ // Generate a new MotionEntry with a new eventId using the resolved action
+ // and flags, and set it as the resolved entry.
+ auto newEntry = std::make_shared<
+ MotionEntry>(mIdGenerator.nextId(), motionEntry.injectionState,
+ motionEntry.eventTime, motionEntry.deviceId,
+ motionEntry.source, motionEntry.displayId,
+ motionEntry.policyFlags, resolvedAction,
+ motionEntry.actionButton, resolvedFlags,
+ motionEntry.metaState, motionEntry.buttonState,
+ motionEntry.classification, motionEntry.edgeFlags,
+ motionEntry.xPrecision, motionEntry.yPrecision,
+ motionEntry.xCursorPosition,
+ motionEntry.yCursorPosition, motionEntry.downTime,
+ usingProperties.value_or(
+ motionEntry.pointerProperties),
+ usingCoords.value_or(motionEntry.pointerCoords));
+ if (mTracer) {
+ ensureEventTraced(motionEntry);
+ newEntry->traceTracker =
+ mTracer->traceDerivedEvent(*newEntry,
+ *motionEntry.traceTracker);
+ }
+ resolvedMotion = newEntry;
+ }
if (ATRACE_ENABLED()) {
std::string message = StringPrintf("Transmute MotionEvent(id=0x%" PRIx32
") to MotionEvent(id=0x%" PRIx32 ").",
@@ -3495,7 +3523,8 @@
<< cancelEvent->getDescription();
std::unique_ptr<DispatchEntry> cancelDispatchEntry =
createDispatchEntry(mIdGenerator, inputTarget, std::move(cancelEvent),
- ftl::Flags<InputTarget::Flags>(), mWindowInfosVsyncId);
+ ftl::Flags<InputTarget::Flags>(), mWindowInfosVsyncId,
+ mTracer.get());
// Send these cancel events to the queue before sending the event from the new
// device.
@@ -4303,6 +4332,10 @@
originalMotionEntry.xCursorPosition,
originalMotionEntry.yCursorPosition, splitDownTime,
pointerProperties, pointerCoords);
+ if (mTracer) {
+ splitMotionEntry->traceTracker =
+ mTracer->traceDerivedEvent(*splitMotionEntry, *originalMotionEntry.traceTracker);
+ }
return splitMotionEntry;
}
@@ -4797,6 +4830,10 @@
pointerCount));
transformMotionEntryForInjectionLocked(*nextInjectedEntry,
motionEvent.getTransform());
+ if (mTracer) {
+ nextInjectedEntry->traceTracker =
+ mTracer->traceInboundEvent(*nextInjectedEntry);
+ }
injectedEntries.push(std::move(nextInjectedEntry));
}
break;
@@ -6597,6 +6634,10 @@
*fallbackKeyCode, event.getScanCode(),
event.getMetaState(), event.getRepeatCount(),
event.getDownTime());
+ if (mTracer) {
+ newEntry->traceTracker =
+ mTracer->traceDerivedEvent(*newEntry, *keyEntry.traceTracker);
+ }
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
ALOGD("Unhandled key event: Dispatching fallback key. "
"originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp
index be09013..3b5a096 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp
@@ -59,6 +59,12 @@
e.downTime, e.flags, e.repeatCount};
}
+void writeEventToBackend(const TracedEvent& event, InputTracingBackendInterface& backend) {
+ std::visit(Visitor{[&](const TracedMotionEvent& e) { backend.traceMotionEvent(e); },
+ [&](const TracedKeyEvent& e) { backend.traceKeyEvent(e); }},
+ event);
+}
+
} // namespace
// --- InputTracer ---
@@ -67,46 +73,83 @@
: mBackend(std::move(backend)) {}
std::unique_ptr<EventTrackerInterface> InputTracer::traceInboundEvent(const EventEntry& entry) {
- TracedEvent traced;
+ // This is a newly traced inbound event. Create a new state to track it and its derived events.
+ auto eventState = std::make_shared<EventState>(*this);
if (entry.type == EventEntry::Type::MOTION) {
const auto& motion = static_cast<const MotionEntry&>(entry);
- traced = createTracedEvent(motion);
+ eventState->events.emplace_back(createTracedEvent(motion));
} else if (entry.type == EventEntry::Type::KEY) {
const auto& key = static_cast<const KeyEntry&>(entry);
- traced = createTracedEvent(key);
+ eventState->events.emplace_back(createTracedEvent(key));
} else {
LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
}
- return std::make_unique<EventTrackerImpl>(*this, std::move(traced));
+ return std::make_unique<EventTrackerImpl>(std::move(eventState), /*isDerived=*/false);
}
void InputTracer::dispatchToTargetHint(const EventTrackerInterface& cookie,
const InputTarget& target) {
- auto& cookieState = getState(cookie);
- if (!cookieState) {
- LOG(FATAL) << "dispatchToTargetHint() should not be called after eventProcessingComplete()";
+ if (isDerivedCookie(cookie)) {
+ LOG(FATAL) << "Event target cannot be updated from a derived cookie.";
+ }
+ auto& eventState = getState(cookie);
+ if (eventState->isEventProcessingComplete) {
+ // TODO(b/210460522): Disallow adding new targets after eventProcessingComplete() is called.
+ return;
}
// TODO(b/210460522): Determine if the event is sensitive based on the target.
}
void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) {
- auto& cookieState = getState(cookie);
- if (!cookieState) {
+ if (isDerivedCookie(cookie)) {
+ LOG(FATAL) << "Event processing cannot be set from a derived cookie.";
+ }
+ auto& eventState = getState(cookie);
+ if (eventState->isEventProcessingComplete) {
LOG(FATAL) << "Traced event was already logged. "
"eventProcessingComplete() was likely called more than once.";
}
- std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); },
- [&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }},
- cookieState->event);
- cookieState.reset();
+ for (const auto& event : eventState->events) {
+ writeEventToBackend(event, *mBackend);
+ }
+ eventState->isEventProcessingComplete = true;
+}
+
+std::unique_ptr<EventTrackerInterface> InputTracer::traceDerivedEvent(
+ const EventEntry& entry, const EventTrackerInterface& originalEventCookie) {
+ // This is an event derived from an already-established event. Use the same state to track
+ // this event too.
+ auto eventState = getState(originalEventCookie);
+
+ if (entry.type == EventEntry::Type::MOTION) {
+ const auto& motion = static_cast<const MotionEntry&>(entry);
+ eventState->events.emplace_back(createTracedEvent(motion));
+ } else if (entry.type == EventEntry::Type::KEY) {
+ const auto& key = static_cast<const KeyEntry&>(entry);
+ eventState->events.emplace_back(createTracedEvent(key));
+ } else {
+ LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
+ }
+
+ if (eventState->isEventProcessingComplete) {
+ // It is possible for a derived event to be dispatched some time after the original event
+ // is dispatched, such as in the case of key fallback events. To account for these cases,
+ // derived events can be traced after the processing is complete for the original event.
+ writeEventToBackend(eventState->events.back(), *mBackend);
+ }
+ return std::make_unique<EventTrackerImpl>(std::move(eventState), /*isDerived=*/true);
}
void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry,
const EventTrackerInterface* cookie) {
const EventEntry& entry = *dispatchEntry.eventEntry;
+ // TODO(b/328618922): Remove resolved key repeats after making repeatCount non-mutable.
+ // The KeyEntry's repeatCount is mutable and can be modified after an event is initially traced,
+ // so we need to find the repeatCount at the time of dispatching to trace it accurately.
+ int32_t resolvedKeyRepeatCount = 0;
TracedEvent traced;
if (entry.type == EventEntry::Type::MOTION) {
@@ -114,6 +157,7 @@
traced = createTracedEvent(motion);
} else if (entry.type == EventEntry::Type::KEY) {
const auto& key = static_cast<const KeyEntry&>(entry);
+ resolvedKeyRepeatCount = key.repeatCount;
traced = createTracedEvent(key);
} else {
LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
@@ -121,9 +165,7 @@
if (!cookie) {
// This event was not tracked as an inbound event, so trace it now.
- std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); },
- [&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }},
- traced);
+ writeEventToBackend(traced, *mBackend);
}
// The vsyncId only has meaning if the event is targeting a window.
@@ -133,31 +175,32 @@
mBackend->traceWindowDispatch({std::move(traced), dispatchEntry.deliveryTime,
dispatchEntry.resolvedFlags, dispatchEntry.targetUid, vsyncId,
windowId, dispatchEntry.transform, dispatchEntry.rawTransform,
- /*hmac=*/{}});
+ /*hmac=*/{}, resolvedKeyRepeatCount});
}
-std::optional<InputTracer::EventState>& InputTracer::getState(const EventTrackerInterface& cookie) {
+std::shared_ptr<InputTracer::EventState>& InputTracer::getState(
+ const EventTrackerInterface& cookie) {
return static_cast<const EventTrackerImpl&>(cookie).mState;
}
-// --- InputTracer::EventTrackerImpl ---
+bool InputTracer::isDerivedCookie(const EventTrackerInterface& cookie) {
+ return static_cast<const EventTrackerImpl&>(cookie).mIsDerived;
+}
-InputTracer::EventTrackerImpl::EventTrackerImpl(InputTracer& tracer, TracedEvent&& event)
- : mTracer(tracer), mState(event) {}
+// --- InputTracer::EventState ---
-InputTracer::EventTrackerImpl::~EventTrackerImpl() {
- if (!mState) {
+InputTracer::EventState::~EventState() {
+ if (isEventProcessingComplete) {
// This event has already been written to the trace as expected.
return;
}
- // We're still holding on to the state, which means it hasn't yet been written to the trace.
- // Write it to the trace now.
+ // The event processing was never marked as complete, so do it now.
// TODO(b/210460522): Determine why/where the event is being destroyed before
// eventProcessingComplete() is called.
- std::visit(Visitor{[&](const TracedMotionEvent& e) { mTracer.mBackend->traceMotionEvent(e); },
- [&](const TracedKeyEvent& e) { mTracer.mBackend->traceKeyEvent(e); }},
- mState->event);
- mState.reset();
+ for (const auto& event : events) {
+ writeEventToBackend(event, *tracer.mBackend);
+ }
+ isEventProcessingComplete = true;
}
} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h
index c8b25c9..ccff30e 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.h
+++ b/services/inputflinger/dispatcher/trace/InputTracer.h
@@ -44,35 +44,43 @@
std::unique_ptr<EventTrackerInterface> traceInboundEvent(const EventEntry&) override;
void dispatchToTargetHint(const EventTrackerInterface&, const InputTarget&) override;
void eventProcessingComplete(const EventTrackerInterface&) override;
+ std::unique_ptr<EventTrackerInterface> traceDerivedEvent(const EventEntry&,
+ const EventTrackerInterface&) override;
void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) override;
private:
std::unique_ptr<InputTracingBackendInterface> mBackend;
- // The state of a tracked event.
+ // The state of a tracked event, shared across all events derived from the original event.
struct EventState {
- const TracedEvent event;
+ explicit inline EventState(InputTracer& tracer) : tracer(tracer){};
+ ~EventState();
+
+ InputTracer& tracer;
+ std::vector<const TracedEvent> events;
+ bool isEventProcessingComplete{false};
// TODO(b/210460522): Add additional args for tracking event sensitivity and
// dispatch target UIDs.
};
// Get the event state associated with a tracking cookie.
- std::optional<EventState>& getState(const EventTrackerInterface&);
+ std::shared_ptr<EventState>& getState(const EventTrackerInterface&);
+ bool isDerivedCookie(const EventTrackerInterface&);
// Implementation of the event tracker cookie. The cookie holds the event state directly for
// convenience to avoid the overhead of tracking the state separately in InputTracer.
class EventTrackerImpl : public EventTrackerInterface {
public:
- explicit EventTrackerImpl(InputTracer&, TracedEvent&& entry);
- virtual ~EventTrackerImpl() override;
+ inline EventTrackerImpl(const std::shared_ptr<EventState>& state, bool isDerivedEvent)
+ : mState(state), mIsDerived(isDerivedEvent) {}
+ EventTrackerImpl(const EventTrackerImpl&) = default;
private:
- InputTracer& mTracer;
- // This event tracker cookie will only hold the state as long as it has not been written
- // to the trace. The state is released when the event is written to the trace.
- mutable std::optional<EventState> mState;
+ mutable std::shared_ptr<EventState> mState;
+ const bool mIsDerived;
- friend std::optional<EventState>& InputTracer::getState(const EventTrackerInterface&);
+ friend std::shared_ptr<EventState>& InputTracer::getState(const EventTrackerInterface&);
+ friend bool InputTracer::isDerivedCookie(const EventTrackerInterface&);
};
};
diff --git a/services/inputflinger/dispatcher/trace/InputTracerInterface.h b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
index c6cd7de..daf716f 100644
--- a/services/inputflinger/dispatcher/trace/InputTracerInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
@@ -52,7 +52,6 @@
* to track the event's lifecycle inside InputDispatcher.
*/
virtual std::unique_ptr<EventTrackerInterface> traceInboundEvent(const EventEntry&) = 0;
-
/**
* Notify the tracer that the traced event will be sent to the given InputTarget.
* The tracer may change how the event is logged depending on the target. For example,
@@ -76,6 +75,19 @@
virtual void eventProcessingComplete(const EventTrackerInterface&) = 0;
/**
+ * Trace an input event that is derived from another event. This is used in cases where an event
+ * is modified from the original, such as when a touch is split across multiple windows, or
+ * when a HOVER_MOVE event is modified to be a HOVER_EXIT, etc. The original event's tracker
+ * must be provided, and a new EventTracker is returned that should be used to track the event's
+ * lifecycle.
+ *
+ * NOTE: The derived tracker cannot be used to change the targets of the original event, meaning
+ * it cannot be used with {@link #dispatchToTargetHint} or {@link eventProcessingComplete}.
+ */
+ virtual std::unique_ptr<EventTrackerInterface> traceDerivedEvent(
+ const EventEntry&, const EventTrackerInterface& originalEventTracker) = 0;
+
+ /**
* Trace an input event being successfully dispatched to a window. The dispatched event may
* be a previously traced inbound event, or it may be a synthesized event that has not been
* previously traced. For inbound events that were previously traced, the EventTracker cookie
diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
index b0eadfe..94a86b9 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
@@ -98,6 +98,7 @@
ui::Transform transform;
ui::Transform rawTransform;
std::array<uint8_t, 32> hmac;
+ int32_t resolvedKeyRepeatCount;
};
virtual void traceWindowDispatch(const WindowDispatchArgs&) = 0;
};
diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
index 8b47b55..462aedc 100644
--- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h
+++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
@@ -25,6 +25,9 @@
*
* This is the interface that PointerChoreographer uses to talk to Window Manager and other
* system components.
+ *
+ * NOTE: In general, the PointerChoreographer must not interact with the policy while
+ * holding any locks.
*/
class PointerChoreographerPolicyInterface {
public:
@@ -37,6 +40,9 @@
* for and runnable on the host, the PointerController implementation must be in a separate
* library, libinputservice, that has the additional dependencies. The PointerController
* will be mocked when testing PointerChoreographer.
+ *
+ * Since this is a factory method used to work around dependencies, it will not interact with
+ * other input components and may be called with the PointerChoreographer lock held.
*/
virtual std::shared_ptr<PointerControllerInterface> createPointerController(
PointerControllerInterface::ControllerType type) = 0;
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.cpp b/services/inputflinger/tests/FakeInputTracingBackend.cpp
index 4655ee8..08738e3 100644
--- a/services/inputflinger/tests/FakeInputTracingBackend.cpp
+++ b/services/inputflinger/tests/FakeInputTracingBackend.cpp
@@ -56,8 +56,8 @@
const std::array<uint8_t, 32>& hmac) {
KeyEvent traced;
traced.initialize(e.id, e.deviceId, e.source, e.displayId, hmac, e.action,
- dispatchArgs.resolvedFlags, e.keyCode, e.scanCode, e.metaState, e.repeatCount,
- e.downTime, e.eventTime);
+ dispatchArgs.resolvedFlags, e.keyCode, e.scanCode, e.metaState,
+ dispatchArgs.resolvedKeyRepeatCount, e.downTime, e.eventTime);
return traced;
}
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
index 2ff9c3c..cb8c3cb 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -20,6 +20,7 @@
#define ASSERT_FOCUS_CHANGE(_changes, _oldFocus, _newFocus) \
{ \
+ ASSERT_TRUE(_changes.has_value()); \
ASSERT_EQ(_oldFocus, _changes->oldFocus); \
ASSERT_EQ(_newFocus, _changes->newFocus); \
}
@@ -152,6 +153,38 @@
ASSERT_FOCUS_CHANGE(changes, /*from*/ invisibleWindowToken, /*to*/ nullptr);
}
+TEST(FocusResolverTest, FocusTransferToMirror) {
+ sp<IBinder> focusableWindowToken = sp<BBinder>::make();
+ auto window = sp<FakeWindowHandle>::make("Window", focusableWindowToken,
+ /*focusable=*/true, /*visible=*/true);
+ auto mirror = sp<FakeWindowHandle>::make("Mirror", focusableWindowToken,
+ /*focusable=*/true, /*visible=*/true);
+
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = focusableWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, {window, mirror});
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
+
+ // The mirror window now comes on top, and the focus does not change
+ changes = focusResolver.setInputWindows(request.displayId, {mirror, window});
+ ASSERT_FALSE(changes.has_value());
+
+ // The window now comes on top while the mirror is removed, and the focus does not change
+ changes = focusResolver.setInputWindows(request.displayId, {window});
+ ASSERT_FALSE(changes.has_value());
+
+ // The window is removed but the mirror is on top, and focus does not change
+ changes = focusResolver.setInputWindows(request.displayId, {mirror});
+ ASSERT_FALSE(changes.has_value());
+
+ // All windows removed
+ changes = focusResolver.setInputWindows(request.displayId, {});
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
+}
+
TEST(FocusResolverTest, SetInputWindows) {
sp<IBinder> focusableWindowToken = sp<BBinder>::make();
std::vector<sp<WindowInfoHandle>> windows;
@@ -169,6 +202,10 @@
focusResolver.setFocusedWindow(request, windows);
ASSERT_EQ(focusableWindowToken, changes->newFocus);
+ // When there are no changes to the window, focus does not change
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FALSE(changes.has_value());
+
// Window visibility changes and the window loses focus
window->setVisible(false);
changes = focusResolver.setInputWindows(request.displayId, windows);
@@ -380,18 +417,13 @@
ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
ASSERT_EQ(request.displayId, changes->displayId);
- // Start with a focused window
- window->setFocusable(true);
- changes = focusResolver.setInputWindows(request.displayId, windows);
- ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
-
// When a display is removed, all windows are removed from the display
// and our focused window loses focus
changes = focusResolver.setInputWindows(request.displayId, {});
ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
focusResolver.displayRemoved(request.displayId);
- // When a display is readded, the window does not get focus since the request was cleared.
+ // When a display is re-added, the window does not get focus since the request was cleared.
changes = focusResolver.setInputWindows(request.displayId, windows);
ASSERT_FALSE(changes);
}
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 118d928..bd54d24 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -340,8 +340,8 @@
binder::Status onSensorPrivacyChanged(int toggleType, int sensor,
bool enabled);
- // This callback is used for additional automotive-specific states for sensor privacy
- // such as AUTO_DRIVER_ASSISTANCE_APPS. The newly defined states will only be valid
+ // This callback is used for additional automotive-specific state for sensor privacy
+ // such as ENABLED_EXCEPT_ALLOWLISTED_APPS. The newly defined states will only be valid
// for camera privacy on automotive devices. onSensorPrivacyChanged() will still be
// invoked whenever the enabled status of a toggle changes.
binder::Status onSensorPrivacyStateChanged(int, int, int) {return binder::Status::ok();}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 46252e1..d771803 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -45,6 +45,7 @@
"android.hardware.power-ndk_shared",
"librenderengine_deps",
"libtimestats_deps",
+ "libsurfaceflinger_common_deps",
"surfaceflinger_defaults",
],
cflags: [
@@ -85,7 +86,6 @@
"libinput",
"libutils",
"libSurfaceFlingerProp",
- "server_configurable_flags",
],
static_libs: [
"libaidlcommonsupport",
@@ -98,10 +98,8 @@
"libscheduler",
"libserviceutils",
"libshaders",
- "libsurfaceflinger_common",
"libtimestats",
"libtonemap",
- "libsurfaceflingerflags",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 0b01c66..a52cc87 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -38,7 +38,6 @@
"libSurfaceFlingerProp",
"libui",
"libutils",
- "server_configurable_flags",
],
static_libs: [
"liblayers_proto",
@@ -90,24 +89,23 @@
cc_library {
name: "libcompositionengine",
- defaults: ["libcompositionengine_defaults"],
- static_libs: [
- "libsurfaceflinger_common",
- "libsurfaceflingerflags",
+ defaults: [
+ "libcompositionengine_defaults",
+ "libsurfaceflinger_common_deps",
],
srcs: [
":libcompositionengine_sources",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
- shared_libs: [
- "server_configurable_flags",
- ],
}
cc_library {
name: "libcompositionengine_mocks",
- defaults: ["libcompositionengine_defaults"],
+ defaults: [
+ "libcompositionengine_defaults",
+ "libsurfaceflinger_common_test_deps",
+ ],
srcs: [
"mock/CompositionEngine.cpp",
"mock/Display.cpp",
@@ -123,11 +121,6 @@
"libgtest",
"libgmock",
"libcompositionengine",
- "libsurfaceflinger_common_test",
- "libsurfaceflingerflags_test",
- ],
- shared_libs: [
- "server_configurable_flags",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
@@ -140,7 +133,10 @@
"frameworks/native/services/surfaceflinger/common/include",
"frameworks/native/services/surfaceflinger/tests/unittests",
],
- defaults: ["libcompositionengine_defaults"],
+ defaults: [
+ "libcompositionengine_defaults",
+ "libsurfaceflinger_common_test_deps",
+ ],
srcs: [
":libcompositionengine_sources",
"tests/planner/CachedSetTest.cpp",
@@ -166,14 +162,11 @@
"librenderengine_mocks",
"libgmock",
"libgtest",
- "libsurfaceflinger_common_test",
- "libsurfaceflingerflags_test",
],
shared_libs: [
// For some reason, libvulkan isn't picked up from librenderengine
// Probably ASAN related?
"libvulkan",
- "server_configurable_flags",
],
sanitize: {
hwaddress: true,
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index c888ccc..77e045d 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -377,8 +377,8 @@
constexpr bool kIsProtected = false;
if (const auto fenceResult =
- mFlinger.captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, buffer,
- kRegionSampling, kGrayscale, kIsProtected, nullptr)
+ mFlinger.captureScreenshot(std::move(renderAreaFuture), getLayerSnapshots, buffer,
+ kRegionSampling, kGrayscale, kIsProtected, nullptr)
.get();
fenceResult.ok()) {
fenceResult.value()->waitForever(LOG_TAG);
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index 16776cf..5455fdc 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -53,7 +53,10 @@
cc_test {
name: "libscheduler_test",
test_suites: ["device-tests"],
- defaults: ["libscheduler_defaults"],
+ defaults: [
+ "libscheduler_defaults",
+ "libsurfaceflinger_common_test_deps",
+ ],
srcs: [
"tests/FrameTargeterTest.cpp",
"tests/PresentLatencyTrackerTest.cpp",
@@ -63,9 +66,5 @@
"libgmock",
"libgtest",
"libscheduler",
- "libsurfaceflingerflags_test",
- ],
- shared_libs: [
- "server_configurable_flags",
],
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index ffd3463..ad59f1a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -834,13 +834,16 @@
const bool touchBoostForExplicitExact = [&] {
if (supportsAppFrameRateOverrideByContent()) {
// Enable touch boost if there are other layers besides exact
- return explicitExact + noVoteLayers != layers.size();
+ return explicitExact + noVoteLayers + explicitGteLayers != layers.size();
} else {
// Enable touch boost if there are no exact layers
return explicitExact == 0;
}
}();
+ const bool touchBoostForCategory =
+ explicitCategoryVoteLayers + noVoteLayers + explicitGteLayers != layers.size();
+
const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
using fps_approx_ops::operator<;
@@ -851,6 +854,7 @@
const bool hasInteraction = signals.touch || interactiveLayers > 0;
if (hasInteraction && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
+ touchBoostForCategory &&
scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) {
ALOGV("Touch Boost");
ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])",
@@ -1554,19 +1558,17 @@
case FrameRateCategory::High:
return FpsRange{90_Hz, 120_Hz};
case FrameRateCategory::Normal:
- return FpsRange{60_Hz, 90_Hz};
+ return FpsRange{60_Hz, 120_Hz};
case FrameRateCategory::Low:
- return FpsRange{30_Hz, 30_Hz};
+ return FpsRange{30_Hz, 120_Hz};
case FrameRateCategory::HighHint:
case FrameRateCategory::NoPreference:
case FrameRateCategory::Default:
LOG_ALWAYS_FATAL("Should not get fps range for frame rate category: %s",
ftl::enum_string(category).c_str());
- return FpsRange{0_Hz, 0_Hz};
default:
LOG_ALWAYS_FATAL("Invalid frame rate category for range: %s",
ftl::enum_string(category).c_str());
- return FpsRange{0_Hz, 0_Hz};
}
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 3f91682..d92edb8 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -565,7 +565,7 @@
}));
}
-void Scheduler::setRenderRate(PhysicalDisplayId id, Fps renderFrameRate) {
+void Scheduler::setRenderRate(PhysicalDisplayId id, Fps renderFrameRate, bool applyImmediately) {
std::scoped_lock lock(mDisplayLock);
ftl::FakeGuard guard(kMainThreadContext);
@@ -586,7 +586,7 @@
ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(),
to_string(mode.modePtr->getVsyncRate()).c_str());
- display.schedulePtr->getTracker().setRenderRate(renderFrameRate);
+ display.schedulePtr->getTracker().setRenderRate(renderFrameRate, applyImmediately);
}
Fps Scheduler::getNextFrameInterval(PhysicalDisplayId id,
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 09f75fd..494a91b 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -188,7 +188,7 @@
const VsyncConfiguration& getVsyncConfiguration() const { return *mVsyncConfiguration; }
// Sets the render rate for the scheduler to run at.
- void setRenderRate(PhysicalDisplayId, Fps);
+ void setRenderRate(PhysicalDisplayId, Fps, bool applyImmediately);
void enableHardwareVsync(PhysicalDisplayId) REQUIRES(kMainThreadContext);
void disableHardwareVsync(PhysicalDisplayId, bool disallow) REQUIRES(kMainThreadContext);
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 84ccf8e..6d6b70d 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -20,7 +20,7 @@
#include <android-base/stringprintf.h>
#include <ftl/concat.h>
-#include <utils/Trace.h>
+#include <gui/TraceUtils.h>
#include <log/log_main.h>
#include <scheduler/TimeKeeper.h>
@@ -44,6 +44,17 @@
TimePoint::fromNs(nextVsyncTime)};
}
+void traceEntry(const VSyncDispatchTimerQueueEntry& entry, nsecs_t now) {
+ if (!ATRACE_ENABLED() || !entry.wakeupTime().has_value() || !entry.targetVsync().has_value()) {
+ return;
+ }
+
+ ftl::Concat trace(ftl::truncated<5>(entry.name()), " alarm in ",
+ ns2us(*entry.wakeupTime() - now), "us; VSYNC in ",
+ ns2us(*entry.targetVsync() - now), "us");
+ ATRACE_FORMAT_INSTANT(trace.c_str());
+}
+
} // namespace
VSyncDispatch::~VSyncDispatch() = default;
@@ -87,6 +98,7 @@
ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
VSyncTracker& tracker, nsecs_t now) {
+ ATRACE_NAME("VSyncDispatchTimerQueueEntry::schedule");
auto nextVsyncTime =
tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync,
now + timing.workDuration +
@@ -98,6 +110,8 @@
mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
bool const wouldSkipAWakeup =
mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
+ ATRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(),
+ wouldSkipAVsyncTarget, wouldSkipAWakeup);
if (FlagManager::getInstance().dont_skip_on_early_ro()) {
if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
nextVsyncTime = mArmedInfo->mActualVsyncTime;
@@ -122,7 +136,7 @@
ScheduleResult VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(
VSyncTracker& tracker, nsecs_t now, VSyncDispatch::ScheduleTiming timing) {
mWorkloadUpdateInfo = timing;
- const auto armedInfo = update(tracker, now, timing, mArmedInfo);
+ const auto armedInfo = getArmedInfo(tracker, now, timing, mArmedInfo);
return {TimePoint::fromNs(armedInfo.mActualWakeupTime),
TimePoint::fromNs(armedInfo.mActualVsyncTime)};
}
@@ -140,11 +154,13 @@
bool const nextVsyncTooClose = mLastDispatchTime &&
(nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod;
if (alreadyDispatchedForVsync) {
+ ATRACE_FORMAT_INSTANT("alreadyDispatchedForVsync");
return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance,
*mLastDispatchTime);
}
if (nextVsyncTooClose) {
+ ATRACE_FORMAT_INSTANT("nextVsyncTooClose");
return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod,
*mLastDispatchTime + currentPeriod);
}
@@ -152,9 +168,11 @@
return nextVsyncTime;
}
-auto VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now,
- VSyncDispatch::ScheduleTiming timing,
- std::optional<ArmingInfo> armedInfo) const -> ArmingInfo {
+auto VSyncDispatchTimerQueueEntry::getArmedInfo(VSyncTracker& tracker, nsecs_t now,
+ VSyncDispatch::ScheduleTiming timing,
+ std::optional<ArmingInfo> armedInfo) const
+ -> ArmingInfo {
+ ATRACE_NAME("VSyncDispatchTimerQueueEntry::getArmedInfo");
const auto earliestReadyBy = now + timing.workDuration + timing.readyDuration;
const auto earliestVsync = std::max(earliestReadyBy, timing.lastVsync);
@@ -165,29 +183,39 @@
const auto nextReadyTime = nextVsyncTime - timing.readyDuration;
const auto nextWakeupTime = nextReadyTime - timing.workDuration;
- bool const wouldSkipAVsyncTarget =
- armedInfo && (nextVsyncTime > (armedInfo->mActualVsyncTime + mMinVsyncDistance));
- bool const wouldSkipAWakeup =
- armedInfo && (nextWakeupTime > (armedInfo->mActualWakeupTime + mMinVsyncDistance));
- if (FlagManager::getInstance().dont_skip_on_early_ro() &&
- (wouldSkipAVsyncTarget || wouldSkipAWakeup)) {
- return *armedInfo;
+ if (FlagManager::getInstance().dont_skip_on_early_ro()) {
+ bool const wouldSkipAVsyncTarget =
+ armedInfo && (nextVsyncTime > (armedInfo->mActualVsyncTime + mMinVsyncDistance));
+ bool const wouldSkipAWakeup =
+ armedInfo && (nextWakeupTime > (armedInfo->mActualWakeupTime + mMinVsyncDistance));
+ ATRACE_FORMAT_INSTANT("%s: wouldSkipAVsyncTarget=%d wouldSkipAWakeup=%d", mName.c_str(),
+ wouldSkipAVsyncTarget, wouldSkipAWakeup);
+ if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
+ return *armedInfo;
+ }
}
return ArmingInfo{nextWakeupTime, nextVsyncTime, nextReadyTime};
}
void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
+ ATRACE_NAME("VSyncDispatchTimerQueueEntry::update");
if (!mArmedInfo && !mWorkloadUpdateInfo) {
return;
}
if (mWorkloadUpdateInfo) {
+ const auto workDelta = mWorkloadUpdateInfo->workDuration - mScheduleTiming.workDuration;
+ const auto readyDelta = mWorkloadUpdateInfo->readyDuration - mScheduleTiming.readyDuration;
+ const auto lastVsyncDelta = mWorkloadUpdateInfo->lastVsync - mScheduleTiming.lastVsync;
+ ATRACE_FORMAT_INSTANT("Workload updated workDelta=%" PRId64 " readyDelta=%" PRId64
+ " lastVsyncDelta=%" PRId64,
+ workDelta, readyDelta, lastVsyncDelta);
mScheduleTiming = *mWorkloadUpdateInfo;
mWorkloadUpdateInfo.reset();
}
- mArmedInfo = update(tracker, now, mScheduleTiming, mArmedInfo);
+ mArmedInfo = getArmedInfo(tracker, now, mScheduleTiming, mArmedInfo);
}
void VSyncDispatchTimerQueueEntry::disarm() {
@@ -282,6 +310,7 @@
void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
nsecs_t now, CallbackMap::const_iterator skipUpdateIt) {
+ ATRACE_CALL();
std::optional<nsecs_t> min;
std::optional<nsecs_t> targetVsync;
std::optional<std::string_view> nextWakeupName;
@@ -294,7 +323,10 @@
if (it != skipUpdateIt) {
callback->update(*mTracker, now);
}
- auto const wakeupTime = *callback->wakeupTime();
+
+ traceEntry(*callback, now);
+
+ const auto wakeupTime = *callback->wakeupTime();
if (!min || *min > wakeupTime) {
nextWakeupName = callback->name();
min = wakeupTime;
@@ -303,11 +335,6 @@
}
if (min && min < mIntendedWakeupTime) {
- if (ATRACE_ENABLED() && nextWakeupName && targetVsync) {
- ftl::Concat trace(ftl::truncated<5>(*nextWakeupName), " alarm in ", ns2us(*min - now),
- "us; VSYNC in ", ns2us(*targetVsync - now), "us");
- ATRACE_NAME(trace.c_str());
- }
setTimer(*min, now);
} else {
ATRACE_NAME("cancel timer");
@@ -316,6 +343,7 @@
}
void VSyncDispatchTimerQueue::timerCallback() {
+ ATRACE_CALL();
struct Invocation {
std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
nsecs_t vsyncTimestamp;
@@ -338,8 +366,9 @@
continue;
}
- auto const readyTime = callback->readyTime();
+ traceEntry(*callback, now);
+ auto const readyTime = callback->readyTime();
auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
callback->executing();
@@ -353,6 +382,8 @@
}
for (auto const& invocation : invocations) {
+ ftl::Concat trace(ftl::truncated<5>(invocation.callback->name()));
+ ATRACE_FORMAT("%s: %s", __func__, trace.c_str());
invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
invocation.deadlineTimestamp);
}
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 252c09c..e4ddc03 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -91,8 +91,8 @@
};
nsecs_t adjustVsyncIfNeeded(VSyncTracker& tracker, nsecs_t nextVsyncTime) const;
- ArmingInfo update(VSyncTracker&, nsecs_t now, VSyncDispatch::ScheduleTiming,
- std::optional<ArmingInfo>) const;
+ ArmingInfo getArmedInfo(VSyncTracker&, nsecs_t now, VSyncDispatch::ScheduleTiming,
+ std::optional<ArmingInfo>) const;
const std::string mName;
const VSyncDispatch::Callback mCallback;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 8697696..db1930d 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -45,16 +45,28 @@
static auto constexpr kMaxPercent = 100u;
+namespace {
+int numVsyncsPerFrame(const ftl::NonNull<DisplayModePtr>& displayModePtr) {
+ const auto idealPeakRefreshPeriod = displayModePtr->getPeakFps().getPeriodNsecs();
+ const auto idealRefreshPeriod = displayModePtr->getVsyncRate().getPeriodNsecs();
+ return static_cast<int>(std::round(static_cast<float>(idealPeakRefreshPeriod) /
+ static_cast<float>(idealRefreshPeriod)));
+}
+} // namespace
+
VSyncPredictor::~VSyncPredictor() = default;
-VSyncPredictor::VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize,
- size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent)
- : mId(modePtr->getPhysicalDisplayId()),
+VSyncPredictor::VSyncPredictor(std::unique_ptr<Clock> clock, ftl::NonNull<DisplayModePtr> modePtr,
+ size_t historySize, size_t minimumSamplesForPrediction,
+ uint32_t outlierTolerancePercent)
+ : mClock(std::move(clock)),
+ mId(modePtr->getPhysicalDisplayId()),
mTraceOn(property_get_bool("debug.sf.vsp_trace", false)),
kHistorySize(historySize),
kMinimumSamplesForPrediction(minimumSamplesForPrediction),
kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
- mDisplayModePtr(modePtr) {
+ mDisplayModePtr(modePtr),
+ mNumVsyncsForFrame(numVsyncsPerFrame(mDisplayModePtr)) {
resetModel();
}
@@ -118,11 +130,8 @@
}
Period VSyncPredictor::minFramePeriodLocked() const {
- const auto idealPeakRefreshPeriod = mDisplayModePtr->getPeakFps().getPeriodNsecs();
- const auto numPeriods = static_cast<int>(std::round(static_cast<float>(idealPeakRefreshPeriod) /
- static_cast<float>(idealPeriod())));
const auto slope = mRateMap.find(idealPeriod())->second.slope;
- return Period::fromNs(slope * numPeriods);
+ return Period::fromNs(slope * mNumVsyncsForFrame);
}
bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
@@ -147,7 +156,7 @@
mKnownTimestamp = timestamp;
}
ATRACE_FORMAT_INSTANT("timestamp rejected. mKnownTimestamp was %.2fms ago",
- (systemTime() - *mKnownTimestamp) / 1e6f);
+ (mClock->now() - *mKnownTimestamp) / 1e6f);
return false;
}
@@ -250,17 +259,6 @@
return true;
}
-auto VSyncPredictor::getVsyncSequenceLocked(nsecs_t timestamp) const -> VsyncSequence {
- const auto vsync = snapToVsync(timestamp);
- if (!mLastVsyncSequence) return {vsync, 0};
-
- const auto [slope, _] = getVSyncPredictionModelLocked();
- const auto [lastVsyncTime, lastVsyncSequence] = *mLastVsyncSequence;
- const auto vsyncSequence = lastVsyncSequence +
- static_cast<int64_t>(std::round((vsync - lastVsyncTime) / static_cast<float>(slope)));
- return {vsync, vsyncSequence};
-}
-
nsecs_t VSyncPredictor::snapToVsync(nsecs_t timePoint) const {
auto const [slope, intercept] = getVSyncPredictionModelLocked();
@@ -298,51 +296,45 @@
}
nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
- std::optional<nsecs_t> lastVsyncOpt) const {
+ std::optional<nsecs_t> lastVsyncOpt) {
ATRACE_CALL();
std::lock_guard lock(mMutex);
- const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
- const auto threshold = currentPeriod / 2;
- const auto minFramePeriod = minFramePeriodLocked().ns();
- const auto lastFrameMissed =
- lastVsyncOpt && std::abs(*lastVsyncOpt - mLastMissedVsync.ns()) < threshold;
- const nsecs_t baseTime =
- FlagManager::getInstance().vrr_config() && !lastFrameMissed && lastVsyncOpt
- ? std::max(timePoint, *lastVsyncOpt + minFramePeriod - threshold)
- : timePoint;
- return snapToVsyncAlignedWithRenderRate(baseTime);
-}
-nsecs_t VSyncPredictor::snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const {
- // update the mLastVsyncSequence for reference point
- mLastVsyncSequence = getVsyncSequenceLocked(timePoint);
+ const auto now = TimePoint::fromNs(mClock->now());
+ purgeTimelines(now);
- const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int {
- if (!mRenderRateOpt) return 0;
- const auto divisor =
- RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()),
- *mRenderRateOpt);
- if (divisor <= 1) return 0;
-
- int mod = mLastVsyncSequence->seq % divisor;
- if (mod == 0) return 0;
-
- // This is actually a bug fix, but guarded with vrr_config since we found it with this
- // config
- if (FlagManager::getInstance().vrr_config()) {
- if (mod < 0) mod += divisor;
- }
-
- return divisor - mod;
- }();
-
- if (renderRatePhase == 0) {
- return mLastVsyncSequence->vsyncTime;
+ if (lastVsyncOpt && *lastVsyncOpt > timePoint) {
+ timePoint = *lastVsyncOpt;
}
- auto const [slope, intercept] = getVSyncPredictionModelLocked();
- const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase;
- return snapToVsync(approximateNextVsync - slope / 2);
+ const auto model = getVSyncPredictionModelLocked();
+ const auto threshold = model.slope / 2;
+ std::optional<Period> minFramePeriodOpt;
+
+ if (mNumVsyncsForFrame > 1) {
+ minFramePeriodOpt = minFramePeriodLocked();
+ }
+
+ std::optional<TimePoint> vsyncOpt;
+ for (auto& timeline : mTimelines) {
+ vsyncOpt = timeline.nextAnticipatedVSyncTimeFrom(model, minFramePeriodOpt,
+ snapToVsync(timePoint), mMissedVsync,
+ lastVsyncOpt ? snapToVsync(*lastVsyncOpt -
+ threshold)
+ : lastVsyncOpt);
+ if (vsyncOpt) {
+ break;
+ }
+ }
+ LOG_ALWAYS_FATAL_IF(!vsyncOpt);
+
+ if (*vsyncOpt > mLastCommittedVsync) {
+ mLastCommittedVsync = *vsyncOpt;
+ ATRACE_FORMAT_INSTANT("mLastCommittedVsync in %.2fms",
+ float(mLastCommittedVsync.ns() - mClock->now()) / 1e6f);
+ }
+
+ return vsyncOpt->ns();
}
/*
@@ -353,39 +345,56 @@
* isVSyncInPhase(33.3, 30) = false
* isVSyncInPhase(50.0, 30) = true
*/
-bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const {
- std::lock_guard lock(mMutex);
- const auto divisor =
- RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()),
- frameRate);
- return isVSyncInPhaseLocked(timePoint, static_cast<unsigned>(divisor));
-}
-
-bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const {
- const TimePoint now = TimePoint::now();
- const auto getTimePointIn = [](TimePoint now, nsecs_t timePoint) -> float {
- return ticks<std::milli, float>(TimePoint::fromNs(timePoint) - now);
- };
- ATRACE_FORMAT("%s timePoint in: %.2f divisor: %zu", __func__, getTimePointIn(now, timePoint),
- divisor);
-
- if (divisor <= 1 || timePoint == 0) {
+bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) {
+ if (timePoint == 0) {
return true;
}
- const nsecs_t period = mRateMap[idealPeriod()].slope;
+ std::lock_guard lock(mMutex);
+ const auto model = getVSyncPredictionModelLocked();
+ const nsecs_t period = model.slope;
const nsecs_t justBeforeTimePoint = timePoint - period / 2;
- const auto vsyncSequence = getVsyncSequenceLocked(justBeforeTimePoint);
- ATRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64,
- getTimePointIn(now, vsyncSequence.vsyncTime), vsyncSequence.seq);
- return vsyncSequence.seq % divisor == 0;
+ const auto now = TimePoint::fromNs(mClock->now());
+ const auto vsync = snapToVsync(justBeforeTimePoint);
+
+ purgeTimelines(now);
+
+ for (auto& timeline : mTimelines) {
+ if (timeline.validUntil() && timeline.validUntil()->ns() > vsync) {
+ return timeline.isVSyncInPhase(model, vsync, frameRate);
+ }
+ }
+
+ // The last timeline should always be valid
+ return mTimelines.back().isVSyncInPhase(model, vsync, frameRate);
}
-void VSyncPredictor::setRenderRate(Fps renderRate) {
+void VSyncPredictor::setRenderRate(Fps renderRate, bool applyImmediately) {
ATRACE_FORMAT("%s %s", __func__, to_string(renderRate).c_str());
ALOGV("%s %s: RenderRate %s ", __func__, to_string(mId).c_str(), to_string(renderRate).c_str());
std::lock_guard lock(mMutex);
+ const auto prevRenderRate = mRenderRateOpt;
mRenderRateOpt = renderRate;
+ const auto renderPeriodDelta =
+ prevRenderRate ? prevRenderRate->getPeriodNsecs() - renderRate.getPeriodNsecs() : 0;
+ const bool newRenderRateIsHigher = renderPeriodDelta > renderRate.getPeriodNsecs() &&
+ mLastCommittedVsync.ns() - mClock->now() > 2 * renderRate.getPeriodNsecs();
+ if (applyImmediately) {
+ while (mTimelines.size() > 1) {
+ mTimelines.pop_front();
+ }
+
+ mTimelines.front().setRenderRate(renderRate);
+ } else if (newRenderRateIsHigher) {
+ mTimelines.clear();
+ mLastCommittedVsync = TimePoint::fromNs(0);
+
+ } else {
+ mTimelines.back().freeze(
+ TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2));
+ }
+ mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, renderRate);
+ purgeTimelines(TimePoint::fromNs(mClock->now()));
}
void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) {
@@ -401,6 +410,7 @@
std::lock_guard lock(mMutex);
mDisplayModePtr = modePtr;
+ mNumVsyncsForFrame = numVsyncsPerFrame(mDisplayModePtr);
traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs());
static constexpr size_t kSizeLimit = 30;
@@ -415,8 +425,14 @@
clearTimestamps();
}
-void VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
- TimePoint lastConfirmedPresentTime) {
+Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
+ TimePoint lastConfirmedPresentTime) {
+ ATRACE_CALL();
+
+ if (mNumVsyncsForFrame <= 1) {
+ return 0ns;
+ }
+
const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
const auto threshold = currentPeriod / 2;
const auto minFramePeriod = minFramePeriodLocked().ns();
@@ -442,17 +458,20 @@
if (!mPastExpectedPresentTimes.empty()) {
const auto phase = Duration(mPastExpectedPresentTimes.back() - expectedPresentTime);
if (phase > 0ns) {
- if (mLastVsyncSequence) {
- mLastVsyncSequence->vsyncTime += phase.ns();
+ for (auto& timeline : mTimelines) {
+ timeline.shiftVsyncSequence(phase);
}
mPastExpectedPresentTimes.clear();
+ return phase;
}
}
+
+ return 0ns;
}
void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime,
TimePoint lastConfirmedPresentTime) {
- ATRACE_CALL();
+ ATRACE_NAME("VSyncPredictor::onFrameBegin");
std::lock_guard lock(mMutex);
if (!mDisplayModePtr->getVrrConfig()) return;
@@ -482,11 +501,14 @@
}
}
- ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
+ const auto phase = ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
+ if (phase > 0ns) {
+ mMissedVsync = {expectedPresentTime, minFramePeriodLocked()};
+ }
}
void VSyncPredictor::onFrameMissed(TimePoint expectedPresentTime) {
- ATRACE_CALL();
+ ATRACE_NAME("VSyncPredictor::onFrameMissed");
std::lock_guard lock(mMutex);
if (!mDisplayModePtr->getVrrConfig()) return;
@@ -496,14 +518,15 @@
const auto lastConfirmedPresentTime =
TimePoint::fromNs(expectedPresentTime.ns() + currentPeriod);
- ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
- mLastMissedVsync = expectedPresentTime;
+ const auto phase = ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime);
+ if (phase > 0ns) {
+ mMissedVsync = {expectedPresentTime, Duration::fromNs(0)};
+ }
}
VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
std::lock_guard lock(mMutex);
- const auto model = VSyncPredictor::getVSyncPredictionModelLocked();
- return {model.slope, model.intercept};
+ return VSyncPredictor::getVSyncPredictionModelLocked();
}
VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const {
@@ -524,6 +547,11 @@
mTimestamps.clear();
mLastTimestampIndex = 0;
}
+
+ mTimelines.clear();
+ mLastCommittedVsync = TimePoint::fromNs(0);
+ mIdealPeriod = Period::fromNs(idealPeriod());
+ mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
}
bool VSyncPredictor::needsMoreSamples() const {
@@ -547,6 +575,171 @@
period / 1e6f, periodInterceptTuple.slope / 1e6f,
periodInterceptTuple.intercept);
}
+ StringAppendF(&result, "\tmTimelines.size()=%zu\n", mTimelines.size());
+}
+
+void VSyncPredictor::purgeTimelines(android::TimePoint now) {
+ const auto kEnoughFramesToBreakPhase = 5;
+ if (mRenderRateOpt &&
+ mLastCommittedVsync.ns() + mRenderRateOpt->getPeriodNsecs() * kEnoughFramesToBreakPhase <
+ mClock->now()) {
+ mTimelines.clear();
+ mLastCommittedVsync = TimePoint::fromNs(0);
+ mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
+ return;
+ }
+
+ while (mTimelines.size() > 1) {
+ const auto validUntilOpt = mTimelines.front().validUntil();
+ if (validUntilOpt && *validUntilOpt < now) {
+ mTimelines.pop_front();
+ } else {
+ break;
+ }
+ }
+ LOG_ALWAYS_FATAL_IF(mTimelines.empty());
+ LOG_ALWAYS_FATAL_IF(mTimelines.back().validUntil().has_value());
+}
+
+auto VSyncPredictor::VsyncTimeline::makeVsyncSequence(TimePoint knownVsync)
+ -> std::optional<VsyncSequence> {
+ if (knownVsync.ns() == 0) return std::nullopt;
+ return std::make_optional<VsyncSequence>({knownVsync.ns(), 0});
+}
+
+VSyncPredictor::VsyncTimeline::VsyncTimeline(TimePoint knownVsync, Period idealPeriod,
+ std::optional<Fps> renderRateOpt)
+ : mIdealPeriod(idealPeriod),
+ mRenderRateOpt(renderRateOpt),
+ mLastVsyncSequence(makeVsyncSequence(knownVsync)) {}
+
+void VSyncPredictor::VsyncTimeline::freeze(TimePoint lastVsync) {
+ LOG_ALWAYS_FATAL_IF(mValidUntil.has_value());
+ ATRACE_FORMAT_INSTANT("renderRate %s valid for %.2f",
+ mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA",
+ float(lastVsync.ns() - TimePoint::now().ns()) / 1e6f);
+ mValidUntil = lastVsync;
+}
+
+std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTimeFrom(
+ Model model, std::optional<Period> minFramePeriodOpt, nsecs_t vsync,
+ MissedVsync missedVsync, std::optional<nsecs_t> lastVsyncOpt) {
+ ATRACE_FORMAT("renderRate %s", mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA");
+
+ nsecs_t vsyncTime = snapToVsyncAlignedWithRenderRate(model, vsync);
+ const auto threshold = model.slope / 2;
+ const auto lastFrameMissed =
+ lastVsyncOpt && std::abs(*lastVsyncOpt - missedVsync.vsync.ns()) < threshold;
+ nsecs_t vsyncFixupTime = 0;
+ if (FlagManager::getInstance().vrr_config() && lastFrameMissed) {
+ // If the last frame missed is the last vsync, we already shifted the timeline. Depends on
+ // whether we skipped the frame (onFrameMissed) or not (onFrameBegin) we apply a different
+ // fixup. There is no need to to shift the vsync timeline again.
+ vsyncTime += missedVsync.fixup.ns();
+ ATRACE_FORMAT_INSTANT("lastFrameMissed");
+ } else if (minFramePeriodOpt) {
+ if (FlagManager::getInstance().vrr_config() && lastVsyncOpt) {
+ // lastVsyncOpt is based on the old timeline before we shifted it. we should correct it
+ // first before trying to use it.
+ if (mLastVsyncSequence->seq > 0) {
+ lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt);
+ }
+ const auto vsyncDiff = vsyncTime - *lastVsyncOpt;
+ if (vsyncDiff <= minFramePeriodOpt->ns() - threshold) {
+ vsyncFixupTime = *lastVsyncOpt + minFramePeriodOpt->ns() - vsyncTime;
+ ATRACE_FORMAT_INSTANT("minFramePeriod violation. next in %.2f which is %.2f "
+ "from "
+ "prev. "
+ "adjust by %.2f",
+ static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f,
+ static_cast<float>(vsyncTime - *lastVsyncOpt) / 1e6f,
+ static_cast<float>(vsyncFixupTime) / 1e6f);
+ }
+ }
+ vsyncTime += vsyncFixupTime;
+ }
+
+ ATRACE_FORMAT_INSTANT("vsync in %.2fms", float(vsyncTime - TimePoint::now().ns()) / 1e6f);
+ if (mValidUntil && vsyncTime > mValidUntil->ns()) {
+ ATRACE_FORMAT_INSTANT("no longer valid for vsync in %.2f",
+ static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f);
+ return std::nullopt;
+ }
+
+ // If we needed a fixup, it means that we changed the render rate and the chosen vsync would
+ // cross minFramePeriod. In that case we need to shift the entire vsync timeline.
+ if (vsyncFixupTime > 0) {
+ shiftVsyncSequence(Duration::fromNs(vsyncFixupTime));
+ }
+
+ return TimePoint::fromNs(vsyncTime);
+}
+
+auto VSyncPredictor::VsyncTimeline::getVsyncSequenceLocked(Model model, nsecs_t vsync)
+ -> VsyncSequence {
+ if (!mLastVsyncSequence) return {vsync, 0};
+
+ const auto [lastVsyncTime, lastVsyncSequence] = *mLastVsyncSequence;
+ const auto vsyncSequence = lastVsyncSequence +
+ static_cast<int64_t>(std::round((vsync - lastVsyncTime) /
+ static_cast<float>(model.slope)));
+ return {vsync, vsyncSequence};
+}
+
+nsecs_t VSyncPredictor::VsyncTimeline::snapToVsyncAlignedWithRenderRate(Model model,
+ nsecs_t vsync) {
+ // update the mLastVsyncSequence for reference point
+ mLastVsyncSequence = getVsyncSequenceLocked(model, vsync);
+
+ const auto renderRatePhase = [&]() -> int {
+ if (!mRenderRateOpt) return 0;
+ const auto divisor =
+ RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod.ns()),
+ *mRenderRateOpt);
+ if (divisor <= 1) return 0;
+
+ int mod = mLastVsyncSequence->seq % divisor;
+ if (mod == 0) return 0;
+
+ // This is actually a bug fix, but guarded with vrr_config since we found it with this
+ // config
+ if (FlagManager::getInstance().vrr_config()) {
+ if (mod < 0) mod += divisor;
+ }
+
+ return divisor - mod;
+ }();
+
+ if (renderRatePhase == 0) {
+ return mLastVsyncSequence->vsyncTime;
+ }
+
+ return mLastVsyncSequence->vsyncTime + model.slope * renderRatePhase;
+}
+
+bool VSyncPredictor::VsyncTimeline::isVSyncInPhase(Model model, nsecs_t vsync, Fps frameRate) {
+ const auto getVsyncIn = [](TimePoint now, nsecs_t timePoint) -> float {
+ return ticks<std::milli, float>(TimePoint::fromNs(timePoint) - now);
+ };
+
+ Fps displayFps = mRenderRateOpt ? *mRenderRateOpt : Fps::fromPeriodNsecs(mIdealPeriod.ns());
+ const auto divisor = RefreshRateSelector::getFrameRateDivisor(displayFps, frameRate);
+ const auto now = TimePoint::now();
+
+ if (divisor <= 1) {
+ return true;
+ }
+ const auto vsyncSequence = getVsyncSequenceLocked(model, vsync);
+ ATRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64 " divisor: %zu",
+ getVsyncIn(now, vsyncSequence.vsyncTime), vsyncSequence.seq, divisor);
+ return vsyncSequence.seq % divisor == 0;
+}
+
+void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase) {
+ if (mLastVsyncSequence) {
+ ATRACE_FORMAT_INSTANT("adjusting vsync by %.2f", static_cast<float>(phase.ns()) / 1e6f);
+ mLastVsyncSequence->vsyncTime += phase.ns();
+ }
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 8fd7e60..3ed1d41 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -22,6 +22,7 @@
#include <vector>
#include <android-base/thread_annotations.h>
+#include <scheduler/TimeKeeper.h>
#include <ui/DisplayId.h>
#include "VSyncTracker.h"
@@ -31,6 +32,7 @@
class VSyncPredictor : public VSyncTracker {
public:
/*
+ * \param [in] Clock The clock abstraction. Useful for unit tests.
* \param [in] PhysicalDisplayid The display this corresponds to.
* \param [in] modePtr The initial display mode
* \param [in] historySize The internal amount of entries to store in the model.
@@ -38,13 +40,13 @@
* predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter
* samples that fall outlierTolerancePercent from an anticipated vsync event.
*/
- VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize,
+ VSyncPredictor(std::unique_ptr<Clock>, ftl::NonNull<DisplayModePtr> modePtr, size_t historySize,
size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent);
~VSyncPredictor();
bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
- std::optional<nsecs_t> lastVsyncOpt = {}) const final
+ std::optional<nsecs_t> lastVsyncOpt = {}) final
EXCLUDES(mMutex);
nsecs_t currentPeriod() const final EXCLUDES(mMutex);
Period minFramePeriod() const final EXCLUDES(mMutex);
@@ -62,11 +64,11 @@
VSyncPredictor::Model getVSyncPredictionModel() const EXCLUDES(mMutex);
- bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex);
+ bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) final EXCLUDES(mMutex);
void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final EXCLUDES(mMutex);
- void setRenderRate(Fps) final EXCLUDES(mMutex);
+ void setRenderRate(Fps, bool applyImmediately) final EXCLUDES(mMutex);
void onFrameBegin(TimePoint expectedPresentTime, TimePoint lastConfirmedPresentTime) final
EXCLUDES(mMutex);
@@ -75,10 +77,44 @@
void dump(std::string& result) const final EXCLUDES(mMutex);
private:
+ struct VsyncSequence {
+ nsecs_t vsyncTime;
+ int64_t seq;
+ };
+
+ struct MissedVsync {
+ TimePoint vsync;
+ Duration fixup = Duration::fromNs(0);
+ };
+
+ class VsyncTimeline {
+ public:
+ VsyncTimeline(TimePoint knownVsync, Period idealPeriod, std::optional<Fps> renderRateOpt);
+ std::optional<TimePoint> nextAnticipatedVSyncTimeFrom(
+ Model model, std::optional<Period> minFramePeriodOpt, nsecs_t vsyncTime,
+ MissedVsync lastMissedVsync, std::optional<nsecs_t> lastVsyncOpt = {});
+ void freeze(TimePoint lastVsync);
+ std::optional<TimePoint> validUntil() const { return mValidUntil; }
+ bool isVSyncInPhase(Model, nsecs_t vsync, Fps frameRate);
+ void shiftVsyncSequence(Duration phase);
+ void setRenderRate(Fps renderRate) { mRenderRateOpt = renderRate; }
+
+ private:
+ nsecs_t snapToVsyncAlignedWithRenderRate(Model model, nsecs_t vsync);
+ VsyncSequence getVsyncSequenceLocked(Model, nsecs_t vsync);
+ std::optional<VsyncSequence> makeVsyncSequence(TimePoint knownVsync);
+
+ const Period mIdealPeriod = Duration::fromNs(0);
+ std::optional<Fps> mRenderRateOpt;
+ std::optional<TimePoint> mValidUntil;
+ std::optional<VsyncSequence> mLastVsyncSequence;
+ };
+
VSyncPredictor(VSyncPredictor const&) = delete;
VSyncPredictor& operator=(VSyncPredictor const&) = delete;
void clearTimestamps() REQUIRES(mMutex);
+ const std::unique_ptr<Clock> mClock;
const PhysicalDisplayId mId;
inline void traceInt64If(const char* name, int64_t value) const;
@@ -88,16 +124,10 @@
bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
Model getVSyncPredictionModelLocked() const REQUIRES(mMutex);
nsecs_t snapToVsync(nsecs_t timePoint) const REQUIRES(mMutex);
- nsecs_t snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const REQUIRES(mMutex);
- bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex);
Period minFramePeriodLocked() const REQUIRES(mMutex);
- void ensureMinFrameDurationIsKept(TimePoint, TimePoint) REQUIRES(mMutex);
+ Duration ensureMinFrameDurationIsKept(TimePoint, TimePoint) REQUIRES(mMutex);
+ void purgeTimelines(android::TimePoint now) REQUIRES(mMutex);
- struct VsyncSequence {
- nsecs_t vsyncTime;
- int64_t seq;
- };
- VsyncSequence getVsyncSequenceLocked(nsecs_t timestamp) const REQUIRES(mMutex);
nsecs_t idealPeriod() const REQUIRES(mMutex);
bool const mTraceOn;
@@ -115,13 +145,16 @@
std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
ftl::NonNull<DisplayModePtr> mDisplayModePtr GUARDED_BY(mMutex);
- std::optional<Fps> mRenderRateOpt GUARDED_BY(mMutex);
-
- mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex);
+ int mNumVsyncsForFrame GUARDED_BY(mMutex);
std::deque<TimePoint> mPastExpectedPresentTimes GUARDED_BY(mMutex);
- TimePoint mLastMissedVsync GUARDED_BY(mMutex);
+ MissedVsync mMissedVsync GUARDED_BY(mMutex);
+
+ std::deque<VsyncTimeline> mTimelines GUARDED_BY(mMutex);
+ TimePoint mLastCommittedVsync GUARDED_BY(mMutex) = TimePoint::fromNs(0);
+ Period mIdealPeriod GUARDED_BY(mMutex) = Duration::fromNs(0);
+ std::optional<Fps> mRenderRateOpt GUARDED_BY(mMutex);
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 37bd4b4..8787cdb 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -56,8 +56,8 @@
* and avoid crossing the minimal frame period of a VRR display.
* \return A prediction of the timestamp of a vsync event.
*/
- virtual nsecs_t nextAnticipatedVSyncTimeFrom(
- nsecs_t timePoint, std::optional<nsecs_t> lastVsyncOpt = {}) const = 0;
+ virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
+ std::optional<nsecs_t> lastVsyncOpt = {}) = 0;
/*
* The current period of the vsync signal.
@@ -82,7 +82,7 @@
* \param [in] timePoint A vsync timestamp
* \param [in] frameRate The frame rate to check for
*/
- virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0;
+ virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) = 0;
/*
* Sets the active mode of the display which includes the vsync period and other VRR attributes.
@@ -102,8 +102,10 @@
* when a display is running at 120Hz but the render frame rate is 60Hz.
*
* \param [in] Fps The render rate the tracker should operate at.
+ * \param [in] applyImmediately Whether to apply the new render rate immediately regardless of
+ * already committed vsyncs.
*/
- virtual void setRenderRate(Fps) = 0;
+ virtual void setRenderRate(Fps, bool applyImmediately) = 0;
virtual void onFrameBegin(TimePoint expectedPresentTime,
TimePoint lastConfirmedPresentTime) = 0;
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index 001938c..2fa3318 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -120,8 +120,8 @@
constexpr size_t kMinSamplesForPrediction = 6;
constexpr uint32_t kDiscardOutlierPercent = 20;
- return std::make_unique<VSyncPredictor>(modePtr, kHistorySize, kMinSamplesForPrediction,
- kDiscardOutlierPercent);
+ return std::make_unique<VSyncPredictor>(std::make_unique<SystemClock>(), modePtr, kHistorySize,
+ kMinSamplesForPrediction, kDiscardOutlierPercent);
}
VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) {
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 85cd3e7..881d678 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -81,7 +81,7 @@
bool addResyncSample(TimePoint timestamp, ftl::Optional<Period> hwcVsyncPeriod);
// TODO(b/185535769): Hide behind API.
- const VsyncTracker& getTracker() const { return *mTracker; }
+ VsyncTracker& getTracker() const { return *mTracker; }
VsyncTracker& getTracker() { return *mTracker; }
VsyncController& getController() { return *mController; }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 57839c6..b3adedd 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1239,8 +1239,8 @@
switch (display->setDesiredMode(std::move(desiredMode))) {
case DisplayDevice::DesiredModeAction::InitiateDisplayModeSwitch:
// DisplayDevice::setDesiredMode updated the render rate, so inform Scheduler.
- mScheduler->setRenderRate(displayId,
- display->refreshRateSelector().getActiveMode().fps);
+ mScheduler->setRenderRate(displayId, display->refreshRateSelector().getActiveMode().fps,
+ /*applyImmediately*/ true);
// Schedule a new frame to initiate the display mode switch.
scheduleComposite(FrameHint::kNone);
@@ -1261,7 +1261,7 @@
mScheduler->setModeChangePending(true);
break;
case DisplayDevice::DesiredModeAction::InitiateRenderRateSwitch:
- mScheduler->setRenderRate(displayId, mode.fps);
+ mScheduler->setRenderRate(displayId, mode.fps, /*applyImmediately*/ false);
if (displayId == mActiveDisplayId) {
mScheduler->updatePhaseConfiguration(mode.fps);
@@ -1382,7 +1382,7 @@
constexpr bool kAllowToEnable = true;
mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, std::move(activeModePtr).take());
- mScheduler->setRenderRate(displayId, renderFps);
+ mScheduler->setRenderRate(displayId, renderFps, /*applyImmediately*/ true);
if (displayId == mActiveDisplayId) {
mScheduler->updatePhaseConfiguration(renderFps);
@@ -2730,7 +2730,8 @@
refreshArgs.forceOutputColorMode = mForceColorMode;
refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
- refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty;
+ refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) ||
+ mVisibleRegionsDirty || mDrawingState.colorMatrixChanged;
refreshArgs.internalDisplayRotationFlags = getActiveDisplayRotationFlags();
if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
@@ -4376,7 +4377,8 @@
// The pacesetter must be registered before EventThread creation below.
mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
if (FlagManager::getInstance().vrr_config()) {
- mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps);
+ mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps,
+ /*applyImmediately*/ true);
}
const auto configs = mScheduler->getVsyncConfiguration().getCurrentConfigs();
@@ -4700,7 +4702,14 @@
return TransactionReadiness::NotReady;
}
- if (!mScheduler->isVsyncValid(expectedPresentTime, transaction.originUid)) {
+ const auto vsyncId = VsyncId{transaction.frameTimelineInfo.vsyncId};
+
+ // Transactions with VsyncId are already throttled by the vsyncId (i.e. Choreographer issued
+ // the vsyncId according to the frame rate override cadence) so we shouldn't throttle again
+ // when applying the transaction. Otherwise we might throttle older transactions
+ // incorrectly as the frame rate of SF changed before it drained the older transactions.
+ if (ftl::to_underlying(vsyncId) == FrameTimelineInfo::INVALID_VSYNC_ID &&
+ !mScheduler->isVsyncValid(expectedPresentTime, transaction.originUid)) {
ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d", expectedPresentTime,
transaction.originUid);
return TransactionReadiness::NotReady;
@@ -4708,8 +4717,7 @@
// 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})) {
+ if (transaction.isAutoTimestamp && frameIsEarly(expectedPresentTime, vsyncId)) {
ATRACE_FORMAT("frameIsEarly vsyncId: %" PRId64 " expectedPresentTime: %" PRId64,
transaction.frameTimelineInfo.vsyncId, expectedPresentTime);
return TransactionReadiness::NotReady;
@@ -8061,6 +8069,19 @@
args.allowProtected, args.grayscale, captureListener);
}
+bool SurfaceFlinger::layersHasProtectedLayer(
+ const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const {
+ bool protectedLayerFound = false;
+ for (auto& [_, layerFe] : layers) {
+ protectedLayerFound |=
+ (layerFe->mSnapshot->isVisible && layerFe->mSnapshot->hasProtectedContent);
+ if (protectedLayerFound) {
+ break;
+ }
+ }
+ return protectedLayerFound;
+}
+
void SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
GetLayerSnapshotsFunction getLayerSnapshots,
ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
@@ -8083,18 +8104,9 @@
const bool supportsProtected = getRenderEngine().supportsProtectedContent();
bool hasProtectedLayer = false;
if (allowProtected && supportsProtected) {
- hasProtectedLayer = mScheduler
- ->schedule([=]() {
- bool protectedLayerFound = false;
- auto layers = getLayerSnapshots();
- for (auto& [_, layerFe] : layers) {
- protectedLayerFound |=
- (layerFe->mSnapshot->isVisible &&
- layerFe->mSnapshot->hasProtectedContent);
- }
- return protectedLayerFound;
- })
- .get();
+ // Snapshots must be taken from the main thread.
+ auto layers = mScheduler->schedule([=]() { return getLayerSnapshots(); }).get();
+ hasProtectedLayer = layersHasProtectedLayer(layers);
}
const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected;
const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
@@ -8119,52 +8131,53 @@
renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
renderengine::impl::ExternalTexture::Usage::
WRITEABLE);
- auto fence = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, texture,
- false /* regionSampling */, grayscale, isProtected,
- captureListener);
- fence.get();
+ auto futureFence =
+ captureScreenshot(std::move(renderAreaFuture), getLayerSnapshots, texture,
+ false /* regionSampling */, grayscale, isProtected, captureListener);
+ futureFence.get();
}
-ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon(
+ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot(
RenderAreaFuture renderAreaFuture, GetLayerSnapshotsFunction getLayerSnapshots,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
- auto future = mScheduler->schedule(
- [=, this, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD(
- kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
- ScreenCaptureResults captureResults;
- std::shared_ptr<RenderArea> renderArea = renderAreaFuture.get();
- if (!renderArea) {
- ALOGW("Skipping screen capture because of invalid render area.");
- if (captureListener) {
- captureResults.fenceResult = base::unexpected(NO_MEMORY);
+ auto takeScreenshotFn = [=, this, renderAreaFuture = std::move(renderAreaFuture)]() REQUIRES(
+ kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
+ ScreenCaptureResults captureResults;
+ std::shared_ptr<RenderArea> renderArea = renderAreaFuture.get();
+ if (!renderArea) {
+ ALOGW("Skipping screen capture because of invalid render area.");
+ if (captureListener) {
+ captureResults.fenceResult = base::unexpected(NO_MEMORY);
+ captureListener->onScreenCaptureCompleted(captureResults);
+ }
+ return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
+ }
+
+ ftl::SharedFuture<FenceResult> renderFuture;
+ renderArea->render([&]() FTL_FAKE_GUARD(kMainThreadContext) {
+ renderFuture = renderScreenImpl(renderArea, getLayerSnapshots, buffer, regionSampling,
+ grayscale, isProtected, captureResults);
+ });
+
+ if (captureListener) {
+ // Defer blocking on renderFuture back to the Binder thread.
+ return ftl::Future(std::move(renderFuture))
+ .then([captureListener, captureResults = std::move(captureResults)](
+ FenceResult fenceResult) mutable -> FenceResult {
+ captureResults.fenceResult = std::move(fenceResult);
captureListener->onScreenCaptureCompleted(captureResults);
- }
- return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
- }
+ return base::unexpected(NO_ERROR);
+ })
+ .share();
+ }
+ return renderFuture;
+ };
- ftl::SharedFuture<FenceResult> renderFuture;
- renderArea->render([&]() FTL_FAKE_GUARD(kMainThreadContext) {
- renderFuture =
- renderScreenImpl(renderArea, getLayerSnapshots, buffer, regionSampling,
- grayscale, isProtected, captureResults);
- });
-
- if (captureListener) {
- // Defer blocking on renderFuture back to the Binder thread.
- return ftl::Future(std::move(renderFuture))
- .then([captureListener, captureResults = std::move(captureResults)](
- FenceResult fenceResult) mutable -> FenceResult {
- captureResults.fenceResult = std::move(fenceResult);
- captureListener->onScreenCaptureCompleted(captureResults);
- return base::unexpected(NO_ERROR);
- })
- .share();
- }
- return renderFuture;
- });
+ auto future =
+ mScheduler->schedule(FTL_FAKE_GUARD(kMainThreadContext, std::move(takeScreenshotFn)));
// Flatten nested futures.
auto chain = ftl::Future(std::move(future)).then([](ftl::SharedFuture<FenceResult> future) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e90d09b..005f2e6 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -874,13 +874,17 @@
// Boot animation, on/off animations and screen capture
void startBootAnim();
+ bool layersHasProtectedLayer(const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const;
+
void captureScreenCommon(RenderAreaFuture, GetLayerSnapshotsFunction, ui::Size bufferSize,
ui::PixelFormat, bool allowProtected, bool grayscale,
const sp<IScreenCaptureListener>&);
- ftl::SharedFuture<FenceResult> captureScreenCommon(
+
+ ftl::SharedFuture<FenceResult> captureScreenshot(
RenderAreaFuture, GetLayerSnapshotsFunction,
const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
bool grayscale, bool isProtected, const sp<IScreenCaptureListener>&);
+
ftl::SharedFuture<FenceResult> renderScreenImpl(
std::shared_ptr<const RenderArea>, GetLayerSnapshotsFunction,
const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index f2ff00b..4a89dd0 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -35,6 +35,7 @@
],
static_libs: [
"libsurfaceflingerflags",
+ "android.os.flags-aconfig-cc",
],
}
@@ -45,5 +46,30 @@
],
static_libs: [
"libsurfaceflingerflags_test",
+ "android.os.flags-aconfig-cc-test",
+ ],
+}
+
+cc_defaults {
+ name: "libsurfaceflinger_common_deps",
+ shared_libs: [
+ "server_configurable_flags",
+ ],
+ static_libs: [
+ "libsurfaceflinger_common",
+ "libsurfaceflingerflags",
+ "android.os.flags-aconfig-cc",
+ ],
+}
+
+cc_defaults {
+ name: "libsurfaceflinger_common_test_deps",
+ shared_libs: [
+ "server_configurable_flags",
+ ],
+ static_libs: [
+ "libsurfaceflinger_common_test",
+ "libsurfaceflingerflags_test",
+ "android.os.flags-aconfig-cc-test",
],
}
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index b7f06a9..3b669c6 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -26,6 +26,7 @@
#include <server_configurable_flags/get_flags.h>
#include <cinttypes>
+#include <android_os.h>
#include <com_android_graphics_surfaceflinger_flags.h>
namespace android {
@@ -109,6 +110,7 @@
/// Trunk stable server flags ///
DUMP_SERVER_FLAG(refresh_rate_overlay_on_external_display);
+ DUMP_SERVER_FLAG(adpf_use_fmq_channel);
/// Trunk stable readonly flags ///
DUMP_READ_ONLY_FLAG(connected_display);
@@ -158,7 +160,7 @@
return getServerConfigurableFlag(serverFlagName); \
}
-#define FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, checkForBootCompleted) \
+#define FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, checkForBootCompleted, owner) \
bool FlagManager::name() const { \
if (checkForBootCompleted) { \
LOG_ALWAYS_FATAL_IF(!mBootCompleted, \
@@ -166,21 +168,24 @@
__func__); \
} \
static const std::optional<bool> debugOverride = getBoolProperty(syspropOverride); \
- static const bool value = getFlagValue([] { return flags::name(); }, debugOverride); \
+ static const bool value = getFlagValue([] { return owner ::name(); }, debugOverride); \
if (mUnitTestMode) { \
/* \
* When testing, we don't want to rely on the cached `value` or the debugOverride. \
*/ \
- return flags::name(); \
+ return owner ::name(); \
} \
return value; \
}
#define FLAG_MANAGER_SERVER_FLAG(name, syspropOverride) \
- FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true)
+ FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true, flags)
#define FLAG_MANAGER_READ_ONLY_FLAG(name, syspropOverride) \
- FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, false)
+ FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, false, flags)
+
+#define FLAG_MANAGER_SERVER_FLAG_IMPORTED(name, syspropOverride, owner) \
+ FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true, owner)
/// Legacy server flags ///
FLAG_MANAGER_LEGACY_SERVER_FLAG(test_flag, "", "")
@@ -216,4 +221,7 @@
/// Trunk stable server flags ///
FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
+/// Trunk stable server flags from outside SurfaceFlinger ///
+FLAG_MANAGER_SERVER_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os)
+
} // namespace android
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 241c814..763963e 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -49,6 +49,7 @@
/// Trunk stable server flags ///
bool refresh_rate_overlay_on_external_display() const;
+ bool adpf_use_fmq_channel() const;
/// Trunk stable readonly flags ///
bool connected_display() const;
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index dab0a3f..925fe0b 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -27,6 +27,7 @@
defaults: [
"android.hardware.graphics.common-ndk_shared",
"surfaceflinger_defaults",
+ "libsurfaceflinger_common_test_deps",
],
test_suites: ["device-tests"],
srcs: [
@@ -66,7 +67,6 @@
static_libs: [
"liblayers_proto",
"android.hardware.graphics.composer@2.1",
- "libsurfaceflingerflags",
],
shared_libs: [
"android.hardware.graphics.common@1.2",
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 08cfc55..0c13db3 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -149,6 +149,7 @@
"android.hardware.graphics.composer3-ndk_static",
"android.hardware.power-ndk_static",
"librenderengine_deps",
+ "libsurfaceflinger_common_test_deps",
],
static_libs: [
"android.hardware.common-V2-ndk",
@@ -173,13 +174,11 @@
"librenderengine_mocks",
"libscheduler",
"libserviceutils",
- "libsurfaceflinger_common_test",
"libtimestats",
"libtimestats_atoms_proto",
"libtimestats_proto",
"libtonemap",
"perfetto_trace_protos",
- "libsurfaceflingerflags_test",
],
shared_libs: [
"android.hardware.configstore-utils",
@@ -208,7 +207,6 @@
"libsync",
"libui",
"libutils",
- "server_configurable_flags",
],
header_libs: [
"android.hardware.graphics.composer3-command-buffer",
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 0a6e305..fe0e3d1 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -259,6 +259,44 @@
config.enableFrameRateOverride = GetParam();
return TestableRefreshRateSelector(modes, activeModeId, config);
}
+
+ template <class T>
+ void testFrameRateCategoryWithMultipleLayers(const std::initializer_list<T>& testCases,
+ const TestableRefreshRateSelector& selector) {
+ std::vector<LayerRequirement> layers;
+ for (auto testCase : testCases) {
+ ALOGI("**** %s: Testing desiredFrameRate=%s, frameRateCategory=%s", __func__,
+ to_string(testCase.desiredFrameRate).c_str(),
+ ftl::enum_string(testCase.frameRateCategory).c_str());
+
+ if (testCase.desiredFrameRate.isValid()) {
+ std::stringstream ss;
+ ss << to_string(testCase.desiredFrameRate)
+ << ftl::enum_string(testCase.frameRateCategory) << "ExplicitDefault";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = testCase.desiredFrameRate,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
+
+ if (testCase.frameRateCategory != FrameRateCategory::Default) {
+ std::stringstream ss;
+ ss << "ExplicitCategory (" << ftl::enum_string(testCase.frameRateCategory) << ")";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitCategory,
+ .frameRateCategory = testCase.frameRateCategory,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
+
+ EXPECT_EQ(testCase.expectedFrameRate,
+ selector.getBestFrameRateMode(layers).modePtr->getPeakFps())
+ << "Did not get expected frame rate for frameRate="
+ << to_string(testCase.desiredFrameRate)
+ << " category=" << ftl::enum_string(testCase.frameRateCategory);
+ }
+ }
};
RefreshRateSelectorTest::RefreshRateSelectorTest() {
@@ -1542,6 +1580,96 @@
}
}
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_withFrameRateCategoryMultiLayers_30_60_90_120) {
+ auto selector = createSelector(makeModes(kMode30, kMode60, kMode90, kMode120), kModeId60);
+
+ struct Case {
+ // Params
+ Fps desiredFrameRate = 0_Hz;
+ FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+
+ // Expected result
+ Fps expectedFrameRate = 0_Hz;
+ };
+
+ testFrameRateCategoryWithMultipleLayers(
+ std::initializer_list<Case>{
+ {0_Hz, FrameRateCategory::High, 90_Hz},
+ {0_Hz, FrameRateCategory::NoPreference, 90_Hz},
+ {0_Hz, FrameRateCategory::Normal, 90_Hz},
+ {0_Hz, FrameRateCategory::Normal, 90_Hz},
+ {0_Hz, FrameRateCategory::NoPreference, 90_Hz},
+ },
+ selector);
+
+ testFrameRateCategoryWithMultipleLayers(
+ std::initializer_list<Case>{
+ {0_Hz, FrameRateCategory::Normal, 60_Hz},
+ {0_Hz, FrameRateCategory::High, 90_Hz},
+ {0_Hz, FrameRateCategory::NoPreference, 90_Hz},
+ },
+ selector);
+
+ testFrameRateCategoryWithMultipleLayers(
+ std::initializer_list<Case>{
+ {30_Hz, FrameRateCategory::High, 90_Hz},
+ {24_Hz, FrameRateCategory::High, 120_Hz},
+ {12_Hz, FrameRateCategory::Normal, 120_Hz},
+ {30_Hz, FrameRateCategory::NoPreference, 120_Hz},
+
+ },
+ selector);
+
+ testFrameRateCategoryWithMultipleLayers(
+ std::initializer_list<Case>{
+ {24_Hz, FrameRateCategory::Default, 120_Hz},
+ {30_Hz, FrameRateCategory::Default, 120_Hz},
+ {120_Hz, FrameRateCategory::Default, 120_Hz},
+ },
+ selector);
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategoryMultiLayers_60_120) {
+ auto selector = createSelector(makeModes(kMode60, kMode120), kModeId60);
+
+ struct Case {
+ // Params
+ Fps desiredFrameRate = 0_Hz;
+ FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+
+ // Expected result
+ Fps expectedFrameRate = 0_Hz;
+ };
+
+ testFrameRateCategoryWithMultipleLayers(std::initializer_list<
+ Case>{{0_Hz, FrameRateCategory::High, 120_Hz},
+ {0_Hz, FrameRateCategory::NoPreference,
+ 120_Hz},
+ {0_Hz, FrameRateCategory::Normal, 120_Hz},
+ {0_Hz, FrameRateCategory::Normal, 120_Hz},
+ {0_Hz, FrameRateCategory::NoPreference,
+ 120_Hz}},
+ selector);
+
+ testFrameRateCategoryWithMultipleLayers(std::initializer_list<
+ Case>{{24_Hz, FrameRateCategory::High, 120_Hz},
+ {30_Hz, FrameRateCategory::High, 120_Hz},
+ {12_Hz, FrameRateCategory::Normal,
+ 120_Hz},
+ {30_Hz, FrameRateCategory::NoPreference,
+ 120_Hz}},
+ selector);
+
+ testFrameRateCategoryWithMultipleLayers(
+ std::initializer_list<Case>{
+ {24_Hz, FrameRateCategory::Default, 120_Hz},
+ {30_Hz, FrameRateCategory::Default, 120_Hz},
+ {120_Hz, FrameRateCategory::Default, 120_Hz},
+ },
+ selector);
+}
+
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_60_120) {
auto selector = createSelector(makeModes(kMode60, kMode120), kModeId60);
@@ -1665,6 +1793,7 @@
lr1.frameRateCategory = FrameRateCategory::HighHint;
lr1.name = "ExplicitCategory HighHint";
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.frameRateCategory = FrameRateCategory::Default;
lr2.desiredRefreshRate = 30_Hz;
lr2.name = "30Hz ExplicitExactOrMultiple";
actualRankedFrameRates = selector.getRankedFrameRates(layers);
@@ -1691,6 +1820,153 @@
EXPECT_EQ(kModeId30, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId());
EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
}
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz Heuristic";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers);
+ // Gets touch boost
+ EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId());
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::Min;
+ lr2.name = "Min";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers);
+ // Gets touch boost
+ EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId());
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::Max;
+ lr2.name = "Max";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers);
+ // Gets touch boost
+ EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId());
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_TouchBoost) {
+ auto selector = createSelector(makeModes(kMode24, kMode30, kMode60, kMode120), kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::NoVote;
+ lr2.name = "NoVote";
+ auto actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+
+ // No touch boost, for example a game that uses setFrameRate(30, default compatibility).
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitDefault";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitCategory;
+ lr2.frameRateCategory = FrameRateCategory::HighHint;
+ lr2.name = "ExplicitCategory HighHint";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitCategory;
+ lr2.frameRateCategory = FrameRateCategory::Low;
+ lr2.name = "ExplicitCategory Low";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.frameRateCategory = FrameRateCategory::Default;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitExactOrMultiple";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitExact;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitExact";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ if (selector.supportsAppFrameRateOverrideByContent()) {
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz,
+ actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+ } else {
+ EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz,
+ actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+ }
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::Min;
+ lr2.name = "Min";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::Max;
+ lr2.name = "Max";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "30Hz Heuristic";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitGte;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitGte";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
}
TEST_P(RefreshRateSelectorTest,
@@ -1725,8 +2001,8 @@
// These layers cannot change mode due to smoothSwitchOnly, and will definitely use
// active mode (120Hz).
{FrameRateCategory::NoPreference, true, 120_Hz, kModeId120},
- {FrameRateCategory::Low, true, 120_Hz, kModeId120},
- {FrameRateCategory::Normal, true, 40_Hz, kModeId120},
+ {FrameRateCategory::Low, true, 40_Hz, kModeId120},
+ {FrameRateCategory::Normal, true, 120_Hz, kModeId120},
{FrameRateCategory::High, true, 120_Hz, kModeId120},
};
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 10e2220..d4735c7 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -25,6 +25,7 @@
#include "Scheduler/EventThread.h"
#include "Scheduler/RefreshRateSelector.h"
#include "Scheduler/VSyncPredictor.h"
+#include "Scheduler/VSyncReactor.h"
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockDisplayMode.h"
@@ -56,6 +57,11 @@
using LayerHierarchyBuilder = surfaceflinger::frontend::LayerHierarchyBuilder;
using RequestedLayerState = surfaceflinger::frontend::RequestedLayerState;
+class ZeroClock : public Clock {
+public:
+ nsecs_t now() const override { return 0; }
+};
+
class SchedulerTest : public testing::Test {
protected:
class MockEventThreadConnection : public android::EventThreadConnection {
@@ -563,7 +569,8 @@
hal::VrrConfig{.minFrameIntervalNs = static_cast<int32_t>(
frameRate.getPeriodNsecs())}));
std::shared_ptr<VSyncPredictor> vrrTracker =
- std::make_shared<VSyncPredictor>(kMode, kHistorySize, kMinimumSamplesForPrediction,
+ std::make_shared<VSyncPredictor>(std::make_unique<ZeroClock>(), kMode, kHistorySize,
+ kMinimumSamplesForPrediction,
kOutlierTolerancePercent);
std::shared_ptr<RefreshRateSelector> vrrSelectorPtr =
std::make_shared<RefreshRateSelector>(makeModes(kMode), kMode->getId());
@@ -576,8 +583,10 @@
scheduler.registerDisplay(kMode->getPhysicalDisplayId(), vrrSelectorPtr, vrrTracker);
vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate);
- scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate);
+ scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate, /*applyImmediately*/ false);
vrrTracker->addVsyncTimestamp(0);
+ // Set 1000 as vsync seq #0
+ vrrTracker->nextAnticipatedVSyncTimeFrom(700);
EXPECT_EQ(Fps::fromPeriodNsecs(1000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
@@ -587,20 +596,21 @@
TimePoint::fromNs(2000)));
// Not crossing the min frame period
- EXPECT_EQ(Fps::fromPeriodNsecs(1500),
+ vrrTracker->onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500));
+ EXPECT_EQ(Fps::fromPeriodNsecs(1000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
TimePoint::fromNs(2500)));
// Change render rate
frameRate = Fps::fromPeriodNsecs(2000);
vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate);
- scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate);
+ scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate, /*applyImmediately*/ false);
EXPECT_EQ(Fps::fromPeriodNsecs(2000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
- TimePoint::fromNs(2000)));
+ TimePoint::fromNs(4500)));
EXPECT_EQ(Fps::fromPeriodNsecs(2000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
- TimePoint::fromNs(4000)));
+ TimePoint::fromNs(6500)));
}
TEST_F(SchedulerTest, resyncAllToHardwareVsync) FTL_FAKE_GUARD(kMainThreadContext) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index d891008..d701a97 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -48,9 +48,9 @@
Period minFramePeriod() const final { return Period::fromNs(currentPeriod()); }
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
- bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
+ bool isVSyncInPhase(nsecs_t, Fps) final { return false; }
void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final {}
- void setRenderRate(Fps) final {}
+ void setRenderRate(Fps, bool) final {}
void onFrameBegin(TimePoint, TimePoint) final {}
void onFrameMissed(TimePoint) final {}
void dump(std::string&) const final {}
@@ -64,7 +64,7 @@
public:
FixedRateIdealStubTracker() : StubTracker{toNs(3ms)} {}
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, std::optional<nsecs_t>) const final {
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, std::optional<nsecs_t>) final {
auto const floor = timePoint % mPeriod;
if (floor == 0) {
return timePoint;
@@ -77,7 +77,7 @@
public:
VRRStubTracker(nsecs_t period) : StubTracker(period) {}
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point, std::optional<nsecs_t>) const final {
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point, std::optional<nsecs_t>) final {
std::lock_guard lock(mMutex);
auto const normalized_to_base = time_point - mBase;
auto const floor = (normalized_to_base) % mPeriod;
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index b9f3d70..48707cb 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -75,6 +75,28 @@
return ftl::as_non_null(createDisplayMode(DisplayModeId(0), refreshRate, kGroup, kResolution,
DEFAULT_DISPLAY_ID));
}
+
+class TestClock : public Clock {
+public:
+ TestClock() = default;
+
+ nsecs_t now() const override { return mNow; }
+ void setNow(nsecs_t now) { mNow = now; }
+
+private:
+ nsecs_t mNow = 0;
+};
+
+class ClockWrapper : public Clock {
+public:
+ ClockWrapper(std::shared_ptr<Clock> const& clock) : mClock(clock) {}
+
+ nsecs_t now() const { return mClock->now(); }
+
+private:
+ std::shared_ptr<Clock> const mClock;
+};
+
} // namespace
struct VSyncPredictorTest : testing::Test {
@@ -86,8 +108,10 @@
static constexpr size_t kOutlierTolerancePercent = 25;
static constexpr nsecs_t mMaxRoundingError = 100;
- VSyncPredictor tracker{mMode, kHistorySize, kMinimumSamplesForPrediction,
- kOutlierTolerancePercent};
+ std::shared_ptr<TestClock> mClock{std::make_shared<TestClock>()};
+
+ VSyncPredictor tracker{std::make_unique<ClockWrapper>(mClock), mMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
};
TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) {
@@ -408,7 +432,8 @@
// See b/151146131
TEST_F(VSyncPredictorTest, hasEnoughPrecision) {
const auto mode = displayMode(mPeriod);
- VSyncPredictor tracker{mode, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+ VSyncPredictor tracker{std::make_unique<ClockWrapper>(mClock), mode, 20,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675,
840923581635, 840940161584, 840956868096,
840973702473, 840990256277, 841007116851,
@@ -595,44 +620,15 @@
tracker.addVsyncTimestamp(mNow);
}
- tracker.setRenderRate(Fps::fromPeriodNsecs(3 * mPeriod));
+ tracker.setRenderRate(Fps::fromPeriodNsecs(3 * mPeriod), /*applyImmediately*/ false);
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 4 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 4 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 4 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 7 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 7 * mPeriod));
-}
-
-TEST_F(VSyncPredictorTest, setRenderRateOfDivisorIsInPhase) {
- auto last = mNow;
- for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
- mNow += mPeriod;
- last = mNow;
- tracker.addVsyncTimestamp(mNow);
- }
-
- const auto refreshRate = Fps::fromPeriodNsecs(mPeriod);
-
- tracker.setRenderRate(refreshRate / 4);
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 3 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 7 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 11 * mPeriod));
-
- tracker.setRenderRate(refreshRate / 2);
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 3 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 5 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5 * mPeriod), Eq(mNow + 7 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 9 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 9 * mPeriod), Eq(mNow + 11 * mPeriod));
-
- tracker.setRenderRate(refreshRate / 6);
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 7 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + 3 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 3 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 3 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 6 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 6 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod));
}
TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) {
@@ -644,7 +640,7 @@
tracker.addVsyncTimestamp(mNow);
}
- tracker.setRenderRate(Fps::fromPeriodNsecs(3.5f * mPeriod));
+ tracker.setRenderRate(Fps::fromPeriodNsecs(3.5f * mPeriod), /*applyImmediately*/ false);
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod));
@@ -655,6 +651,178 @@
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod));
}
+TEST_F(VSyncPredictorTest, setRenderRateHighIsAppliedImmediately) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto vsyncRate = Fps::fromPeriodNsecs(500);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1000));
+
+ // commit to a vsync in the future
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(2000), /*applyImmediately*/ false);
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+ EXPECT_EQ(8000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
+
+ EXPECT_EQ(12000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000));
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(3500), /*applyImmediately*/ false);
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+ EXPECT_EQ(8000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
+ EXPECT_EQ(10000, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 8000));
+ EXPECT_EQ(12000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000));
+ EXPECT_EQ(15500, vrrTracker.nextAnticipatedVSyncTimeFrom(12000, 12000));
+ EXPECT_EQ(19000, vrrTracker.nextAnticipatedVSyncTimeFrom(15500, 15500));
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(2500), /*applyImmediately*/ false);
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+ EXPECT_EQ(8000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
+ EXPECT_EQ(10000, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 8000));
+ EXPECT_EQ(12000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000));
+ EXPECT_EQ(15500, vrrTracker.nextAnticipatedVSyncTimeFrom(12000, 12000));
+ EXPECT_EQ(19000, vrrTracker.nextAnticipatedVSyncTimeFrom(15500, 15500));
+ EXPECT_EQ(21500, vrrTracker.nextAnticipatedVSyncTimeFrom(19000, 19000));
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false);
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+ EXPECT_EQ(7000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
+ EXPECT_EQ(9000, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 8000));
+ EXPECT_EQ(11000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000));
+ EXPECT_EQ(13000, vrrTracker.nextAnticipatedVSyncTimeFrom(12000, 12000));
+ EXPECT_EQ(17000, vrrTracker.nextAnticipatedVSyncTimeFrom(15500, 15500));
+ EXPECT_EQ(20000, vrrTracker.nextAnticipatedVSyncTimeFrom(19000, 19000));
+}
+
+TEST_F(VSyncPredictorTest, minFramePeriodDoesntApplyWhenSameWithRefreshRate) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto vsyncRate = Fps::fromPeriodNsecs(1000);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1000));
+
+ // Assume that the last vsync is wrong due to a vsync drift. It shouldn't matter.
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1700));
+}
+
+TEST_F(VSyncPredictorTest, setRenderRateExplicitAppliedImmediately) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto vsyncRate = Fps::fromPeriodNsecs(500);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1000));
+
+ // commit to a vsync in the future
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 2000));
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(2000), /*applyImmediately*/ true);
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000));
+ EXPECT_EQ(7000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+ EXPECT_EQ(9000, vrrTracker.nextAnticipatedVSyncTimeFrom(7000, 7000));
+}
+
+TEST_F(VSyncPredictorTest, selectsClosestVsyncAfterInactivity) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto vsyncRate = Fps::fromPeriodNsecs(500);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(5000), /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4700));
+ EXPECT_EQ(10000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+
+ mClock->setNow(50000);
+ EXPECT_EQ(50500, vrrTracker.nextAnticipatedVSyncTimeFrom(50000, 10000));
+}
+
+TEST_F(VSyncPredictorTest, returnsCorrectVsyncWhenLastIsNot) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto vsyncRate = Fps::fromPeriodNsecs(500);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1234, 1234));
+}
+
TEST_F(VSyncPredictorTest, adjustsVrrTimeline) {
SET_FLAG_FOR_TEST(flags::vrr_config, true);
@@ -670,10 +838,10 @@
.setVrrConfig(std::move(vrrConfig))
.build());
- VSyncPredictor vrrTracker{kMode, kHistorySize, kMinimumSamplesForPrediction,
- kOutlierTolerancePercent};
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
- vrrTracker.setRenderRate(minFrameRate);
+ vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false);
vrrTracker.addVsyncTimestamp(0);
EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000));
@@ -687,7 +855,95 @@
vrrTracker.onFrameMissed(TimePoint::fromNs(4500));
EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500));
EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+
+ vrrTracker.onFrameBegin(TimePoint::fromNs(7000), TimePoint::fromNs(6500));
+ EXPECT_EQ(10500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000));
}
+
+TEST_F(VSyncPredictorTest, adjustsVrrTimelineTwoClients) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto refreshRate = Fps::fromPeriodNsecs(500);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), refreshRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+
+ // App runs ahead
+ EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2700));
+ EXPECT_EQ(4000, vrrTracker.nextAnticipatedVSyncTimeFrom(3000, 3000));
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
+
+ // SF starts to catch up
+ EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2700));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(3000), TimePoint::fromNs(0));
+
+ // SF misses last frame (3000) and observes that when committing (4000)
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+ EXPECT_EQ(4000, vrrTracker.nextAnticipatedVSyncTimeFrom(3700));
+ vrrTracker.onFrameMissed(TimePoint::fromNs(4000));
+
+ // SF wakes up again instead of the (4000) missed frame
+ EXPECT_EQ(4500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(4500), TimePoint::fromNs(4500));
+
+ // Timeline shifted. The app needs to get the next frame at (7500) as its last frame (6500) will
+ // be presented at (7500)
+ EXPECT_EQ(7500, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
+ EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(5500), TimePoint::fromNs(4500));
+
+ EXPECT_EQ(8500, vrrTracker.nextAnticipatedVSyncTimeFrom(7500, 7500));
+ EXPECT_EQ(6500, vrrTracker.nextAnticipatedVSyncTimeFrom(5500, 5500));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(6500), TimePoint::fromNs(5500));
+}
+
+TEST_F(VSyncPredictorTest, renderRateIsPreservedForCommittedVsyncs) {
+ tracker.addVsyncTimestamp(1000);
+
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(6000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(7000));
+
+ tracker.setRenderRate(Fps::fromPeriodNsecs(2000), /*applyImmediately*/ false);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(6000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(7000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(7001), Eq(9000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(9000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(11000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(10001), Eq(11000));
+
+ tracker.setRenderRate(Fps::fromPeriodNsecs(3000), /*applyImmediately*/ false);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(6000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(7000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(7001), Eq(9000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(9000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(11000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(10001), Eq(11000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(11001), Eq(14000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(12001), Eq(14000));
+
+ // Check the purge logic works
+ mClock->setNow(20000);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(2000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(8000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(8000));
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index 3870983..c311901 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -29,14 +29,14 @@
MOCK_METHOD(bool, addVsyncTimestamp, (nsecs_t), (override));
MOCK_METHOD(nsecs_t, nextAnticipatedVSyncTimeFrom, (nsecs_t, std::optional<nsecs_t>),
- (const, override));
+ (override));
MOCK_METHOD(nsecs_t, currentPeriod, (), (const, override));
MOCK_METHOD(Period, minFramePeriod, (), (const, override));
MOCK_METHOD(void, resetModel, (), (override));
MOCK_METHOD(bool, needsMoreSamples, (), (const, override));
- MOCK_METHOD(bool, isVSyncInPhase, (nsecs_t, Fps), (const, override));
+ MOCK_METHOD(bool, isVSyncInPhase, (nsecs_t, Fps), (override));
MOCK_METHOD(void, setDisplayModePtr, (ftl::NonNull<DisplayModePtr>), (override));
- MOCK_METHOD(void, setRenderRate, (Fps), (override));
+ MOCK_METHOD(void, setRenderRate, (Fps, bool), (override));
MOCK_METHOD(void, onFrameBegin, (TimePoint, TimePoint), (override));
MOCK_METHOD(void, onFrameMissed, (TimePoint), (override));
MOCK_METHOD(void, dump, (std::string&), (const, override));