diff options
33 files changed, 807 insertions, 358 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 8d37aac0cb..826a8dbc2c 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -3280,6 +3280,12 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // 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 28bd7932a2..ae7d8e04d4 100644 --- a/cmds/installd/otapreopt_script.sh +++ b/cmds/installd/otapreopt_script.sh @@ -50,6 +50,12 @@ else 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 8b2842a3ae..3f4ee13b95 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -287,6 +287,12 @@ prebuilt_etc { } 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"], @@ -353,6 +359,12 @@ prebuilt_etc { } 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/input/Input.h b/include/input/Input.h index 374254fc84..ddc376809e 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -662,10 +662,6 @@ public: 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 @@ public: 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 42dcd3c394..aca4b622d1 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -670,13 +670,6 @@ private: 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 2dd310e9ca..35cea8132d 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -2976,14 +2976,14 @@ status_t Parcel::restartWrite(size_t desired) 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/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 5c280f4b2c..e378b864f7 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -115,6 +115,14 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { 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), diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp index 4518b67d4c..54290cd629 100644 --- a/libs/gui/Choreographer.cpp +++ b/libs/gui/Choreographer.cpp @@ -143,9 +143,9 @@ Choreographer::~Choreographer() { 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::handleRefreshRateUpdates() { } } -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 } } +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 55a7aa7ddc..fc79b03c23 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 @@ public: 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 @@ private: 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 e606b9941e..0f16f714dc 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -30,6 +30,7 @@ cc_test { "BLASTBufferQueue_test.cpp", "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", + "Choreographer_test.cpp", "CompositorTiming_test.cpp", "CpuConsumer_test.cpp", "EndToEndNativeInputTest.cpp", @@ -61,6 +62,7 @@ cc_test { "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 0000000000..2ac2550f07 --- /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 d58fb42f05..ff9d9a94c9 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -751,6 +751,18 @@ void MotionEvent::offsetLocation(float xOffset, float yOffset) { 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 e49f4eb6f6..b3a36ebf5a 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 @@ android::base::unique_fd dupChannelFd(int fd) { 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 @@ static const char* PROPERTY_RESAMPLING_ENABLED = "ro.input.resampling"; * 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 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum 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 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum if (!motionEvent) return NO_MEMORY; updateTouchState(mMsg); - initializeMotionEvent(motionEvent, &mMsg); + initializeMotionEvent(*motionEvent, mMsg); *outSeq = mMsg.header.seq; *outEvent = motionEvent; @@ -987,7 +1041,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum 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 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum 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 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum 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 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum 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 @@ status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, 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 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, 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 @@ ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { 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 0412d08181..6872af2aa5 100644 --- a/libs/input/MotionPredictorMetricsManager.cpp +++ b/libs/input/MotionPredictorMetricsManager.cpp @@ -113,7 +113,12 @@ void MotionPredictorMetricsManager::onRecord(const MotionEvent& inputEvent) { // 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 @@ void MotionPredictorMetricsManager::computeAtomFields() { 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 60feb53dcc..02d4c07bfa 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 540766d66c..0df06b790e 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -371,8 +371,10 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { 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, SplitPointerMove) { 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 35430207f9..b5fab496e2 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 @@ void verifyArgsEqualToEvent(const PublishMotionArgs& args, const MotionEvent& mo 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 31cc1459fc..cc41eeb5e7 100644 --- a/libs/input/tests/MotionPredictorMetricsManager_test.cpp +++ b/libs/input/tests/MotionPredictorMetricsManager_test.cpp @@ -238,14 +238,17 @@ TEST(MakeMotionEventTest, MakeLiftMotionEvent) { // --- 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 @@ TEST(GenerateConstantGroundTruthPointsTest, BasicTest) { 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 @@ TEST(GenerateConstantGroundTruthPointsTest, BasicTest) { // 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 @@ TEST(GenerateCircularArcGroundTruthTest, CounterclockwiseSquare) { // --- 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 @@ std::vector<PredictionPoint> generatePredictionsByLinearExtrapolation( 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 @@ TEST(GeneratePredictionsTest, GenerateConstantPredictions) { 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 @@ ReportAtomFunction createMockReportAtomFunction(std::vector<AtomFields>& reporte // • 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 @@ void runMetricsManager(const std::vector<GroundTruthPoint>& groundTruthPoints, 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 @@ TEST(MotionPredictorMetricsManagerTest, NoPredictions) { // 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 @@ TEST(MotionPredictorMetricsManagerTest, CounterclockwiseOctagonGroundTruthLinear } } +// 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 343d81f917..0000000000 --- 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 1cb7f7ba8c..0b0bb63a81 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 6119ba7f68..c630d972b2 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 8f005a56f8..bed31e27a8 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -148,29 +148,31 @@ AChoreographer* AChoreographer_getInstance() { 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 eb7a9d5bfa..feb76a1ccd 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> @@ -766,8 +767,7 @@ void SkiaVkRenderEngine::waitFence(GrDirectContext* grContext, base::borrowed_fd 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 */); } @@ -775,8 +775,7 @@ base::unique_fd SkiaVkRenderEngine::flushAndSubmit(GrDirectContext* grContext) { 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/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index 3ac4285304..9db3574389 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 isHoverAction(int32_t action) { 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 @@ PointerChoreographer::PointerChoreographer(InputListenerInterface& listener, mStylusPointerIconEnabled(false) {} void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) { - std::scoped_lock _l(mLock); + PointerDisplayChange pointerDisplayChange; + + { // acquire lock + std::scoped_lock _l(mLock); + + mInputDeviceInfos = args.inputDeviceInfos; + pointerDisplayChange = updatePointerControllersLocked(); + } // release lock - mInputDeviceInfos = args.inputDeviceInfos; - updatePointerControllersLocked(); + notifyPointerDisplayChange(pointerDisplayChange, mPolicy); mNextListener.notify(args); } @@ -104,7 +121,7 @@ NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotio << 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::processMouseEventLocked(const NotifyMotio } 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 @@ int32_t PointerChoreographer::getTargetMouseDisplayLocked(int32_t associatedDisp 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 @@ bool PointerChoreographer::canUnfadeOnDisplay(int32_t displayId) { 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 @@ void PointerChoreographer::updatePointerControllersLocked() { 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 @@ void PointerChoreographer::notifyPointerDisplayIdChangedLocked() { 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 @@ FloatPoint PointerChoreographer::getMouseCursorPosition(int32_t displayId) { } 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 6aab3aade0..db1488b546 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -109,11 +109,13 @@ public: 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 1298b5d511..06d5c7dd4b 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -140,6 +140,7 @@ struct KeyEntry : EventEntry { 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 057b996a23..73bbed6c68 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -676,13 +676,13 @@ std::optional<nsecs_t> getDownTime(const EventEntry& eventEntry) { 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]; @@ -2651,19 +2651,14 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( { 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); } } @@ -4815,6 +4810,10 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev pointerCount)); transformMotionEntryForInjectionLocked(*nextInjectedEntry, motionEvent.getTransform()); + if (mTracer) { + nextInjectedEntry->traceTracker = + mTracer->traceInboundEvent(*nextInjectedEntry); + } injectedEntries.push(std::move(nextInjectedEntry)); } break; diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp index 0be64e67ca..10f6b0f768 100644 --- a/services/inputflinger/dispatcher/trace/InputTracer.cpp +++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp @@ -107,6 +107,10 @@ void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) { 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 +118,7 @@ void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry, 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); @@ -133,7 +138,7 @@ void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry, mBackend->traceWindowDispatch({std::move(traced), dispatchEntry.deliveryTime, dispatchEntry.resolvedFlags, dispatchEntry.targetUid, vsyncId, windowId, dispatchEntry.transform, dispatchEntry.rawTransform, - /*hmac=*/{}}); + /*hmac=*/{}, resolvedKeyRepeatCount}); } InputTracer::EventState& InputTracer::getState(const EventTrackerInterface& cookie) { diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h index b0eadfe51f..94a86b94a2 100644 --- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h +++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h @@ -98,6 +98,7 @@ public: 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 8b47b555e5..462aedc539 100644 --- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h +++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h @@ -25,6 +25,9 @@ namespace android { * * 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 @@ public: * 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 4655ee8458..08738e3973 100644 --- a/services/inputflinger/tests/FakeInputTracingBackend.cpp +++ b/services/inputflinger/tests/FakeInputTracingBackend.cpp @@ -56,8 +56,8 @@ KeyEvent toInputEvent(const trace::TracedKeyEvent& e, 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/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index ffd3463296..ad59f1a574 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -834,13 +834,16 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi 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 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi 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 @@ FpsRange RefreshRateSelector::getFrameRateCategoryRange(FrameRateCategory catego 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/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index 0a6e3054dd..fe0e3d11ae 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -259,6 +259,44 @@ protected: 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_withFrameRateCategory_30_60 } } +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 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_HighH 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 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_HighH 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 @@ TEST_P(RefreshRateSelectorTest, // 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}, }; |