diff options
52 files changed, 2194 insertions, 1709 deletions
diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS index 643b2c2d11..e9fb85ba60 100644 --- a/cmds/installd/OWNERS +++ b/cmds/installd/OWNERS @@ -1,11 +1,10 @@ set noparent -calin@google.com jsharkey@android.com maco@google.com mast@google.com +jiakaiz@google.com narayan@google.com ngeoffray@google.com rpl@google.com -toddke@google.com patb@google.com diff --git a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h index 8d2944afd8..f178027b35 100644 --- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h +++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h @@ -31,11 +31,7 @@ namespace aidl::android::os { */ class PersistableBundle { public: - PersistableBundle() noexcept { - if (__builtin_available(android __ANDROID_API_V__, *)) { - mPBundle = APersistableBundle_new(); - } - } + PersistableBundle() noexcept : mPBundle(APersistableBundle_new()) {} // takes ownership of the APersistableBundle* PersistableBundle(APersistableBundle* _Nonnull bundle) noexcept : mPBundle(bundle) {} // takes ownership of the APersistableBundle* @@ -331,32 +327,20 @@ class PersistableBundle { } bool getBooleanVector(const std::string& key, std::vector<bool>* _Nonnull vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { - return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(), - vec); - } - return false; + return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(), + vec); } bool getIntVector(const std::string& key, std::vector<int32_t>* _Nonnull vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { - return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(), + return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(), vec); - } - return false; } bool getLongVector(const std::string& key, std::vector<int64_t>* _Nonnull vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { - return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(), + return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(), vec); - } - return false; } bool getDoubleVector(const std::string& key, std::vector<double>* _Nonnull vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { - return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle, key.c_str(), + return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle, key.c_str(), vec); - } - return false; } // Takes ownership of and frees the char** and its elements. @@ -377,17 +361,15 @@ class PersistableBundle { } bool getStringVector(const std::string& key, std::vector<std::string>* _Nonnull vec) { - if (__builtin_available(android __ANDROID_API_V__, *)) { - int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0, - &stringAllocator, nullptr); - if (bytes > 0) { - char** strings = (char**)malloc(bytes); - if (strings) { - bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), strings, bytes, - &stringAllocator, nullptr); - *vec = moveStringsInternal<std::vector<std::string>>(strings, bytes); - return true; - } + int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0, + &stringAllocator, nullptr); + if (bytes > 0) { + char** strings = (char**)malloc(bytes); + if (strings) { + bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), strings, bytes, + &stringAllocator, nullptr); + *vec = moveStringsInternal<std::vector<std::string>>(strings, bytes); + return true; } } return false; diff --git a/libs/fakeservicemanager/FakeServiceManager.cpp b/libs/fakeservicemanager/FakeServiceManager.cpp index ae242f32a3..08f30de637 100644 --- a/libs/fakeservicemanager/FakeServiceManager.cpp +++ b/libs/fakeservicemanager/FakeServiceManager.cpp @@ -122,9 +122,19 @@ std::vector<IServiceManager::ServiceDebugInfo> FakeServiceManager::getServiceDeb } void FakeServiceManager::clear() { - std::lock_guard<std::mutex> l(mMutex); + std::map<String16, sp<IBinder>> backup; + + { + std::lock_guard<std::mutex> l(mMutex); + backup = mNameToService; + mNameToService.clear(); + } + + // destructors may access FSM, so avoid recursive lock + backup.clear(); // explicit - mNameToService.clear(); + // TODO: destructors may have added more services here - may want + // to check this or abort } } // namespace android @@ -147,4 +157,4 @@ void clearFakeServiceManager() { LOG_ALWAYS_FATAL_IF(gFakeServiceManager == nullptr, "Fake Service Manager is not available. Forgot to call setupFakeServiceManager?"); gFakeServiceManager->clear(); } -} //extern "C"
\ No newline at end of file +} //extern "C" diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp index c6f33639c7..7d37fd37cb 100644 --- a/libs/gui/Choreographer.cpp +++ b/libs/gui/Choreographer.cpp @@ -344,8 +344,11 @@ void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) { handleRefreshRateUpdates(); } -void Choreographer::dispatchHdcpLevelsChanged(PhysicalDisplayId, int32_t, int32_t) { - LOG_ALWAYS_FATAL("dispatchHdcpLevelsChanged was called but was never registered"); +void Choreographer::dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, + int32_t maxLevel) { + ALOGV("choreographer %p ~ received hdcp levels change event (displayId=%s, connectedLevel=%d, " + "maxLevel=%d), ignoring.", + this, to_string(displayId).c_str(), connectedLevel, maxLevel); } void Choreographer::handleMessage(const Message& message) { diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index a185a59fca..8bc1292986 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -2,11 +2,11 @@ LIBNATIVEWINDOW { global: AHardwareBuffer_acquire; AHardwareBuffer_allocate; - AHardwareBuffer_allocate2; # llndk # systemapi - AHardwareBuffer_createFromHandle; # llndk # systemapi + AHardwareBuffer_allocate2; # llndk systemapi + AHardwareBuffer_createFromHandle; # llndk systemapi AHardwareBuffer_describe; AHardwareBuffer_getId; # introduced=31 - AHardwareBuffer_getNativeHandle; # llndk # systemapi + AHardwareBuffer_getNativeHandle; # llndk systemapi AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; AHardwareBuffer_lockAndGetInfo; # introduced=29 @@ -17,8 +17,8 @@ LIBNATIVEWINDOW { AHardwareBuffer_unlock; AHardwareBuffer_readFromParcel; # introduced=34 AHardwareBuffer_writeToParcel; # introduced=34 - AHardwareBuffer_getDataSpace; # llndk # systemapi - AHardwareBuffer_setDataSpace; # llndk # systemapi + AHardwareBuffer_getDataSpace; # llndk systemapi + AHardwareBuffer_setDataSpace; # llndk systemapi ANativeWindowBuffer_getHardwareBuffer; # llndk ANativeWindow_OemStorageGet; # llndk ANativeWindow_OemStorageSet; # llndk @@ -29,18 +29,18 @@ LIBNATIVEWINDOW { ANativeWindow_getBuffersDefaultDataSpace; # introduced=34 ANativeWindow_getFormat; ANativeWindow_getHeight; - ANativeWindow_getLastDequeueDuration; # systemapi # introduced=30 - ANativeWindow_getLastDequeueStartTime; # systemapi # introduced=30 - ANativeWindow_getLastQueueDuration; # systemapi # introduced=30 + ANativeWindow_getLastDequeueDuration; # systemapi introduced=30 + ANativeWindow_getLastDequeueStartTime; # systemapi introduced=30 + ANativeWindow_getLastQueueDuration; # systemapi introduced=30 ANativeWindow_getWidth; ANativeWindow_lock; ANativeWindow_query; # llndk ANativeWindow_queryf; # llndk ANativeWindow_queueBuffer; # llndk - ANativeWindow_setCancelBufferInterceptor; # systemapi # introduced=30 - ANativeWindow_setDequeueBufferInterceptor; # systemapi # introduced=30 - ANativeWindow_setPerformInterceptor; # systemapi # introduced=30 - ANativeWindow_setQueueBufferInterceptor; # systemapi # introduced=30 + ANativeWindow_setCancelBufferInterceptor; # systemapi introduced=30 + ANativeWindow_setDequeueBufferInterceptor; # systemapi introduced=30 + ANativeWindow_setPerformInterceptor; # systemapi introduced=30 + ANativeWindow_setQueueBufferInterceptor; # systemapi introduced=30 ANativeWindow_release; ANativeWindow_setAutoPrerotation; # llndk ANativeWindow_setAutoRefresh; # llndk @@ -51,7 +51,7 @@ LIBNATIVEWINDOW { ANativeWindow_setBuffersGeometry; ANativeWindow_setBuffersTimestamp; # llndk ANativeWindow_setBuffersTransform; - ANativeWindow_setDequeueTimeout; # systemapi # introduced=30 + ANativeWindow_setDequeueTimeout; # systemapi introduced=30 ANativeWindow_setFrameRate; # introduced=30 ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31 ANativeWindow_setSharedBufferMode; # llndk diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index 367bee89f9..f58f543854 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -125,8 +125,10 @@ void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_S } void RenderEngineThreaded::waitUntilInitialized() const { - std::unique_lock<std::mutex> lock(mInitializedMutex); - mInitializedCondition.wait(lock, [=] { return mIsInitialized; }); + if (!mIsInitialized) { + std::unique_lock<std::mutex> lock(mInitializedMutex); + mInitializedCondition.wait(lock, [this] { return mIsInitialized.load(); }); + } } std::future<void> RenderEngineThreaded::primeCache(bool shouldPrimeUltraHDR) { diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h index 74af2bd776..3f1e67f285 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -97,7 +97,7 @@ private: // Used to allow select thread safe methods to be accessed without requiring the // method to be invoked on the RenderEngine thread - bool mIsInitialized = false; + std::atomic_bool mIsInitialized = false; mutable std::mutex mInitializedMutex; mutable std::condition_variable mInitializedCondition; diff --git a/opengl/tests/gldual/AndroidManifest.xml b/opengl/tests/gldual/AndroidManifest.xml index a36f4f715e..d6335b0698 100644 --- a/opengl/tests/gldual/AndroidManifest.xml +++ b/opengl/tests/gldual/AndroidManifest.xml @@ -20,8 +20,9 @@ android:label="@string/gldual_activity"> <activity android:name="GLDualActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" - android:launchMode="singleTask" - android:configChanges="orientation|keyboardHidden"> + android:launchMode="singleTask" + android:configChanges="orientation|keyboardHidden" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp index 5d87d34adb..43ebc69c46 100644 --- a/services/inputflinger/InputFilter.cpp +++ b/services/inputflinger/InputFilter.cpp @@ -52,15 +52,15 @@ InputFilter::InputFilter(InputListenerInterface& listener, IInputFlingerRust& ru } void InputFilter::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) { + mDeviceInfos.clear(); + mDeviceInfos.reserve(args.inputDeviceInfos.size()); + for (auto info : args.inputDeviceInfos) { + AidlDeviceInfo& aidlInfo = mDeviceInfos.emplace_back(); + aidlInfo.deviceId = info.getId(); + aidlInfo.external = info.isExternal(); + } if (isFilterEnabled()) { - std::vector<AidlDeviceInfo> deviceInfos; - for (auto info : args.inputDeviceInfos) { - AidlDeviceInfo aidlInfo; - aidlInfo.deviceId = info.getId(); - aidlInfo.external = info.isExternal(); - deviceInfos.push_back(aidlInfo); - } - LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(deviceInfos).isOk()); + LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(mDeviceInfos).isOk()); } mNextListener.notify(args); } @@ -74,7 +74,7 @@ void InputFilter::notifyKey(const NotifyKeyArgs& args) { LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk()); return; } - mNextListener.notifyKey(args); + mNextListener.notify(args); } void InputFilter::notifyMotion(const NotifyMotionArgs& args) { @@ -112,7 +112,7 @@ void InputFilter::setAccessibilityBounceKeysThreshold(nsecs_t threshold) { if (mConfig.bounceKeysThresholdNs != threshold) { mConfig.bounceKeysThresholdNs = threshold; - LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk()); + notifyConfigurationChangedLocked(); } } @@ -121,7 +121,14 @@ void InputFilter::setAccessibilityStickyKeysEnabled(bool enabled) { if (mConfig.stickyKeysEnabled != enabled) { mConfig.stickyKeysEnabled = enabled; - LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk()); + notifyConfigurationChangedLocked(); + } +} + +void InputFilter::notifyConfigurationChangedLocked() { + LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk()); + if (isFilterEnabled()) { + LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(mDeviceInfos).isOk()); } } diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h index 9fa7a8758f..a38fbf6f61 100644 --- a/services/inputflinger/InputFilter.h +++ b/services/inputflinger/InputFilter.h @@ -45,6 +45,7 @@ public: aidl::com::android::server::inputflinger::IInputFilter::IInputFilterCallbacks; using InputFilterConfiguration = aidl::com::android::server::inputflinger::InputFilterConfiguration; + using AidlDeviceInfo = aidl::com::android::server::inputflinger::DeviceInfo; explicit InputFilter(InputListenerInterface& listener, IInputFlingerRust&); ~InputFilter() override = default; @@ -65,10 +66,14 @@ private: InputListenerInterface& mNextListener; std::shared_ptr<InputFilterCallbacks> mCallbacks; std::shared_ptr<IInputFilter> mInputFilterRust; + // Keep track of connected peripherals, so that if filters are enabled later, we can pass that + // info to the filters + std::vector<AidlDeviceInfo> mDeviceInfos; mutable std::mutex mLock; InputFilterConfiguration mConfig GUARDED_BY(mLock); bool isFilterEnabled(); + void notifyConfigurationChangedLocked() REQUIRES(mLock); }; } // namespace android diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 112f16b38a..162a7bd6c5 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -835,7 +835,7 @@ void InputDispatcher::dispatchOnce() { // Run a dispatch loop if there are no pending commands. // The dispatch loop might enqueue commands to run afterwards. if (!haveCommandsLocked()) { - dispatchOnceInnerLocked(&nextWakeupTime); + dispatchOnceInnerLocked(/*byref*/ nextWakeupTime); } // Run all pending commands if there are any. @@ -942,7 +942,7 @@ std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked( return DEFAULT_INPUT_DISPATCHING_TIMEOUT; } -void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { +void InputDispatcher::dispatchOnceInnerLocked(nsecs_t& nextWakeupTime) { nsecs_t currentTime = now(); // Reset the key repeat timer whenever normal dispatch is suspended while the @@ -966,9 +966,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { bool isAppSwitchDue; if (!REMOVE_APP_SWITCH_DROPS) { isAppSwitchDue = mAppSwitchDueTime <= currentTime; - if (mAppSwitchDueTime < *nextWakeupTime) { - *nextWakeupTime = mAppSwitchDueTime; - } + nextWakeupTime = std::min(nextWakeupTime, mAppSwitchDueTime); } // Ready to start a new event. @@ -989,9 +987,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { if (currentTime >= mKeyRepeatState.nextRepeatTime) { mPendingEvent = synthesizeKeyRepeatLocked(currentTime); } else { - if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) { - *nextWakeupTime = mKeyRepeatState.nextRepeatTime; - } + nextWakeupTime = std::min(nextWakeupTime, mKeyRepeatState.nextRepeatTime); } } @@ -1121,7 +1117,10 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } } if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) { - dropReason = DropReason::BLOCKED; + if (!isFromSource(motionEntry->source, AINPUT_SOURCE_CLASS_POINTER)) { + // Only drop events that are focus-dispatched. + dropReason = DropReason::BLOCKED; + } } done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime); break; @@ -1154,7 +1153,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { mLastDropReason = dropReason; releasePendingEventLocked(); - *nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately + nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately } } @@ -1392,8 +1391,9 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason reason = "inbound event was dropped because of pending overdue app switch"; break; case DropReason::BLOCKED: - ALOGI("Dropped event because the current application is not responding and the user " - "has started interacting with a different application."); + LOG(INFO) << "Dropping because the current application is not responding and the user " + "has started interacting with a different application: " + << entry.getDescription(); reason = "inbound event was dropped because the current application is not responding " "and the user has started interacting with a different application"; break; @@ -1414,6 +1414,9 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason switch (entry.type) { case EventEntry::Type::KEY: { CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, reason); + const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry); + options.displayId = keyEntry.displayId; + options.deviceId = keyEntry.deviceId; synthesizeCancelationEventsForAllConnectionsLocked(options); break; } @@ -1421,10 +1424,14 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry); if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) { CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, reason); + options.displayId = motionEntry.displayId; + options.deviceId = motionEntry.deviceId; synthesizeCancelationEventsForAllConnectionsLocked(options); } else { CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, reason); + options.displayId = motionEntry.displayId; + options.deviceId = motionEntry.deviceId; synthesizeCancelationEventsForAllConnectionsLocked(options); } break; @@ -1738,7 +1745,7 @@ std::vector<InputTarget> InputDispatcher::getInputTargetsFromWindowHandlesLocked } bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) { + DropReason* dropReason, nsecs_t& nextWakeupTime) { // Preprocessing. if (!entry->dispatchInProgress) { if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN && @@ -1789,9 +1796,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<con // Handle case where the policy asked us to try again later last time. if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) { if (currentTime < entry->interceptKeyWakeupTime) { - if (entry->interceptKeyWakeupTime < *nextWakeupTime) { - *nextWakeupTime = entry->interceptKeyWakeupTime; - } + nextWakeupTime = std::min(nextWakeupTime, entry->interceptKeyWakeupTime); return false; // wait until next wakeup } entry->interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN; @@ -1871,7 +1876,7 @@ void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<const SensorEntry>& entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) { + DropReason* dropReason, nsecs_t& nextWakeupTime) { if (DEBUG_OUTBOUND_EVENT_DETAILS) { ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, " "source=0x%x, sensorType=%s", @@ -1911,7 +1916,7 @@ bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<const MotionEntry> entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) { + DropReason* dropReason, nsecs_t& nextWakeupTime) { ATRACE_CALL(); // Preprocessing. if (!entry->dispatchInProgress) { @@ -2156,7 +2161,7 @@ bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime, } sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( - nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime, + nsecs_t currentTime, const EventEntry& entry, nsecs_t& nextWakeupTime, InputEventInjectionResult& outInjectionResult) { outInjectionResult = InputEventInjectionResult::FAILED; // Default result @@ -2195,7 +2200,7 @@ sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( ALOGW("Waiting because no window has focus but %s may eventually add a " "window when it finishes starting up. Will wait for %" PRId64 "ms", mAwaitedFocusedApplication->getName().c_str(), millis(timeout)); - *nextWakeupTime = *mNoFocusedWindowTimeoutTime; + nextWakeupTime = std::min(nextWakeupTime, *mNoFocusedWindowTimeoutTime); outInjectionResult = InputEventInjectionResult::PENDING; return nullptr; } else if (currentTime > *mNoFocusedWindowTimeoutTime) { @@ -2240,7 +2245,7 @@ sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( // prior input events. if (entry.type == EventEntry::Type::KEY) { if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) { - *nextWakeupTime = *mKeyIsWaitingForEventsTimeout; + nextWakeupTime = std::min(nextWakeupTime, *mKeyIsWaitingForEventsTimeout); outInjectionResult = InputEventInjectionResult::PENDING; return nullptr; } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 010dbb28e4..3567288f1d 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -209,7 +209,7 @@ private: // This method should only be called on the input dispatcher's own thread. void dispatchOnce(); - void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock); + void dispatchOnceInnerLocked(nsecs_t& nextWakeupTime) REQUIRES(mLock); // Enqueues an inbound event. Returns true if mLooper->wake() should be called. bool enqueueInboundEventLocked(std::unique_ptr<EventEntry> entry) REQUIRES(mLock); @@ -435,9 +435,9 @@ private: bool dispatchDeviceResetLocked(nsecs_t currentTime, const DeviceResetEntry& entry) REQUIRES(mLock); bool dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); + DropReason* dropReason, nsecs_t& nextWakeupTime) REQUIRES(mLock); bool dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<const MotionEntry> entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); + DropReason* dropReason, nsecs_t& nextWakeupTime) REQUIRES(mLock); void dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<const FocusEntry> entry) REQUIRES(mLock); void dispatchPointerCaptureChangedLocked( @@ -449,7 +449,7 @@ private: void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<const EventEntry> entry, const std::vector<InputTarget>& inputTargets) REQUIRES(mLock); void dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<const SensorEntry>& entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); + DropReason* dropReason, nsecs_t& nextWakeupTime) REQUIRES(mLock); void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<const DragEntry> entry) REQUIRES(mLock); void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry); @@ -521,7 +521,7 @@ private: int32_t getTargetDisplayId(const EventEntry& entry); sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked( - nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime, + nsecs_t currentTime, const EventEntry& entry, nsecs_t& nextWakeupTime, android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock); std::vector<InputTarget> findTouchedWindowTargetsLocked( nsecs_t currentTime, const MotionEntry& entry, diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp index 01e983a371..19788cee40 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -81,6 +81,7 @@ std::string GestureConverter::dump() const { out << StringPrintf("Button state: 0x%08x\n", mButtonState); out << "Down time: " << mDownTime << "\n"; out << "Current classification: " << ftl::enum_string(mCurrentClassification) << "\n"; + out << "Is hovering: " << mIsHovering << "\n"; out << "Enable Tap Timestamp: " << mWhenToEnableTapToClick << "\n"; return out.str(); } @@ -89,7 +90,7 @@ std::list<NotifyArgs> GestureConverter::reset(nsecs_t when) { std::list<NotifyArgs> out; switch (mCurrentClassification) { case MotionClassification::TWO_FINGER_SWIPE: - out.push_back(endScroll(when, when)); + out += endScroll(when, when); break; case MotionClassification::MULTI_FINGER_SWIPE: out += handleMultiFingerSwipeLift(when, when); @@ -173,6 +174,8 @@ std::list<NotifyArgs> GestureConverter::handleMove(nsecs_t when, nsecs_t readTim const Gesture& gesture) { float deltaX = gesture.details.move.dx; float deltaY = gesture.details.move.dy; + const auto [oldXCursorPosition, oldYCursorPosition] = + mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); if (ENABLE_TOUCHPAD_PALM_REJECTION_V2) { bool wasHoverCancelled = mIsHoverCancelled; // Gesture will be cancelled if it started before the user started typing and @@ -184,6 +187,7 @@ std::list<NotifyArgs> GestureConverter::handleMove(nsecs_t when, nsecs_t readTim // This is the first event of the cancelled gesture, we won't return because we need to // generate a HOVER_EXIT event mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); + return exitHover(when, readTime, oldXCursorPosition, oldYCursorPosition); } else if (mIsHoverCancelled) { return {}; } @@ -204,24 +208,27 @@ std::list<NotifyArgs> GestureConverter::handleMove(nsecs_t when, nsecs_t readTim mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); } - const auto [xCursorPosition, yCursorPosition] = + std::list<NotifyArgs> out; + const bool down = isPointerDown(mButtonState); + if (!down) { + out += enterHover(when, readTime, oldXCursorPosition, oldYCursorPosition); + } + const auto [newXCursorPosition, newYCursorPosition] = mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); PointerCoords coords; coords.clear(); - coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); - coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_X, newXCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, newYCursorPosition); coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); - const bool down = isPointerDown(mButtonState); coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); - const int32_t action = mIsHoverCancelled - ? AMOTION_EVENT_ACTION_HOVER_EXIT - : (down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE); - return {makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState, - /* pointerCount= */ 1, mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition)}; + const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE; + out.push_back(makeMotionArgs(when, readTime, action, /*actionButton=*/0, mButtonState, + /*pointerCount=*/1, &coords, newXCursorPosition, + newYCursorPosition)); + return out; } std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_t readTime, @@ -270,16 +277,16 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ newButtonState |= actionButton; pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, newButtonState, - /* pointerCount= */ 1, mFingerProps.data(), - &coords, xCursorPosition, yCursorPosition)); + /*pointerCount=*/1, &coords, xCursorPosition, + yCursorPosition)); } } if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) { mDownTime = when; + out += exitHover(when, readTime, xCursorPosition, yCursorPosition); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1, - mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition)); + &coords, xCursorPosition, yCursorPosition)); } out.splice(out.end(), pressEvents); @@ -295,20 +302,16 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ newButtonState &= ~actionButton; out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, newButtonState, /* pointerCount= */ 1, - mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition)); + &coords, xCursorPosition, yCursorPosition)); } } if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) { coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0, - newButtonState, /* pointerCount= */ 1, mFingerProps.data(), - &coords, xCursorPosition, yCursorPosition)); - // Send a HOVER_MOVE to tell the application that the mouse is hovering again. - out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_HOVER_MOVE, - /*actionButton=*/0, newButtonState, /*pointerCount=*/1, - mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition)); + newButtonState, /* pointerCount= */ 1, &coords, + xCursorPosition, yCursorPosition)); + mButtonState = newButtonState; + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); } mButtonState = newButtonState; return out; @@ -333,18 +336,18 @@ std::list<NotifyArgs> GestureConverter::releaseAllButtons(nsecs_t when, nsecs_t if (mButtonState & button) { newButtonState &= ~button; out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE, - button, newButtonState, /*pointerCount=*/1, - mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition)); + button, newButtonState, /*pointerCount=*/1, &coords, + xCursorPosition, yCursorPosition)); } } + mButtonState = 0; if (pointerDown) { coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0, - newButtonState, /*pointerCount=*/1, mFingerProps.data(), - &coords, xCursorPosition, yCursorPosition)); + mButtonState, /*pointerCount=*/1, &coords, xCursorPosition, + yCursorPosition)); + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); } - mButtonState = 0; return out; } @@ -355,6 +358,8 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT const auto [xCursorPosition, yCursorPosition] = mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) { + out += exitHover(when, readTime, xCursorPosition, yCursorPosition); + mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE; coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); @@ -362,8 +367,8 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT mDownTime = when; NotifyMotionArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0, - mButtonState, /* pointerCount= */ 1, mFingerProps.data(), - mFakeFingerCoords.data(), xCursorPosition, yCursorPosition); + mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition); args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; out.push_back(args); } @@ -378,8 +383,8 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, -gesture.details.scroll.dy); NotifyMotionArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0, - mButtonState, /* pointerCount= */ 1, mFingerProps.data(), - mFakeFingerCoords.data(), xCursorPosition, yCursorPosition); + mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition); args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; out.push_back(args); return out; @@ -395,7 +400,7 @@ std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTi // ensure consistency between touchscreen and touchpad flings), so we're just using // the "start fling" gestures as a marker for the end of a two-finger scroll // gesture. - return {endScroll(when, readTime)}; + return endScroll(when, readTime); } break; case GESTURES_FLING_TAP_DOWN: @@ -418,18 +423,21 @@ std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTi return {}; } -NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { +std::list<NotifyArgs> GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { + std::list<NotifyArgs> out; const auto [xCursorPosition, yCursorPosition] = mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0); NotifyMotionArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0, - mButtonState, /* pointerCount= */ 1, mFingerProps.data(), - mFakeFingerCoords.data(), xCursorPosition, yCursorPosition); + mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition); args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; + out.push_back(args); mCurrentClassification = MotionClassification::NONE; - return args; + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); + return out; } [[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipe(nsecs_t when, @@ -445,7 +453,11 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { // three and then put a fourth finger down), the gesture library will treat it as two // separate swipes with an appropriate lift event between them, so we don't have to worry // about the finger count changing mid-swipe. + + out += exitHover(when, readTime, xCursorPosition, yCursorPosition); + mCurrentClassification = MotionClassification::MULTI_FINGER_SWIPE; + mSwipeFingerCount = fingerCount; constexpr float FAKE_FINGER_SPACING = 100; @@ -464,16 +476,14 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { fingerCount); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); for (size_t i = 1; i < mSwipeFingerCount; i++) { out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_POINTER_DOWN | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), /* actionButton= */ 0, mButtonState, - /* pointerCount= */ i + 1, mFingerProps.data(), - mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + /* pointerCount= */ i + 1, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition)); } } float rotatedDeltaX = dx, rotatedDeltaY = -dy; @@ -491,8 +501,7 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0, mButtonState, /* pointerCount= */ mSwipeFingerCount, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); return out; } @@ -512,15 +521,14 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { AMOTION_EVENT_ACTION_POINTER_UP | ((i - 1) << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), /* actionButton= */ 0, mButtonState, /* pointerCount= */ i, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); } out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT, 0); mCurrentClassification = MotionClassification::NONE; + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); mSwipeFingerCount = 0; return out; } @@ -539,6 +547,10 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { LOG_ALWAYS_FATAL_IF(gesture.details.pinch.zoom_state != GESTURES_ZOOM_START, "First pinch gesture does not have the START zoom state (%d instead).", gesture.details.pinch.zoom_state); + std::list<NotifyArgs> out; + + out += exitHover(when, readTime, xCursorPosition, yCursorPosition); + mCurrentClassification = MotionClassification::PINCH; mPinchFingerSeparation = INITIAL_PINCH_SEPARATION_PX; mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0); @@ -551,17 +563,14 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); mDownTime = when; - std::list<NotifyArgs> out; out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_POINTER_DOWN | 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 2, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); return out; } @@ -579,8 +588,8 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { xCursorPosition + mPinchFingerSeparation / 2); mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); return {makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0, - mButtonState, /*pointerCount=*/2, mFingerProps.data(), - mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)}; + mButtonState, /*pointerCount=*/2, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition)}; } std::list<NotifyArgs> GestureConverter::endPinch(nsecs_t when, nsecs_t readTime) { @@ -593,20 +602,53 @@ std::list<NotifyArgs> GestureConverter::endPinch(nsecs_t when, nsecs_t readTime) AMOTION_EVENT_ACTION_POINTER_UP | 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT, /*actionButton=*/0, mButtonState, /*pointerCount=*/2, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); - out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0, - mButtonState, /*pointerCount=*/1, mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); - mCurrentClassification = MotionClassification::NONE; + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0, + mButtonState, /*pointerCount=*/1, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition)); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 0); + mCurrentClassification = MotionClassification::NONE; + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); return out; } +std::list<NotifyArgs> GestureConverter::enterHover(nsecs_t when, nsecs_t readTime, + float xCursorPosition, float yCursorPosition) { + if (!mIsHovering) { + mIsHovering = true; + return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_ENTER, xCursorPosition, + yCursorPosition)}; + } else { + return {}; + } +} + +std::list<NotifyArgs> GestureConverter::exitHover(nsecs_t when, nsecs_t readTime, + float xCursorPosition, float yCursorPosition) { + if (mIsHovering) { + mIsHovering = false; + return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_EXIT, xCursorPosition, + yCursorPosition)}; + } else { + return {}; + } +} + +NotifyMotionArgs GestureConverter::makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action, + float xCursorPosition, float yCursorPosition) { + PointerCoords coords; + coords.clear(); + coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0); + return makeMotionArgs(when, readTime, action, /*actionButton=*/0, mButtonState, + /*pointerCount=*/1, &coords, xCursorPosition, yCursorPosition); +} + NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action, int32_t actionButton, int32_t buttonState, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, float xCursorPosition, float yCursorPosition) { return {mReaderContext.getNextId(), @@ -624,7 +666,7 @@ NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime mCurrentClassification, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, - pointerProperties, + mFingerProps.data(), pointerCoords, /* xPrecision= */ 1.0f, /* yPrecision= */ 1.0f, diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h index 88e7b99877..07cc56c618 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h @@ -75,7 +75,7 @@ private: [[nodiscard]] std::list<NotifyArgs> handleFling(nsecs_t when, nsecs_t readTime, nsecs_t gestureStartTime, const Gesture& gesture); - [[nodiscard]] NotifyMotionArgs endScroll(nsecs_t when, nsecs_t readTime); + [[nodiscard]] std::list<NotifyArgs> endScroll(nsecs_t when, nsecs_t readTime); [[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipe(nsecs_t when, nsecs_t readTime, uint32_t fingerCount, float dx, @@ -85,12 +85,18 @@ private: const Gesture& gesture); [[nodiscard]] std::list<NotifyArgs> endPinch(nsecs_t when, nsecs_t readTime); + [[nodiscard]] std::list<NotifyArgs> enterHover(nsecs_t when, nsecs_t readTime, + float xCursorPosition, float yCursorPosition); + [[nodiscard]] std::list<NotifyArgs> exitHover(nsecs_t when, nsecs_t readTime, + float xCursorPosition, float yCursorPosition); + + NotifyMotionArgs makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action, + float xCursorPosition, float yCursorPosition); + NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action, int32_t actionButton, int32_t buttonState, - uint32_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords, float xCursorPosition, - float yCursorPosition); + uint32_t pointerCount, const PointerCoords* pointerCoords, + float xCursorPosition, float yCursorPosition); void enableTapToClick(nsecs_t when); bool mIsHoverCancelled{false}; @@ -111,6 +117,9 @@ private: // button values (AMOTION_EVENT_BUTTON_...). uint32_t mButtonState = 0; nsecs_t mDownTime = 0; + // Whether we are currently in a hover state (i.e. a HOVER_ENTER event has been sent without a + // matching HOVER_EXIT). + bool mIsHovering = false; MotionClassification mCurrentClassification = MotionClassification::NONE; // Only used when mCurrentClassification is MULTI_FINGER_SWIPE. diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp index 6d6b7d821c..f3a6f01014 100644 --- a/services/inputflinger/tests/CursorInputMapper_test.cpp +++ b/services/inputflinger/tests/CursorInputMapper_test.cpp @@ -16,14 +16,24 @@ #include "CursorInputMapper.h" +#include <list> +#include <string> +#include <tuple> +#include <variant> + #include <android-base/logging.h> #include <com_android_input_flags.h> #include <gtest/gtest.h> +#include <linux/input-event-codes.h> +#include <linux/input.h> +#include <utils/Timers.h> #include "FakePointerController.h" #include "InputMapperTest.h" #include "InterfaceMocks.h" +#include "NotifyArgs.h" #include "TestEventMatchers.h" +#include "ui/Rotation.h" #define TAG "CursorInputMapper_test" @@ -40,23 +50,29 @@ constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE; constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE; constexpr auto INVALID_CURSOR_POSITION = AMOTION_EVENT_INVALID_CURSOR_POSITION; constexpr int32_t DISPLAY_ID = 0; +constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1; constexpr int32_t DISPLAY_WIDTH = 480; constexpr int32_t DISPLAY_HEIGHT = 800; constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified +constexpr int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; + namespace input_flags = com::android::input::flags; /** * Unit tests for CursorInputMapper. - * This class is named 'CursorInputMapperUnitTest' to avoid name collision with the existing - * 'CursorInputMapperTest'. If all of the CursorInputMapper tests are migrated here, the name - * can be simplified to 'CursorInputMapperTest'. - * TODO(b/283812079): move CursorInputMapper tests here. + * These classes are named 'CursorInputMapperUnitTest...' to avoid name collision with the existing + * 'CursorInputMapperTest...' classes. If all of the CursorInputMapper tests are migrated here, the + * name can be simplified to 'CursorInputMapperTest'. + * + * TODO(b/283812079): move the remaining CursorInputMapper tests here. The ones that are left all + * depend on viewport association, for which we'll need to fake InputDeviceContext. */ -class CursorInputMapperUnitTest : public InputMapperUnitTest { +class CursorInputMapperUnitTestBase : public InputMapperUnitTest { protected: - void SetUp() override { - InputMapperUnitTest::SetUp(); + void SetUp() override { SetUpWithBus(BUS_USB); } + void SetUpWithBus(int bus) override { + InputMapperUnitTest::SetUpWithBus(bus); // Current scan code state - all keys are UP by default setScanCodeState(KeyState::UP, @@ -71,7 +87,10 @@ protected: mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); + } + void createMapper() { + createDevice(); mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration); } @@ -83,19 +102,42 @@ protected: mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, InputReaderConfiguration::Change::POINTER_CAPTURE); ASSERT_THAT(args, - ElementsAre( - VariantWith<NotifyDeviceResetArgs>(AllOf(WithDeviceId(DEVICE_ID))))); + ElementsAre(VariantWith<NotifyDeviceResetArgs>( + AllOf(WithDeviceId(DEVICE_ID), WithEventTime(ARBITRARY_TIME))))); // Check that generation also got bumped ASSERT_GT(mDevice->getGeneration(), generation); } }; +class CursorInputMapperUnitTest : public CursorInputMapperUnitTestBase { +protected: + void SetUp() override { + input_flags::enable_pointer_choreographer(false); + CursorInputMapperUnitTestBase::SetUp(); + } +}; + +TEST_F(CursorInputMapperUnitTest, GetSourcesReturnsMouseInPointerMode) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + + ASSERT_EQ(AINPUT_SOURCE_MOUSE, mMapper->getSources()); +} + +TEST_F(CursorInputMapperUnitTest, GetSourcesReturnsTrackballInNavigationMode) { + mPropertyMap.addProperty("cursor.mode", "navigation"); + createMapper(); + + ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mMapper->getSources()); +} + /** * Move the mouse and then click the button. Check whether HOVER_EXIT is generated when hovering * ends. Currently, it is not. */ TEST_F(CursorInputMapperUnitTest, HoverAndLeftButtonPress) { + createMapper(); std::list<NotifyArgs> args; // Move the cursor a little @@ -139,6 +181,7 @@ TEST_F(CursorInputMapperUnitTest, HoverAndLeftButtonPress) { * When it's not SOURCE_MOUSE, CursorInputMapper doesn't populate cursor position values. */ TEST_F(CursorInputMapperUnitTest, ProcessPointerCapture) { + createMapper(); setPointerCapture(true); std::list<NotifyArgs> args; @@ -213,4 +256,978 @@ TEST_F(CursorInputMapperUnitTest, ProcessPointerCapture) { WithRelativeMotion(10.0f, 20.0f))))); } +TEST_F(CursorInputMapperUnitTest, + PopulateDeviceInfoReturnsRangeFromPointerControllerInPointerMode) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + mFakePolicy->clearViewports(); + mFakePointerController->clearBounds(); + createMapper(); + + InputDeviceInfo info; + mMapper->populateDeviceInfo(info); + + // Initially there should not be a valid motion range because there's no viewport or pointer + // bounds. + ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE)); + ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE, + AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); + + // When the bounds are set, then there should be a valid motion range. + mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1); + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); + std::list<NotifyArgs> args = + mMapper->reconfigure(systemTime(), mReaderConfiguration, + InputReaderConfiguration::Change::DISPLAY_INFO); + ASSERT_THAT(args, testing::IsEmpty()); + + InputDeviceInfo info2; + mMapper->populateDeviceInfo(info2); + + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, 1, + 800 - 1, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, 2, + 480 - 1, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE, + AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); +} + +TEST_F(CursorInputMapperUnitTest, PopulateDeviceInfoReturnsScaledRangeInNavigationMode) { + mPropertyMap.addProperty("cursor.mode", "navigation"); + createMapper(); + + InputDeviceInfo info; + mMapper->populateDeviceInfo(info); + + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_TRACKBALL, + -1.0f, 1.0f, 0.0f, + 1.0f / TRACKBALL_MOVEMENT_THRESHOLD)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_TRACKBALL, + -1.0f, 1.0f, 0.0f, + 1.0f / TRACKBALL_MOVEMENT_THRESHOLD)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE, + AINPUT_SOURCE_TRACKBALL, 0.0f, 1.0f, 0.0f, 0.0f)); +} + +TEST_F(CursorInputMapperUnitTest, ProcessShouldSetAllFieldsAndIncludeGlobalMetaState) { + mPropertyMap.addProperty("cursor.mode", "navigation"); + createMapper(); + + EXPECT_CALL(mMockInputReaderContext, getGlobalMetaState()) + .WillRepeatedly(Return(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON)); + + std::list<NotifyArgs> args; + + // Button press. + // Mostly testing non x/y behavior here so we don't need to check again elsewhere. + args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID), + WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), + WithEdgeFlags(0), WithPolicyFlags(0), + WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPointerCount(1), WithPointerId(0, 0), + WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f), + WithPressure(1.0f), + WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD, + TRACKBALL_MOVEMENT_THRESHOLD), + WithDownTime(ARBITRARY_TIME))), + VariantWith<NotifyMotionArgs>( + AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID), + WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), + WithEdgeFlags(0), WithPolicyFlags(0), + WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithPointerCount(1), WithPointerId(0, 0), + WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f), + WithPressure(1.0f), + WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD, + TRACKBALL_MOVEMENT_THRESHOLD), + WithDownTime(ARBITRARY_TIME))))); + args.clear(); + + // Button release. Should have same down time. + args += process(ARBITRARY_TIME + 1, EV_KEY, BTN_MOUSE, 0); + args += process(ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithEventTime(ARBITRARY_TIME + 1), + WithDeviceId(DEVICE_ID), + WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), + WithEdgeFlags(0), WithPolicyFlags(0), + WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON), + WithButtonState(0), WithPointerCount(1), + WithPointerId(0, 0), WithToolType(ToolType::MOUSE), + WithCoords(0.0f, 0.0f), WithPressure(0.0f), + WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD, + TRACKBALL_MOVEMENT_THRESHOLD), + WithDownTime(ARBITRARY_TIME))), + VariantWith<NotifyMotionArgs>( + AllOf(WithEventTime(ARBITRARY_TIME + 1), + WithDeviceId(DEVICE_ID), + WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), + WithEdgeFlags(0), WithPolicyFlags(0), + WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON), + WithButtonState(0), WithPointerCount(1), + WithPointerId(0, 0), WithToolType(ToolType::MOUSE), + WithCoords(0.0f, 0.0f), WithPressure(0.0f), + WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD, + TRACKBALL_MOVEMENT_THRESHOLD), + WithDownTime(ARBITRARY_TIME))))); +} + +TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleIndependentXYUpdates) { + mPropertyMap.addProperty("cursor.mode", "navigation"); + createMapper(); + + std::list<NotifyArgs> args; + + // Motion in X but not Y. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f), + WithPressure(0.0f))))); + args.clear(); + + // Motion in Y but not X. + args += process(ARBITRARY_TIME, EV_REL, REL_Y, -2); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD), + WithPressure(0.0f))))); + args.clear(); +} + +TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleIndependentButtonUpdates) { + mPropertyMap.addProperty("cursor.mode", "navigation"); + createMapper(); + + std::list<NotifyArgs> args; + + // Button press. + args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))))); + args.clear(); + + // Button release. + args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); +} + +TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleCombinedXYAndButtonUpdates) { + mPropertyMap.addProperty("cursor.mode", "navigation"); + createMapper(); + + std::list<NotifyArgs> args; + + // Combined X, Y and Button. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, -2); + args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD, + -2.0f / TRACKBALL_MOVEMENT_THRESHOLD), + WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD, + -2.0f / TRACKBALL_MOVEMENT_THRESHOLD), + WithPressure(1.0f))))); + args.clear(); + + // Move X, Y a bit while pressed. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 2); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithCoords(2.0f / TRACKBALL_MOVEMENT_THRESHOLD, + 1.0f / TRACKBALL_MOVEMENT_THRESHOLD), + WithPressure(1.0f))))); + args.clear(); + + // Release Button. + args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); + args.clear(); +} + +TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleAllButtons) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + std::list<NotifyArgs> args; + + // press BTN_LEFT, release BTN_LEFT + args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(100.0f, 200.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(100.0f, 200.0f), WithPressure(1.0f))))); + args.clear(); + + args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0), WithCoords(100.0f, 200.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0), WithCoords(100.0f, 200.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(0), WithCoords(100.0f, 200.0f), + WithPressure(0.0f))))); + args.clear(); + + // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE + args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 1); + args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY | + AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(100.0f, 200.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(100.0f, 200.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY | + AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(100.0f, 200.0f), WithPressure(1.0f))))); + args.clear(); + + args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(100.0f, 200.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(100.0f, 200.0f), WithPressure(1.0f))))); + args.clear(); + + args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0), WithCoords(100.0f, 200.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithButtonState(0), + WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(100.0f, 200.0f), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithButtonState(0), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(100.0f, 200.0f), WithPressure(0.0f))))); +} + +class CursorInputMapperButtonKeyTest + : public CursorInputMapperUnitTest, + public testing::WithParamInterface< + std::tuple<int32_t /*evdevCode*/, int32_t /*expectedButtonState*/, + int32_t /*expectedKeyCode*/>> {}; + +TEST_P(CursorInputMapperButtonKeyTest, ProcessShouldHandleButtonKey) { + auto [evdevCode, expectedButtonState, expectedKeyCode] = GetParam(); + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + std::list<NotifyArgs> args; + + args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), + WithKeyCode(expectedKeyCode))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(expectedButtonState), + WithCoords(100.0f, 200.0f), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(expectedButtonState), + WithCoords(100.0f, 200.0f), WithPressure(0.0f))))); + args.clear(); + + args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0), WithCoords(100.0f, 200.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(0), WithCoords(100.0f, 200.0f), + WithPressure(0.0f))), + VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), + WithKeyCode(expectedKeyCode))))); +} + +INSTANTIATE_TEST_SUITE_P( + SideExtraBackAndForward, CursorInputMapperButtonKeyTest, + testing::Values(std::make_tuple(BTN_SIDE, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK), + std::make_tuple(BTN_EXTRA, AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD), + std::make_tuple(BTN_BACK, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK), + std::make_tuple(BTN_FORWARD, AMOTION_EVENT_BUTTON_FORWARD, + AKEYCODE_FORWARD))); + +TEST_F(CursorInputMapperUnitTest, ProcessShouldMoveThePointerAroundInPointerMode) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + std::list<NotifyArgs> args; + + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(110.0f, 220.0f), WithPressure(0.0f), WithSize(0.0f), + WithTouchDimensions(0.0f, 0.0f), WithToolDimensions(0.0f, 0.0f), + WithOrientation(0.0f), WithDistance(0.0f))))); + ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f)); +} + +/** + * When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any + * pointer acceleration or speed processing should not be applied. + */ +TEST_F(CursorInputMapperUnitTest, PointerCaptureDisablesVelocityProcessing) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f, + /*highThreshold=*/100.f, /*acceleration=*/10.f); + mReaderConfiguration.pointerVelocityControlParameters = testParams; + mFakePolicy->setVelocityControlParams(testParams); + createMapper(); + + std::list<NotifyArgs> args; + + // Move and verify scale is applied. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))))); + NotifyMotionArgs motionArgs = std::get<NotifyMotionArgs>(args.front()); + const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + ASSERT_GT(relX, 10); + ASSERT_GT(relY, 20); + args.clear(); + + // Enable Pointer Capture + setPointerCapture(true); + + // Move and verify scale is not applied. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE_RELATIVE), + WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(10, 20))))); +} + +// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging +// logic can be removed. +class CursorInputMapperUnitTestWithChoreographer : public CursorInputMapperUnitTestBase { +protected: + void SetUp() override { + input_flags::enable_pointer_choreographer(true); + CursorInputMapperUnitTestBase::SetUp(); + } +}; + +TEST_F(CursorInputMapperUnitTestWithChoreographer, PopulateDeviceInfoReturnsRangeFromPolicy) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + mFakePolicy->clearViewports(); + mFakePointerController->clearBounds(); + createMapper(); + + InputDeviceInfo info; + mMapper->populateDeviceInfo(info); + + // Initially there should not be a valid motion range because there's no viewport or pointer + // bounds. + ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE)); + ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE, + AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); + + // When the viewport and the default pointer display ID is set, then there should be a valid + // motion range. + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); + std::list<NotifyArgs> args = + mMapper->reconfigure(systemTime(), mReaderConfiguration, + InputReaderConfiguration::Change::DISPLAY_INFO); + ASSERT_THAT(args, testing::IsEmpty()); + + InputDeviceInfo info2; + mMapper->populateDeviceInfo(info2); + + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, 0, + DISPLAY_WIDTH - 1, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, 0, + DISPLAY_HEIGHT - 1, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE, + AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); +} + +TEST_F(CursorInputMapperUnitTestWithChoreographer, ProcessShouldHandleAllButtonsWithZeroCoords) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + std::list<NotifyArgs> args; + + // press BTN_LEFT, release BTN_LEFT + args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))))); + args.clear(); + args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0), WithCoords(0.0f, 0.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithButtonState(0), WithCoords(0.0f, 0.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(0), WithCoords(0.0f, 0.0f), + WithPressure(0.0f))))); + args.clear(); + + // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE + args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 1); + args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY | + AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY | + AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))))); + args.clear(); + + args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))))); + args.clear(); + + args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0), WithCoords(0.0f, 0.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithButtonState(0), + WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithButtonState(0), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); +} + +class CursorInputMapperButtonKeyTestWithChoreographer + : public CursorInputMapperUnitTestWithChoreographer, + public testing::WithParamInterface< + std::tuple<int32_t /*evdevCode*/, int32_t /*expectedButtonState*/, + int32_t /*expectedKeyCode*/>> {}; + +TEST_P(CursorInputMapperButtonKeyTestWithChoreographer, + ProcessShouldHandleButtonKeyWithZeroCoords) { + auto [evdevCode, expectedButtonState, expectedKeyCode] = GetParam(); + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + std::list<NotifyArgs> args; + + args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 1); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), + WithKeyCode(expectedKeyCode))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(expectedButtonState), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + WithButtonState(expectedButtonState), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); + args.clear(); + + args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 0); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), + WithButtonState(0), WithCoords(0.0f, 0.0f), + WithPressure(0.0f))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithButtonState(0), WithCoords(0.0f, 0.0f), + WithPressure(0.0f))), + VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), + WithKeyCode(expectedKeyCode))))); +} + +INSTANTIATE_TEST_SUITE_P( + SideExtraBackAndForward, CursorInputMapperButtonKeyTestWithChoreographer, + testing::Values(std::make_tuple(BTN_SIDE, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK), + std::make_tuple(BTN_EXTRA, AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD), + std::make_tuple(BTN_BACK, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK), + std::make_tuple(BTN_FORWARD, AMOTION_EVENT_BUTTON_FORWARD, + AKEYCODE_FORWARD))); + +TEST_F(CursorInputMapperUnitTestWithChoreographer, ProcessWhenModeIsPointerShouldKeepZeroCoords) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + std::list<NotifyArgs> args; + + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(0.0f, 0.0f), WithPressure(0.0f), WithSize(0.0f), + WithTouchDimensions(0.0f, 0.0f), WithToolDimensions(0.0f, 0.0f), + WithOrientation(0.0f), WithDistance(0.0f))))); +} + +TEST_F(CursorInputMapperUnitTestWithChoreographer, PointerCaptureDisablesVelocityProcessing) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f, + /*highThreshold=*/100.f, /*acceleration=*/10.f); + mReaderConfiguration.pointerVelocityControlParameters = testParams; + mFakePolicy->setVelocityControlParams(testParams); + createMapper(); + + NotifyMotionArgs motionArgs; + std::list<NotifyArgs> args; + + // Move and verify scale is applied. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE), + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))))); + motionArgs = std::get<NotifyMotionArgs>(args.front()); + const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + ASSERT_GT(relX, 10); + ASSERT_GT(relY, 20); + args.clear(); + + // Enable Pointer Capture + setPointerCapture(true); + + // Move and verify scale is not applied. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithSource(AINPUT_SOURCE_MOUSE_RELATIVE), + WithMotionAction(AMOTION_EVENT_ACTION_MOVE))))); + motionArgs = std::get<NotifyMotionArgs>(args.front()); + const float relX2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float relY2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + ASSERT_EQ(10, relX2); + ASSERT_EQ(20, relY2); +} + +TEST_F(CursorInputMapperUnitTestWithChoreographer, ConfigureDisplayIdNoAssociatedViewport) { + // Set up the default display. + mFakePolicy->clearViewports(); + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_90, + /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); + + // Set up the secondary display as the display on which the pointer should be shown. + // The InputDevice is not associated with any display. + mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, + ui::ROTATION_0, /*isActive=*/true, "local:1", NO_PORT, + ViewportType::EXTERNAL); + mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); + + createMapper(); + + mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); + mFakePointerController->setPosition(100, 200); + + // Ensure input events are generated without display ID or coords, because they will be decided + // later by PointerChoreographer. + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(ADISPLAY_ID_NONE), + WithCoords(0.0f, 0.0f))))); +} + +namespace { + +// Minimum timestamp separation between subsequent input events from a Bluetooth device. +constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4); +// Maximum smoothing time delta so that we don't generate events too far into the future. +constexpr nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32); + +} // namespace + +class BluetoothCursorInputMapperUnitTest : public CursorInputMapperUnitTestBase { +protected: + void SetUp() override { + input_flags::enable_pointer_choreographer(false); + SetUpWithBus(BUS_BLUETOOTH); + + mFakePointerController = std::make_shared<FakePointerController>(); + mFakePolicy->setPointerController(mFakePointerController); + } +}; + +TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmoothening) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + std::list<NotifyArgs> argsList; + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + + // Process several events that come in quick succession, according to their timestamps. + for (int i = 0; i < 3; i++) { + constexpr static nsecs_t delta = ms2ns(1); + static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA); + kernelEventTime += delta; + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + } +} + +TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmootheningIsCapped) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + std::list<NotifyArgs> argsList; + + nsecs_t expectedEventTime = ARBITRARY_TIME; + argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + + // Process several events with the same timestamp from the kernel. + // Ensure that we do not generate events too far into the future. + constexpr static int32_t numEvents = + MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA; + for (int i = 0; i < numEvents; i++) { + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + } + + // By processing more events with the same timestamp, we should not generate events with a + // timestamp that is more than the specified max time delta from the timestamp at its injection. + const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA; + for (int i = 0; i < 3; i++) { + argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(cappedEventTime))))); + argsList.clear(); + } +} + +TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmootheningNotUsed) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + std::list<NotifyArgs> argsList; + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + + // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp + // smoothening is not needed, its timestamp is not affected. + kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1); + expectedEventTime = kernelEventTime; + + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); +} + +// --- BluetoothCursorInputMapperUnitTestWithChoreographer --- + +class BluetoothCursorInputMapperUnitTestWithChoreographer : public CursorInputMapperUnitTestBase { +protected: + void SetUp() override { + input_flags::enable_pointer_choreographer(true); + SetUpWithBus(BUS_BLUETOOTH); + + mFakePointerController = std::make_shared<FakePointerController>(); + mFakePolicy->setPointerController(mFakePointerController); + } +}; + +TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmoothening) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + std::list<NotifyArgs> argsList; + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + + // Process several events that come in quick succession, according to their timestamps. + for (int i = 0; i < 3; i++) { + constexpr static nsecs_t delta = ms2ns(1); + static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA); + kernelEventTime += delta; + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + } +} + +TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmootheningIsCapped) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + std::list<NotifyArgs> argsList; + + nsecs_t expectedEventTime = ARBITRARY_TIME; + argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + + // Process several events with the same timestamp from the kernel. + // Ensure that we do not generate events too far into the future. + constexpr static int32_t numEvents = + MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA; + for (int i = 0; i < numEvents; i++) { + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + } + + // By processing more events with the same timestamp, we should not generate events with a + // timestamp that is more than the specified max time delta from the timestamp at its injection. + const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA; + for (int i = 0; i < 3; i++) { + argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1); + argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(cappedEventTime))))); + argsList.clear(); + } +} + +TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmootheningNotUsed) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + createMapper(); + std::list<NotifyArgs> argsList; + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); + + // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp + // smoothening is not needed, its timestamp is not affected. + kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1); + expectedEventTime = kernelEventTime; + + argsList += process(kernelEventTime, EV_REL, REL_X, 1); + argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); + EXPECT_THAT(argsList, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime))))); + argsList.clear(); +} + } // namespace android diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp index 80319f2487..31e117352b 100644 --- a/services/inputflinger/tests/FakePointerController.cpp +++ b/services/inputflinger/tests/FakePointerController.cpp @@ -28,6 +28,10 @@ void FakePointerController::setBounds(float minX, float minY, float maxX, float mMaxY = maxY; } +void FakePointerController::clearBounds() { + mHaveBounds = false; +} + const std::map<int32_t, std::vector<int32_t>>& FakePointerController::getSpots() { return mSpotsByDisplay; } diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h index 800f8649fd..061ae62b57 100644 --- a/services/inputflinger/tests/FakePointerController.h +++ b/services/inputflinger/tests/FakePointerController.h @@ -33,6 +33,7 @@ public: virtual ~FakePointerController() {} void setBounds(float minX, float minY, float maxX, float maxY); + void clearBounds(); const std::map<int32_t, std::vector<int32_t>>& getSpots(); void setPosition(float x, float y) override; diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp index 69772af6b3..163076974f 100644 --- a/services/inputflinger/tests/GestureConverter_test.cpp +++ b/services/inputflinger/tests/GestureConverter_test.cpp @@ -115,12 +115,32 @@ TEST_F(GestureConverterTest, Move) { converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10), - WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X - 5, POINTER_Y + 10), + WithRelativeMotion(-5, 10), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10)); + + // The same gesture again should only repeat the HOVER_MOVE and cursor position change, not the + // HOVER_ENTER. + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X - 10, POINTER_Y + 20), + WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); + + ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 10, POINTER_Y + 20)); } TEST_F(GestureConverterTest, Move_Rotated) { @@ -134,10 +154,16 @@ TEST_F(GestureConverterTest, Move_Rotated) { converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X + 10, POINTER_Y + 5), WithRelativeMotion(10, 5), - WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X + 10, POINTER_Y + 5), + WithRelativeMotion(10, 5), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X + 10, POINTER_Y + 5)); } @@ -153,8 +179,6 @@ TEST_F(GestureConverterTest, ButtonsChange) { /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), @@ -210,12 +234,32 @@ TEST_F(GestureConverterTest, ButtonsChange) { WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithButtonState(0), WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } +TEST_F(GestureConverterTest, ButtonDownAfterMoveExitsHover) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + + Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE, + /*is_tap=*/false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); + ASSERT_THAT(args.front(), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), WithButtonState(0), + WithCoords(POINTER_X - 5, POINTER_Y + 10), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)))); +} + TEST_F(GestureConverterTest, DragWithButton) { InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); @@ -275,7 +319,7 @@ TEST_F(GestureConverterTest, DragWithButton) { WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithButtonState(0), WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER), @@ -328,13 +372,20 @@ TEST_F(GestureConverterTest, Scroll) { args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithCoords(POINTER_X, POINTER_Y - 15), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X, POINTER_Y - 15), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Scroll_Rotated) { @@ -380,12 +431,19 @@ TEST_F(GestureConverterTest, Scroll_Rotated) { args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithCoords(POINTER_X - 15, POINTER_Y), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X - 15, POINTER_Y), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Scroll_ClearsClassificationAfterGesture) { @@ -591,6 +649,12 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) { WithMotionClassification( MotionClassification::MULTI_FINGER_SWIPE), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -677,6 +741,9 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) { VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), WithPointerCount(1u), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -809,6 +876,12 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) { WithMotionClassification( MotionClassification::MULTI_FINGER_SWIPE), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -869,6 +942,12 @@ TEST_F(GestureConverterTest, Pinch_Inwards) { WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -929,6 +1008,12 @@ TEST_F(GestureConverterTest, Pinch_Outwards) { WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -1013,6 +1098,11 @@ TEST_F(GestureConverterTest, ResetWithButtonPressed) { AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithButtonState(0), WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -1027,13 +1117,20 @@ TEST_F(GestureConverterTest, ResetDuringScroll) { std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithCoords(POINTER_X, POINTER_Y - 10), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X, POINTER_Y - 10), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) { @@ -1071,6 +1168,11 @@ TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) { WithMotionClassification( MotionClassification::MULTI_FINGER_SWIPE), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -1098,6 +1200,12 @@ TEST_F(GestureConverterTest, ResetDuringPinch) { WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -1112,7 +1220,7 @@ TEST_F(GestureConverterTest, FlingTapDown) { converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); @@ -1131,13 +1239,7 @@ TEST_F(GestureConverterTest, Tap) { /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, @@ -1146,6 +1248,12 @@ TEST_F(GestureConverterTest, Tap) { ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), @@ -1175,7 +1283,7 @@ TEST_F(GestureConverterTest, Tap) { WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), @@ -1192,13 +1300,7 @@ TEST_F(GestureConverterTest, Click) { /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, @@ -1207,6 +1309,12 @@ TEST_F(GestureConverterTest, Click) { ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), @@ -1243,7 +1351,7 @@ TEST_F(GestureConverterTest, Click) { WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), @@ -1266,13 +1374,7 @@ TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, /* down= */ GESTURES_BUTTON_LEFT, @@ -1301,13 +1403,7 @@ TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabledWithDelay, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); - - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, /* down= */ GESTURES_BUTTON_LEFT, @@ -1353,7 +1449,11 @@ TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabledWithDelay, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); - ASSERT_EQ(5u, args.size()); + ASSERT_EQ(6u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithRelativeMotion(0.f, 0.f), WithButtonState(0))); + args.pop_front(); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithRelativeMotion(0.f, 0.f), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))); @@ -1373,7 +1473,7 @@ TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabledWithDelay, WithButtonState(0))); args.pop_front(); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithRelativeMotion(0, 0), WithButtonState(0))); } @@ -1390,13 +1490,7 @@ TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, @@ -1404,6 +1498,12 @@ TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled, args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), @@ -1441,7 +1541,7 @@ TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled, WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), @@ -1463,14 +1563,7 @@ TEST_F_WITH_FLAGS(GestureConverterTest, MoveEnablesTapToClick, Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10), - WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); - - ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10)); + // We don't need to check args here, since it's covered by the Move test. // Future taps should be re-enabled ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); @@ -1489,7 +1582,9 @@ TEST_F_WITH_FLAGS(GestureConverterTest, KeypressCancelsHoverMove, converter.handleGesture(gestureStartTime, READ_TIME, gestureStartTime, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); // Key presses with IME connection should cancel ongoing move gesture nsecs_t currentTime = gestureStartTime + 100; @@ -1512,7 +1607,9 @@ TEST_F_WITH_FLAGS(GestureConverterTest, KeypressCancelsHoverMove, args = converter.handleGesture(currentTime, READ_TIME, currentTime, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); } // TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging @@ -1535,6 +1632,21 @@ TEST_F(GestureConverterTestWithChoreographer, Move) { converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), WithRelativeMotion(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(0, 0), WithRelativeMotion(-5, 10), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); + + // The same gesture again should only repeat the HOVER_MOVE, not the HOVER_ENTER. + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), @@ -1552,10 +1664,16 @@ TEST_F(GestureConverterTestWithChoreographer, Move_Rotated) { converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(10, 5), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), WithRelativeMotion(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(0, 0), WithRelativeMotion(10, 5), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, ButtonsChange) { @@ -1621,12 +1739,32 @@ TEST_F(GestureConverterTestWithChoreographer, ButtonsChange) { WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithButtonState(0), WithCoords(0, 0), WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } +TEST_F(GestureConverterTestWithChoreographer, ButtonDownAfterMoveExitsHover) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + + Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE, + /*is_tap=*/false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); + ASSERT_THAT(args.front(), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), WithButtonState(0), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT)))); +} + TEST_F(GestureConverterTestWithChoreographer, DragWithButton) { InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); @@ -1679,7 +1817,7 @@ TEST_F(GestureConverterTestWithChoreographer, DragWithButton) { WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithButtonState(0), WithCoords(0, 0), WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); @@ -1730,12 +1868,20 @@ TEST_F(GestureConverterTestWithChoreographer, Scroll) { args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0 - 15), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0, -15), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Scroll_Rotated) { @@ -1781,11 +1927,19 @@ TEST_F(GestureConverterTestWithChoreographer, Scroll_Rotated) { args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(-15, 0), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(-15, 0), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsClassificationAfterGesture) { @@ -1990,6 +2144,12 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Vertical) { WithMotionClassification( MotionClassification::MULTI_FINGER_SWIPE), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2076,6 +2236,9 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Rotated) { VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), WithPointerCount(1u), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2208,6 +2371,12 @@ TEST_F(GestureConverterTestWithChoreographer, FourFingerSwipe_Horizontal) { WithMotionClassification( MotionClassification::MULTI_FINGER_SWIPE), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2267,6 +2436,12 @@ TEST_F(GestureConverterTestWithChoreographer, Pinch_Inwards) { WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2326,6 +2501,12 @@ TEST_F(GestureConverterTestWithChoreographer, Pinch_Outwards) { WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2409,6 +2590,11 @@ TEST_F(GestureConverterTestWithChoreographer, ResetWithButtonPressed) { AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithButtonState(0), WithCoords(0, 0), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2423,12 +2609,20 @@ TEST_F(GestureConverterTestWithChoreographer, ResetDuringScroll) { std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, -10), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0, -10), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, ResetDuringThreeFingerSwipe) { @@ -2466,6 +2660,11 @@ TEST_F(GestureConverterTestWithChoreographer, ResetDuringThreeFingerSwipe) { WithMotionClassification( MotionClassification::MULTI_FINGER_SWIPE), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2493,6 +2692,12 @@ TEST_F(GestureConverterTestWithChoreographer, ResetDuringPinch) { WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2507,7 +2712,7 @@ TEST_F(GestureConverterTestWithChoreographer, FlingTapDown) { converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); } @@ -2522,13 +2727,7 @@ TEST_F(GestureConverterTestWithChoreographer, Tap) { /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, @@ -2537,6 +2736,11 @@ TEST_F(GestureConverterTestWithChoreographer, Tap) { ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), @@ -2563,7 +2767,7 @@ TEST_F(GestureConverterTestWithChoreographer, Tap) { WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), @@ -2580,12 +2784,7 @@ TEST_F(GestureConverterTestWithChoreographer, Click) { /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); - - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, @@ -2594,6 +2793,11 @@ TEST_F(GestureConverterTestWithChoreographer, Click) { ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), @@ -2628,7 +2832,7 @@ TEST_F(GestureConverterTestWithChoreographer, Click) { WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), @@ -2651,13 +2855,7 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabl /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, /* down= */ GESTURES_BUTTON_LEFT, @@ -2686,12 +2884,7 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabl /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); - - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, /* down= */ GESTURES_BUTTON_LEFT, @@ -2737,7 +2930,11 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabl /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); - ASSERT_EQ(5u, args.size()); + ASSERT_EQ(6u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithRelativeMotion(0.f, 0.f), WithButtonState(0))); + args.pop_front(); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithRelativeMotion(0.f, 0.f), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))); @@ -2757,7 +2954,7 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabl WithButtonState(0))); args.pop_front(); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithRelativeMotion(0, 0), WithButtonState(0))); } @@ -2774,13 +2971,7 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, ClickWithTapToClickDisa /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, @@ -2789,6 +2980,11 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, ClickWithTapToClickDisa ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), @@ -2823,7 +3019,7 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, ClickWithTapToClickDisa WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), @@ -2845,13 +3041,7 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, MoveEnablesTapToClick, Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the Move test. // Future taps should be re-enabled ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); @@ -2870,7 +3060,9 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, KeypressCancelsHoverMov converter.handleGesture(gestureStartTime, READ_TIME, gestureStartTime, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); // Key presses with IME connection should cancel ongoing move gesture nsecs_t currentTime = gestureStartTime + 100; @@ -2893,7 +3085,9 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, KeypressCancelsHoverMov args = converter.handleGesture(currentTime, READ_TIME, currentTime, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); } } // namespace android diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index a880a4c6bd..c92736ebf1 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -8809,6 +8809,104 @@ TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyAnrWasNotCalled()); } +/** + * If we are pruning input queue, we should never drop pointer events. Otherwise, we risk having + * an inconsistent event stream inside the dispatcher. In this test, we make sure that the + * dispatcher doesn't prune pointer events incorrectly. + * + * This test reproduces a crash in InputDispatcher. + * To reproduce the crash, we need to simulate the conditions for "pruning input queue" to occur. + * + * Keep the currently focused application (mApplication), and have no focused window. + * We set up two additional windows: + * 1) The navigation bar window. This simulates the system "NavigationBar", which is used in the + * 3-button navigation mode. This window injects a BACK button when it's touched. 2) The application + * window. This window is not focusable, but is touchable. + * + * We first touch the navigation bar, which causes it to inject a key. Since there's no focused + * window, the dispatcher doesn't process this key, and all other events inside dispatcher are now + * blocked. The dispatcher is waiting for 'mApplication' to add a focused window. + * + * Now, we touch "Another window". This window is owned by a different application than + * 'mApplication'. This causes the dispatcher to stop waiting for 'mApplication' to add a focused + * window. Now, the "pruning input queue" behaviour should kick in, and the dispatcher should start + * dropping the events from its queue. Ensure that no crash occurs. + * + * In this test, we are setting long timeouts to prevent ANRs and events dropped due to being stale. + * This does not affect the test running time. + */ +TEST_F(InputDispatcherMultiWindowAnr, PruningInputQueueShouldNotDropPointerEvents) { + std::shared_ptr<FakeApplicationHandle> systemUiApplication = + std::make_shared<FakeApplicationHandle>(); + systemUiApplication->setDispatchingTimeout(3000ms); + mFakePolicy->setStaleEventTimeout(3000ms); + sp<FakeWindowHandle> navigationBar = + sp<FakeWindowHandle>::make(systemUiApplication, mDispatcher, "NavigationBar", + ADISPLAY_ID_DEFAULT); + navigationBar->setFocusable(false); + navigationBar->setWatchOutsideTouch(true); + navigationBar->setFrame(Rect(0, 0, 100, 100)); + + mApplication->setDispatchingTimeout(3000ms); + // 'mApplication' is already focused, but we call it again here to make it explicit. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication); + + std::shared_ptr<FakeApplicationHandle> anotherApplication = + std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> appWindow = + sp<FakeWindowHandle>::make(anotherApplication, mDispatcher, "Another window", + ADISPLAY_ID_DEFAULT); + appWindow->setFocusable(false); + appWindow->setFrame(Rect(100, 100, 200, 200)); + + mDispatcher->onWindowInfosChanged( + {{*navigationBar->getInfo(), *appWindow->getInfo()}, {}, 0, 0}); + // 'mFocusedWindow' is no longer in the dispatcher window list, and therefore loses focus + mFocusedWindow->consumeFocusEvent(false); + + // Touch down the navigation bar. It consumes the touch and injects a key into the dispatcher + // in response. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .build()); + navigationBar->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Key will not be sent anywhere because we have no focused window. It will remain pending. + // Pretend we are injecting KEYCODE_BACK, but it doesn't actually matter what key it is. + InputEventInjectionResult result = + injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT, + InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms, + /*allowKeyRepeat=*/false); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result); + + // Finish the gesture - lift up finger and inject ACTION_UP key event + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .build()); + result = injectKey(*mDispatcher, AKEY_EVENT_ACTION_UP, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT, + InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms, + /*allowKeyRepeat=*/false); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result); + // The key that was injected is blocking the dispatcher, so the navigation bar shouldn't be + // getting any events yet. + navigationBar->assertNoEvents(); + + // Now touch "Another window". This touch is going to a different application than the one we + // are waiting for (which is 'mApplication'). + // This should cause the dispatcher to drop the pending focus-dispatched events (like the key + // trying to be injected) and to continue processing the rest of the events in the original + // order. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(150)) + .build()); + navigationBar->consumeMotionEvent(WithMotionAction(ACTION_UP)); + navigationBar->consumeMotionEvent(WithMotionAction(ACTION_OUTSIDE)); + appWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + appWindow->assertNoEvents(); + navigationBar->assertNoEvents(); +} + // These tests ensure we cannot send touch events to a window that's positioned behind a window // that has feature NO_INPUT_CHANNEL. // Layout: diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp index 36be684f96..5f43bd225b 100644 --- a/services/inputflinger/tests/InputMapperTest.cpp +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -19,12 +19,13 @@ #include <InputReaderBase.h> #include <gtest/gtest.h> #include <ui/Rotation.h> +#include <utils/Timers.h> namespace android { using testing::Return; -void InputMapperUnitTest::SetUp() { +void InputMapperUnitTest::SetUpWithBus(int bus) { mFakePointerController = std::make_shared<FakePointerController>(); mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); mFakePointerController->setPosition(INITIAL_CURSOR_X, INITIAL_CURSOR_Y); @@ -36,15 +37,24 @@ void InputMapperUnitTest::SetUp() { EXPECT_CALL(mMockInputReaderContext, getPolicy()).WillRepeatedly(Return(mFakePolicy.get())); EXPECT_CALL(mMockInputReaderContext, getEventHub()).WillRepeatedly(Return(&mMockEventHub)); - InputDeviceIdentifier identifier; - identifier.name = "device"; - identifier.location = "USB1"; - identifier.bus = 0; - EXPECT_CALL(mMockEventHub, getDeviceIdentifier(EVENTHUB_ID)).WillRepeatedly(Return(identifier)); + mIdentifier.name = "device"; + mIdentifier.location = "USB1"; + mIdentifier.bus = bus; + EXPECT_CALL(mMockEventHub, getDeviceIdentifier(EVENTHUB_ID)) + .WillRepeatedly(Return(mIdentifier)); + EXPECT_CALL(mMockEventHub, getConfiguration(EVENTHUB_ID)).WillRepeatedly([&](int32_t) { + return mPropertyMap; + }); +} + +void InputMapperUnitTest::createDevice() { mDevice = std::make_unique<InputDevice>(&mMockInputReaderContext, DEVICE_ID, - /*generation=*/2, identifier); + /*generation=*/2, mIdentifier); + mDevice->addEmptyEventHubDevice(EVENTHUB_ID); mDeviceContext = std::make_unique<InputDeviceContext>(*mDevice, EVENTHUB_ID); + std::list<NotifyArgs> _ = + mDevice->configure(systemTime(), mReaderConfiguration, /*changes=*/{}); } void InputMapperUnitTest::setupAxis(int axis, bool valid, int32_t min, int32_t max, @@ -215,8 +225,8 @@ std::list<NotifyArgs> InputMapperTest::handleTimeout(InputMapper& mapper, nsecs_ return generatedArgs; } -void InputMapperTest::assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, - float min, float max, float flat, float fuzz) { +void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, float min, + float max, float flat, float fuzz) { const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source); ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source; ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source; @@ -227,11 +237,9 @@ void InputMapperTest::assertMotionRange(const InputDeviceInfo& info, int32_t axi ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source; } -void InputMapperTest::assertPointerCoords(const PointerCoords& coords, float x, float y, - float pressure, float size, float touchMajor, - float touchMinor, float toolMajor, float toolMinor, - float orientation, float distance, - float scaledAxisEpsilon) { +void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure, float size, + float touchMajor, float touchMinor, float toolMajor, float toolMinor, + float orientation, float distance, float scaledAxisEpsilon) { ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), scaledAxisEpsilon); ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), scaledAxisEpsilon); ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON); diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h index 05b0e97ff6..e176a65551 100644 --- a/services/inputflinger/tests/InputMapperTest.h +++ b/services/inputflinger/tests/InputMapperTest.h @@ -32,6 +32,7 @@ #include "InterfaceMocks.h" #include "TestConstants.h" #include "TestInputListener.h" +#include "input/PropertyMap.h" namespace android { @@ -41,7 +42,15 @@ protected: static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000; static constexpr float INITIAL_CURSOR_X = 400; static constexpr float INITIAL_CURSOR_Y = 240; - virtual void SetUp() override; + virtual void SetUp() override { SetUpWithBus(0); } + virtual void SetUpWithBus(int bus); + + /** + * Initializes mDevice and mDeviceContext. When this happens, mDevice takes a copy of + * mPropertyMap, so tests that need to set configuration properties should do so before calling + * this. Others will most likely want to call it in their SetUp method. + */ + void createDevice(); void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution); @@ -54,6 +63,7 @@ protected: std::list<NotifyArgs> process(int32_t type, int32_t code, int32_t value); std::list<NotifyArgs> process(nsecs_t when, int32_t type, int32_t code, int32_t value); + InputDeviceIdentifier mIdentifier; MockEventHubInterface mMockEventHub; sp<FakeInputReaderPolicy> mFakePolicy; std::shared_ptr<FakePointerController> mFakePointerController; @@ -64,6 +74,7 @@ protected: InputReaderConfiguration mReaderConfiguration; // The mapper should be created by the subclasses. std::unique_ptr<InputMapper> mMapper; + PropertyMap mPropertyMap; }; /** @@ -130,13 +141,13 @@ protected: void resetMapper(InputMapper& mapper, nsecs_t when); std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when); - - static void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, - float min, float max, float flat, float fuzz); - static void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure, - float size, float touchMajor, float touchMinor, float toolMajor, - float toolMinor, float orientation, float distance, - float scaledAxisEpsilon = 1.f); }; +void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, float min, + float max, float flat, float fuzz); + +void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure, float size, + float touchMajor, float touchMinor, float toolMajor, float toolMinor, + float orientation, float distance, float scaledAxisEpsilon = 1.f); + } // namespace android diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index c6536de0d4..91aa0cabfb 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -97,8 +97,6 @@ static constexpr uint32_t STYLUS_FUSION_SOURCE = // Minimum timestamp separation between subsequent input events from a Bluetooth device. static constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4); -// Maximum smoothing time delta so that we don't generate events too far into the future. -constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32); namespace input_flags = com::android::input::flags; @@ -4228,255 +4226,6 @@ protected: } }; -TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources()); -} - -TEST_F(CursorInputMapperTest, WhenModeIsNavigation_GetSources_ReturnsTrackball) { - addConfigurationProperty("cursor.mode", "navigation"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper.getSources()); -} - -TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeFromPointerController) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - InputDeviceInfo info; - mapper.populateDeviceInfo(info); - - // Initially there may not be a valid motion range. - ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE)); - ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, - AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); - - // When the bounds are set, then there should be a valid motion range. - mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - InputDeviceInfo info2; - mapper.populateDeviceInfo(info2); - - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, - AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, - 1, 800 - 1, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, - AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, - 2, 480 - 1, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, - AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_MOUSE, - 0.0f, 1.0f, 0.0f, 0.0f)); -} - -TEST_F(CursorInputMapperTest, WhenModeIsNavigation_PopulateDeviceInfo_ReturnsScaledRange) { - addConfigurationProperty("cursor.mode", "navigation"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - InputDeviceInfo info; - mapper.populateDeviceInfo(info); - - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, - AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_TRACKBALL, - -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, - AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_TRACKBALL, - -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, - AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_TRACKBALL, - 0.0f, 1.0f, 0.0f, 0.0f)); -} - -TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) { - addConfigurationProperty("cursor.mode", "navigation"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - NotifyMotionArgs args; - - // Button press. - // Mostly testing non x/y behavior here so we don't need to check again elsewhere. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(0, args.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState); - ASSERT_EQ(0, args.edgeFlags); - ASSERT_EQ(uint32_t(1), args.getPointerCount()); - ASSERT_EQ(0, args.pointerProperties[0].id); - ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action); - ASSERT_EQ(0, args.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState); - ASSERT_EQ(0, args.edgeFlags); - ASSERT_EQ(uint32_t(1), args.getPointerCount()); - ASSERT_EQ(0, args.pointerProperties[0].id); - ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - // Button release. Should have same down time. - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, BTN_MOUSE, 0); - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action); - ASSERT_EQ(0, args.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(0, args.buttonState); - ASSERT_EQ(0, args.edgeFlags); - ASSERT_EQ(uint32_t(1), args.getPointerCount()); - ASSERT_EQ(0, args.pointerProperties[0].id); - ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_EQ(0, args.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(0, args.buttonState); - ASSERT_EQ(0, args.edgeFlags); - ASSERT_EQ(uint32_t(1), args.getPointerCount()); - ASSERT_EQ(0, args.pointerProperties[0].id); - ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); -} - -TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) { - addConfigurationProperty("cursor.mode", "navigation"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - NotifyMotionArgs args; - - // Motion in X but not Y. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, - 0.0f)); - - // Motion in Y but not X. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, -2); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, - -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f)); -} - -TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) { - addConfigurationProperty("cursor.mode", "navigation"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - NotifyMotionArgs args; - - // Button press. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - // Button release. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); -} - -TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { - addConfigurationProperty("cursor.mode", "navigation"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - NotifyMotionArgs args; - - // Combined X, Y and Button. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, -2); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, - -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, - -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f)); - - // Move X, Y a bit while pressed. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 2); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], - 2.0f / TRACKBALL_MOVEMENT_THRESHOLD, - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f)); - - // Release Button. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); -} - TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotions) { mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID); addConfigurationProperty("cursor.mode", "navigation"); @@ -4548,328 +4297,6 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotion ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, 1)); } -TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); - mFakePointerController->setPosition(100, 200); - - NotifyMotionArgs motionArgs; - NotifyKeyArgs keyArgs; - - // press BTN_LEFT, release BTN_LEFT - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY, - motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY, - motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - // press BTN_BACK, release BTN_BACK - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - // press BTN_SIDE, release BTN_SIDE - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - // press BTN_FORWARD, release BTN_FORWARD - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); - - // press BTN_EXTRA, release BTN_EXTRA - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); -} - -TEST_F(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerAround) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); - mFakePointerController->setPosition(100, 200); - - NotifyMotionArgs args; - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f)); -} - -/** - * When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any - * pointer acceleration or speed processing should not be applied. - */ -TEST_F(CursorInputMapperTest, PointerCaptureDisablesVelocityProcessing) { - addConfigurationProperty("cursor.mode", "pointer"); - const VelocityControlParameters testParams(/*scale=*/5.f, /*low threshold=*/0.f, - /*high threshold=*/100.f, /*acceleration=*/10.f); - mFakePolicy->setVelocityControlParams(testParams); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - NotifyDeviceResetArgs resetArgs; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); - ASSERT_EQ(DEVICE_ID, resetArgs.deviceId); - - NotifyMotionArgs args; - - // Move and verify scale is applied. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); - const float relX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); - const float relY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); - ASSERT_GT(relX, 10); - ASSERT_GT(relY, 20); - - // Enable Pointer Capture - mFakePolicy->setPointerCapture(true); - configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE); - NotifyPointerCaptureChangedArgs captureArgs; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs)); - ASSERT_TRUE(captureArgs.request.enable); - - // Move and verify scale is not applied. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_EQ(10, args.pointerCoords[0].getX()); - ASSERT_EQ(20, args.pointerCoords[0].getY()); -} - TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) { addConfigurationProperty("cursor.mode", "pointer"); CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); @@ -4996,391 +4423,6 @@ protected: } }; -TEST_F(CursorInputMapperTestWithChoreographer, PopulateDeviceInfoReturnsRangeFromPolicy) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - InputDeviceInfo info; - mapper.populateDeviceInfo(info); - - // Initially there may not be a valid motion range. - ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE)); - ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE, - AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); - - // When the viewport and the default pointer display ID is set, then there should be a valid - // motion range. - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - InputDeviceInfo info2; - mapper.populateDeviceInfo(info2); - - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, 0, - DISPLAY_WIDTH - 1, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, 0, - DISPLAY_HEIGHT - 1, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE, - AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); -} - -TEST_F(CursorInputMapperTestWithChoreographer, ProcessShouldHandleAllButtonsWithZeroCoords) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - prepareDisplay(ui::ROTATION_0); - - mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); - mFakePointerController->setPosition(100, 200); - - NotifyMotionArgs motionArgs; - NotifyKeyArgs keyArgs; - - // press BTN_LEFT, release BTN_LEFT - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY, - motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY, - motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - // press BTN_BACK, release BTN_BACK - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - // press BTN_SIDE, release BTN_SIDE - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); - - // press BTN_FORWARD, release BTN_FORWARD - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); - - // press BTN_EXTRA, release BTN_EXTRA - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); - ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.buttonState); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); -} - -TEST_F(CursorInputMapperTestWithChoreographer, ProcessWhenModeIsPointerShouldKeepZeroCoords) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - prepareDisplay(ui::ROTATION_0); - - mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); - mFakePointerController->setPosition(100, 200); - - NotifyMotionArgs args; - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); -} - -TEST_F(CursorInputMapperTestWithChoreographer, PointerCaptureDisablesVelocityProcessing) { - addConfigurationProperty("cursor.mode", "pointer"); - const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f, - /*highThreshold=*/100.f, /*acceleration=*/10.f); - mFakePolicy->setVelocityControlParams(testParams); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - prepareDisplay(ui::ROTATION_0); - - NotifyDeviceResetArgs resetArgs; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); - ASSERT_EQ(DEVICE_ID, resetArgs.deviceId); - - NotifyMotionArgs args; - - // Move and verify scale is applied. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); - const float relX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); - const float relY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); - ASSERT_GT(relX, 10); - ASSERT_GT(relY, 20); - - // Enable Pointer Capture - mFakePolicy->setPointerCapture(true); - configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE); - NotifyPointerCaptureChangedArgs captureArgs; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs)); - ASSERT_TRUE(captureArgs.request.enable); - - // Move and verify scale is not applied. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - const float relX2 = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); - const float relY2 = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); - ASSERT_EQ(10, relX2); - ASSERT_EQ(20, relY2); -} - -TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdNoAssociatedViewport) { - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - // Set up the default display. - prepareDisplay(ui::ROTATION_90); - - // Set up the secondary display as the display on which the pointer should be shown. - // The InputDevice is not associated with any display. - prepareSecondaryDisplay(); - mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); - mFakePointerController->setPosition(100, 200); - - // Ensure input events are generated without display ID and coords, - // because they will be decided later by PointerChoreographer. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(ADISPLAY_ID_NONE), - WithCoords(0.0f, 0.0f)))); -} - TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdWithAssociatedViewport) { CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); @@ -5433,223 +4475,6 @@ TEST_F(CursorInputMapperTestWithChoreographer, WithCoords(0.0f, 0.0f)))); } -// --- BluetoothCursorInputMapperTest --- - -class BluetoothCursorInputMapperTest : public CursorInputMapperTestBase { -protected: - void SetUp() override { - input_flags::enable_pointer_choreographer(false); - InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH); - - mFakePointerController = std::make_shared<FakePointerController>(); - mFakePolicy->setPointerController(mFakePointerController); - } -}; - -TEST_F(BluetoothCursorInputMapperTest, TimestampSmoothening) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - nsecs_t kernelEventTime = ARBITRARY_TIME; - nsecs_t expectedEventTime = ARBITRARY_TIME; - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - - // Process several events that come in quick succession, according to their timestamps. - for (int i = 0; i < 3; i++) { - constexpr static nsecs_t delta = ms2ns(1); - static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA); - kernelEventTime += delta; - expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; - - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - } -} - -TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningIsCapped) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - nsecs_t expectedEventTime = ARBITRARY_TIME; - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - - // Process several events with the same timestamp from the kernel. - // Ensure that we do not generate events too far into the future. - constexpr static int32_t numEvents = - MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA; - for (int i = 0; i < numEvents; i++) { - expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - } - - // By processing more events with the same timestamp, we should not generate events with a - // timestamp that is more than the specified max time delta from the timestamp at its injection. - const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA; - for (int i = 0; i < 3; i++) { - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(cappedEventTime)))); - } -} - -TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningNotUsed) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - nsecs_t kernelEventTime = ARBITRARY_TIME; - nsecs_t expectedEventTime = ARBITRARY_TIME; - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - - // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp - // smoothening is not needed, its timestamp is not affected. - kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1); - expectedEventTime = kernelEventTime; - - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); -} - -// --- BluetoothCursorInputMapperTestWithChoreographer --- - -class BluetoothCursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase { -protected: - void SetUp() override { - input_flags::enable_pointer_choreographer(true); - InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH); - - mFakePointerController = std::make_shared<FakePointerController>(); - mFakePolicy->setPointerController(mFakePointerController); - } -}; - -TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmoothening) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - // Set up the default display. - prepareDisplay(ui::ROTATION_0); - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - nsecs_t kernelEventTime = ARBITRARY_TIME; - nsecs_t expectedEventTime = ARBITRARY_TIME; - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - - // Process several events that come in quick succession, according to their timestamps. - for (int i = 0; i < 3; i++) { - constexpr static nsecs_t delta = ms2ns(1); - static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA); - kernelEventTime += delta; - expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; - - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - } -} - -TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmootheningIsCapped) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - // Set up the default display. - prepareDisplay(ui::ROTATION_0); - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - nsecs_t expectedEventTime = ARBITRARY_TIME; - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - - // Process several events with the same timestamp from the kernel. - // Ensure that we do not generate events too far into the future. - constexpr static int32_t numEvents = - MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA; - for (int i = 0; i < numEvents; i++) { - expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - } - - // By processing more events with the same timestamp, we should not generate events with a - // timestamp that is more than the specified max time delta from the timestamp at its injection. - const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA; - for (int i = 0; i < 3; i++) { - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(cappedEventTime)))); - } -} - -TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmootheningNotUsed) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - // Set up the default display. - prepareDisplay(ui::ROTATION_0); - mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); - - nsecs_t kernelEventTime = ARBITRARY_TIME; - nsecs_t expectedEventTime = ARBITRARY_TIME; - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); - - // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp - // smoothening is not needed, its timestamp is not affected. - kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1); - expectedEventTime = kernelEventTime; - - process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); - process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime)))); -} - // --- TouchInputMapperTest --- class TouchInputMapperTest : public InputMapperTest { diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h index 73949136f4..9de80af853 100644 --- a/services/inputflinger/tests/InterfaceMocks.h +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -48,7 +48,7 @@ namespace android { class MockInputReaderContext : public InputReaderContext { public: MOCK_METHOD(void, updateGlobalMetaState, (), (override)); - int32_t getGlobalMetaState() override { return 0; }; + MOCK_METHOD(int32_t, getGlobalMetaState, (), (override)); MOCK_METHOD(void, disableVirtualKeysUntil, (nsecs_t time), (override)); MOCK_METHOD(bool, shouldDropVirtualKey, (nsecs_t now, int32_t keyCode, int32_t scanCode), diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp index 2ef79999a2..b44529bd04 100644 --- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp +++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp @@ -55,6 +55,7 @@ protected: void SetUp() override { InputMapperUnitTest::SetUp(); + createDevice(); // set key-codes expected in tests for (const auto& [scanCode, outKeycode] : mKeyCodeMap) { diff --git a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp index 9fa6cdd298..5e67506b48 100644 --- a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp +++ b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp @@ -23,6 +23,11 @@ class MultiTouchMotionAccumulatorTest : public InputMapperUnitTest { protected: static constexpr size_t SLOT_COUNT = 8; + void SetUp() override { + InputMapperUnitTest::SetUp(); + createDevice(); + } + MultiTouchMotionAccumulator mMotionAccumulator; void processMotionEvent(int32_t type, int32_t code, int32_t value) { diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h index 8ba497a3ea..a3e8eafea3 100644 --- a/services/inputflinger/tests/TestEventMatchers.h +++ b/services/inputflinger/tests/TestEventMatchers.h @@ -18,6 +18,7 @@ #include <cmath> #include <compare> +#include <ios> #include <android-base/stringprintf.h> #include <android/input.h> @@ -678,6 +679,24 @@ MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") { return argPressure == pressure; } +MATCHER_P(WithSize, size, "MotionEvent with specified size") { + const auto argSize = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_SIZE); + *result_listener << "expected size " << size << ", but got " << argSize; + return argSize == size; +} + +MATCHER_P(WithOrientation, orientation, "MotionEvent with specified orientation") { + const auto argOrientation = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); + *result_listener << "expected orientation " << orientation << ", but got " << argOrientation; + return argOrientation == orientation; +} + +MATCHER_P(WithDistance, distance, "MotionEvent with specified distance") { + const auto argDistance = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_DISTANCE); + *result_listener << "expected distance " << distance << ", but got " << argDistance; + return argDistance == distance; +} + MATCHER_P2(WithTouchDimensions, maj, min, "InputEvent with specified touch dimensions") { const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR); const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR); @@ -721,6 +740,12 @@ MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") { return arg.buttonState == buttons; } +MATCHER_P(WithMetaState, metaState, "InputEvent with specified meta state") { + *result_listener << "expected meta state 0x" << std::hex << metaState << ", but got 0x" + << arg.metaState; + return arg.metaState == metaState; +} + MATCHER_P(WithActionButton, actionButton, "InputEvent with specified action button") { *result_listener << "expected action button " << actionButton << ", but got " << arg.actionButton; @@ -743,4 +768,16 @@ MATCHER_P2(WithPrecision, xPrecision, yPrecision, "MotionEvent with specified pr return arg.xPrecision == xPrecision && arg.yPrecision == yPrecision; } +MATCHER_P(WithPolicyFlags, policyFlags, "InputEvent with specified policy flags") { + *result_listener << "expected policy flags 0x" << std::hex << policyFlags << ", but got 0x" + << arg.policyFlags; + return arg.policyFlags == static_cast<uint32_t>(policyFlags); +} + +MATCHER_P(WithEdgeFlags, edgeFlags, "InputEvent with specified edge flags") { + *result_listener << "expected edge flags 0x" << std::hex << edgeFlags << ", but got 0x" + << arg.edgeFlags; + return arg.edgeFlags == edgeFlags; +} + } // namespace android diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp index 8cf738cf4f..4be1e8c54a 100644 --- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp +++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp @@ -37,6 +37,8 @@ constexpr auto ACTION_UP = AMOTION_EVENT_ACTION_UP; constexpr auto BUTTON_PRESS = AMOTION_EVENT_ACTION_BUTTON_PRESS; constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE; constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE; +constexpr auto HOVER_ENTER = AMOTION_EVENT_ACTION_HOVER_ENTER; +constexpr auto HOVER_EXIT = AMOTION_EVENT_ACTION_HOVER_EXIT; constexpr int32_t DISPLAY_ID = 0; constexpr int32_t DISPLAY_WIDTH = 480; constexpr int32_t DISPLAY_HEIGHT = 800; @@ -107,6 +109,7 @@ protected: *outValue = 0; return OK; }); + createDevice(); mMapper = createInputMapper<TouchpadInputMapper>(*mDeviceContext, mReaderConfiguration); } }; @@ -150,12 +153,14 @@ TEST_F(TouchpadInputMapperTest, HoverAndLeftButtonPress) { setScanCodeState(KeyState::UP, {BTN_LEFT}); args += process(EV_SYN, SYN_REPORT, 0); ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_EXIT)), VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)), VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)), VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)), - VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)))); + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)))); // Liftoff args.clear(); @@ -217,12 +222,14 @@ TEST_F(TouchpadInputMapperTestWithChoreographer, HoverAndLeftButtonPress) { setScanCodeState(KeyState::UP, {BTN_LEFT}); args += process(EV_SYN, SYN_REPORT, 0); ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_EXIT)), VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)), VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)), VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)), - VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)))); + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)))); // Liftoff args.clear(); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h index 1a235e9401..18a96f4de7 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h @@ -24,6 +24,7 @@ #include <compositionengine/LayerFE.h> #include <compositionengine/OutputColorSetting.h> #include <math/mat4.h> +#include <scheduler/interface/ICompositor.h> #include <ui/FenceTime.h> #include <ui/Transform.h> @@ -89,17 +90,14 @@ struct CompositionRefreshArgs { // If set, causes the dirty regions to flash with the delay std::optional<std::chrono::microseconds> devOptFlashDirtyRegionsDelay; - // Optional. - // The earliest time to send the present command to the HAL. - std::optional<std::chrono::steady_clock::time_point> earliestPresentTime; - - // The expected time for the next present - nsecs_t expectedPresentTime{0}; + scheduler::FrameTargets frameTargets; // The frameInterval for the next present - Fps frameInterval{}; + // TODO (b/315371484): Calculate per display and store on `FrameTarget`. + Fps frameInterval; // If set, a frame has been scheduled for that time. + // TODO (b/255601557): Calculate per display. std::optional<std::chrono::steady_clock::time_point> scheduledFrameTime; std::vector<BorderRenderInfo> borderInfoList; diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 09c7c9933a..1c2f6cb686 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -28,8 +28,11 @@ #include <compositionengine/impl/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> #include <compositionengine/impl/planner/Planner.h> +#include <ftl/algorithm.h> #include <ftl/future.h> #include <gui/TraceUtils.h> +#include <scheduler/FrameTargeter.h> +#include <scheduler/Time.h> #include <optional> #include <thread> @@ -429,7 +432,28 @@ void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArg ftl::Future<std::monostate> Output::present( const compositionengine::CompositionRefreshArgs& refreshArgs) { - ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str()); + const auto stringifyExpectedPresentTime = [this, &refreshArgs]() -> std::string { + return ftl::Optional(getDisplayId()) + .and_then(PhysicalDisplayId::tryCast) + .and_then([&refreshArgs](PhysicalDisplayId id) { + return refreshArgs.frameTargets.get(id); + }) + .transform([](const auto& frameTargetPtr) { + return frameTargetPtr.get()->expectedPresentTime(); + }) + .transform([](TimePoint expectedPresentTime) { + return base::StringPrintf(" vsyncIn %.2fms", + ticks<std::milli, float>(expectedPresentTime - + TimePoint::now())); + }) + .or_else([] { + // There is no vsync for this output. + return std::make_optional(std::string()); + }) + .value(); + }; + ATRACE_FORMAT("%s for %s%s", __func__, mNamePlusId.c_str(), + stringifyExpectedPresentTime().c_str()); ALOGV(__FUNCTION__); updateColorProfile(refreshArgs); @@ -853,8 +877,14 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr return; } - editState().earliestPresentTime = refreshArgs.earliestPresentTime; - editState().expectedPresentTime = refreshArgs.expectedPresentTime; + if (auto frameTargetPtrOpt = ftl::Optional(getDisplayId()) + .and_then(PhysicalDisplayId::tryCast) + .and_then([&refreshArgs](PhysicalDisplayId id) { + return refreshArgs.frameTargets.get(id); + })) { + editState().earliestPresentTime = frameTargetPtrOpt->get()->earliestPresentTime(); + editState().expectedPresentTime = frameTargetPtrOpt->get()->expectedPresentTime().ns(); + } editState().frameInterval = refreshArgs.frameInterval; editState().powerCallback = refreshArgs.powerCallback; diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 6e862b4a1e..c80c8fd237 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -584,6 +584,9 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: return true; + case DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE: + return true; + case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: { return connection->mEventRegistration.test( gui::ISurfaceComposer::EventRegistration::modeChanged); diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 5094232a4e..9c4f7a510b 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -62,6 +62,10 @@ void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUp mLastAnimationTime = std::max(lastPresentTime, now); break; case LayerUpdateType::SetFrameRate: + if (FlagManager::getInstance().vrr_config()) { + break; + } + FALLTHROUGH_INTENDED; case LayerUpdateType::Buffer: FrameTimeData frameTime = {.presentTime = lastPresentTime, .queueTime = mLastUpdatedTime, diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index bfc47e67a6..27ca17f481 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -51,8 +51,12 @@ #include "FrameRateOverrideMappings.h" #include "FrontEnd/LayerHandle.h" #include "OneShotTimer.h" +#include "RefreshRateStats.h" +#include "SurfaceFlingerFactory.h" #include "SurfaceFlingerProperties.h" +#include "TimeStats/TimeStats.h" #include "VSyncTracker.h" +#include "VsyncConfiguration.h" #include "VsyncController.h" #include "VsyncSchedule.h" @@ -67,10 +71,14 @@ namespace android::scheduler { Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features, - sp<VsyncModulator> modulatorPtr, IVsyncTrackerCallback& vsyncTrackerCallback) - : impl::MessageQueue(compositor), + surfaceflinger::Factory& factory, Fps activeRefreshRate, TimeStats& timeStats, + IVsyncTrackerCallback& vsyncTrackerCallback) + : android::impl::MessageQueue(compositor), mFeatures(features), - mVsyncModulator(std::move(modulatorPtr)), + mVsyncConfiguration(factory.createVsyncConfiguration(activeRefreshRate)), + mVsyncModulator(sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs())), + mRefreshRateStats(std::make_unique<RefreshRateStats>(timeStats, activeRefreshRate, + hal::PowerMode::OFF)), mSchedulerCallback(callback), mVsyncTrackerCallback(vsyncTrackerCallback) {} @@ -182,9 +190,9 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, const FrameTargeter::BeginFrameArgs beginFrameArgs = {.frameBeginTime = SchedulerClock::now(), .vsyncId = vsyncId, - // TODO(b/255601557): Calculate per display. .expectedVsyncTime = expectedVsyncTime, - .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration}; + .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration, + .hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration}; ftl::NonNull<const Display*> pacesetterPtr = pacesetterPtrLocked(); pacesetterPtr->targeterPtr->beginFrame(beginFrameArgs, *pacesetterPtr->schedulePtr); @@ -193,11 +201,20 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, FrameTargets targets; targets.try_emplace(pacesetterPtr->displayId, &pacesetterPtr->targeterPtr->target()); + // TODO (b/256196556): Followers should use the next VSYNC after the frontrunner, not the + // pacesetter. + // Update expectedVsyncTime, which may have been adjusted by beginFrame. + expectedVsyncTime = pacesetterPtr->targeterPtr->target().expectedPresentTime(); + for (const auto& [id, display] : mDisplays) { if (id == pacesetterPtr->displayId) continue; + auto followerBeginFrameArgs = beginFrameArgs; + followerBeginFrameArgs.expectedVsyncTime = + display.schedulePtr->vsyncDeadlineAfter(expectedVsyncTime); + FrameTargeter& targeter = *display.targeterPtr; - targeter.beginFrame(beginFrameArgs, *display.schedulePtr); + targeter.beginFrame(followerBeginFrameArgs, *display.schedulePtr); targets.try_emplace(id, &targeter.target()); } @@ -307,9 +324,10 @@ ConnectionHandle Scheduler::createEventThread(Cycle cycle, frametimeline::TokenManager* tokenManager, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) { - auto eventThread = std::make_unique<impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf", - getVsyncSchedule(), tokenManager, *this, - workDuration, readyDuration); + auto eventThread = + std::make_unique<android::impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf", + getVsyncSchedule(), tokenManager, *this, + workDuration, readyDuration); auto& handle = cycle == Cycle::Render ? mAppConnectionHandle : mSfConnectionHandle; handle = createConnection(std::move(eventThread)); @@ -495,8 +513,23 @@ void Scheduler::setDuration(ConnectionHandle handle, std::chrono::nanoseconds wo thread->setDuration(workDuration, readyDuration); } -void Scheduler::setVsyncConfigSet(const VsyncConfigSet& configs, Period vsyncPeriod) { - setVsyncConfig(mVsyncModulator->setVsyncConfigSet(configs), vsyncPeriod); +void Scheduler::updatePhaseConfiguration(Fps refreshRate) { + mRefreshRateStats->setRefreshRate(refreshRate); + mVsyncConfiguration->setRefreshRateFps(refreshRate); + setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()), + refreshRate.getPeriod()); +} + +void Scheduler::resetPhaseConfiguration(Fps refreshRate) { + // Cancel the pending refresh rate change, if any, before updating the phase configuration. + mVsyncModulator->cancelRefreshRateChange(); + + mVsyncConfiguration->reset(); + updatePhaseConfiguration(refreshRate); +} + +void Scheduler::setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode powerMode) { + mRefreshRateStats->setPowerMode(powerMode); } void Scheduler::setVsyncConfig(const VsyncConfig& config, Period vsyncPeriod) { @@ -560,7 +593,7 @@ void Scheduler::onHardwareVsyncRequest(PhysicalDisplayId id, bool enabled) { // On main thread to serialize reads/writes of pending hardware VSYNC state. static_cast<void>( - schedule([=]() FTL_FAKE_GUARD(mDisplayLock) FTL_FAKE_GUARD(kMainThreadContext) { + schedule([=, this]() FTL_FAKE_GUARD(mDisplayLock) FTL_FAKE_GUARD(kMainThreadContext) { ATRACE_NAME(ftl::Concat(whence, ' ', id.value, ' ', enabled).c_str()); if (const auto displayOpt = mDisplays.get(id)) { @@ -869,6 +902,12 @@ void Scheduler::dump(utils::Dumper& dumper) const { mFrameRateOverrideMappings.dump(dumper); dumper.eol(); + mVsyncConfiguration->dump(dumper.out()); + dumper.eol(); + + mRefreshRateStats->dump(dumper.out()); + dumper.eol(); + { utils::Dumper::Section section(dumper, "Frame Targeting"sv); diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index a29d153516..f62f1baf7c 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -88,23 +88,30 @@ struct hash<android::scheduler::ConnectionHandle> { namespace android { class FenceTime; +class TimeStats; namespace frametimeline { class TokenManager; } // namespace frametimeline +namespace surfaceflinger { +class Factory; +} // namespace surfaceflinger + namespace scheduler { using GlobalSignals = RefreshRateSelector::GlobalSignals; +class RefreshRateStats; +class VsyncConfiguration; class VsyncSchedule; class Scheduler : public IEventThreadCallback, android::impl::MessageQueue { using Impl = android::impl::MessageQueue; public: - Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, sp<VsyncModulator>, - IVsyncTrackerCallback&); + Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, surfaceflinger::Factory&, + Fps activeRefreshRate, TimeStats&, IVsyncTrackerCallback&); virtual ~Scheduler(); void startTimers(); @@ -201,7 +208,10 @@ public: } } - void setVsyncConfigSet(const VsyncConfigSet&, Period vsyncPeriod); + void updatePhaseConfiguration(Fps); + void resetPhaseConfiguration(Fps) REQUIRES(kMainThreadContext); + + const VsyncConfiguration& getVsyncConfiguration() const { return *mVsyncConfiguration; } // Sets the render rate for the scheduler to run at. void setRenderRate(PhysicalDisplayId, Fps); @@ -249,8 +259,10 @@ public: // Indicates that touch interaction is taking place. void onTouchHint(); - void setDisplayPowerMode(PhysicalDisplayId, hal::PowerMode powerMode) - REQUIRES(kMainThreadContext); + void setDisplayPowerMode(PhysicalDisplayId, hal::PowerMode) REQUIRES(kMainThreadContext); + + // TODO(b/255635821): Track this per display. + void setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode) REQUIRES(kMainThreadContext); ConstVsyncSchedulePtr getVsyncSchedule(std::optional<PhysicalDisplayId> = std::nullopt) const EXCLUDES(mDisplayLock); @@ -464,9 +476,14 @@ private: const FeatureFlags mFeatures; + // Stores phase offsets configured per refresh rate. + const std::unique_ptr<VsyncConfiguration> mVsyncConfiguration; + // Shifts the VSYNC phase during certain transactions and refresh rate changes. const sp<VsyncModulator> mVsyncModulator; + const std::unique_ptr<RefreshRateStats> mRefreshRateStats; + // Used to choose refresh rate if content detection is enabled. LayerHistory mLayerHistory; @@ -497,9 +514,7 @@ private: : displayId(displayId), selectorPtr(std::move(selectorPtr)), schedulePtr(std::move(schedulePtr)), - targeterPtr(std::make_unique< - FrameTargeter>(displayId, - features.test(Feature::kBackpressureGpuComposition))) {} + targeterPtr(std::make_unique<FrameTargeter>(displayId, features)) {} const PhysicalDisplayId displayId; diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Features.h b/services/surfaceflinger/Scheduler/include/scheduler/Features.h index 7c72ac6afc..52485fb4e9 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/Features.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/Features.h @@ -29,6 +29,7 @@ enum class Feature : std::uint8_t { kTracePredictedVsync = 1 << 3, kBackpressureGpuComposition = 1 << 4, kSmallDirtyContentDetection = 1 << 5, + kExpectedPresentTime = 1 << 6, }; using FeatureFlags = ftl::Flags<Feature>; diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h index 70d4846070..a5bb6c216f 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h @@ -19,11 +19,13 @@ #include <array> #include <atomic> #include <memory> +#include <optional> #include <ui/DisplayId.h> #include <ui/Fence.h> #include <ui/FenceTime.h> +#include <scheduler/Features.h> #include <scheduler/Time.h> #include <scheduler/VsyncId.h> #include <scheduler/interface/CompositeResult.h> @@ -49,14 +51,11 @@ public: TimePoint expectedPresentTime() const { return mExpectedPresentTime; } + std::optional<TimePoint> earliestPresentTime() const { return mEarliestPresentTime; } + // The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details. TimePoint pastVsyncTime(Period minFramePeriod) const; - // Equivalent to `pastVsyncTime` unless running N VSYNCs ahead. - TimePoint previousFrameVsyncTime(Period minFramePeriod) const { - return mExpectedPresentTime - minFramePeriod; - } - // The present fence for the frame that had targeted the most recent VSYNC before this frame. // If the target VSYNC for any given frame is more than `vsyncPeriod` in the future, then the // VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the @@ -69,8 +68,6 @@ public: return mPresentFences.front().fenceTime; } - bool wouldPresentEarly(Period minFramePeriod) const; - bool isFramePending() const { return mFramePending; } bool didMissFrame() const { return mFrameMissed; } bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; } @@ -79,9 +76,17 @@ protected: explicit FrameTarget(const std::string& displayLabel); ~FrameTarget() = default; + bool wouldPresentEarly(Period minFramePeriod) const; + + // Equivalent to `pastVsyncTime` unless running N VSYNCs ahead. + TimePoint previousFrameVsyncTime(Period minFramePeriod) const { + return mExpectedPresentTime - minFramePeriod; + } + VsyncId mVsyncId; TimePoint mFrameBeginTime; TimePoint mExpectedPresentTime; + std::optional<TimePoint> mEarliestPresentTime; TracedOrdinal<bool> mFramePending; TracedOrdinal<bool> mFrameMissed; @@ -95,6 +100,8 @@ protected: std::array<FenceWithFenceTime, 2> mPresentFences; private: + friend class FrameTargeterTestBase; + template <int N> inline bool targetsVsyncsAhead(Period minFramePeriod) const { static_assert(N > 1); @@ -105,9 +112,10 @@ private: // Computes a display's per-frame metrics about past/upcoming targeting of present deadlines. class FrameTargeter final : private FrameTarget { public: - FrameTargeter(PhysicalDisplayId displayId, bool backpressureGpuComposition) + FrameTargeter(PhysicalDisplayId displayId, FeatureFlags flags) : FrameTarget(to_string(displayId)), - mBackpressureGpuComposition(backpressureGpuComposition) {} + mBackpressureGpuComposition(flags.test(Feature::kBackpressureGpuComposition)), + mSupportsExpectedPresentTime(flags.test(Feature::kExpectedPresentTime)) {} const FrameTarget& target() const { return *this; } @@ -116,10 +124,14 @@ public: VsyncId vsyncId; TimePoint expectedVsyncTime; Duration sfWorkDuration; + Duration hwcMinWorkDuration; }; void beginFrame(const BeginFrameArgs&, const IVsyncSource&); + std::optional<TimePoint> computeEarliestPresentTime(Period minFramePeriod, + Duration hwcMinWorkDuration); + // TODO(b/241285191): Merge with FrameTargeter::endFrame. FenceTimePtr setPresentFence(sp<Fence>); @@ -128,7 +140,7 @@ public: void dump(utils::Dumper&) const; private: - friend class FrameTargeterTest; + friend class FrameTargeterTestBase; // For tests. using IsFencePendingFuncPtr = bool (*)(const FenceTimePtr&, int graceTimeMs); @@ -138,6 +150,7 @@ private: static bool isFencePending(const FenceTimePtr&, int graceTimeMs); const bool mBackpressureGpuComposition; + const bool mSupportsExpectedPresentTime; TimePoint mScheduledPresentTime; CompositionCoverageFlags mCompositionCoverage; diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp index e80372be31..68c277d499 100644 --- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp +++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp @@ -82,6 +82,10 @@ void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& v } } + if (!mSupportsExpectedPresentTime) { + mEarliestPresentTime = computeEarliestPresentTime(minFramePeriod, args.hwcMinWorkDuration); + } + ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(args.vsyncId), ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()), mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)"); @@ -121,6 +125,14 @@ void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& v if (mGpuFrameMissed) mGpuFrameMissedCount++; } +std::optional<TimePoint> FrameTargeter::computeEarliestPresentTime(Period minFramePeriod, + Duration hwcMinWorkDuration) { + if (wouldPresentEarly(minFramePeriod)) { + return previousFrameVsyncTime(minFramePeriod) - hwcMinWorkDuration; + } + return {}; +} + void FrameTargeter::endFrame(const CompositeResult& result) { mCompositionCoverage = result.compositionCoverage; } diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp index c883385755..a9abcaf14c 100644 --- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp +++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp @@ -44,12 +44,18 @@ struct VsyncSource final : IVsyncSource { } // namespace -class FrameTargeterTest : public testing::Test { +class FrameTargeterTestBase : public testing::Test { public: + FrameTargeterTestBase(FeatureFlags flags) : mTargeter(PhysicalDisplayId::fromPort(13), flags) {} + const auto& target() const { return mTargeter.target(); } + bool wouldPresentEarly(Period minFramePeriod) const { + return target().wouldPresentEarly(minFramePeriod); + } + struct Frame { - Frame(FrameTargeterTest* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime, + Frame(FrameTargeterTestBase* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime, Duration frameDuration, Fps refreshRate, Fps peakRefreshRate, FrameTargeter::IsFencePendingFuncPtr isFencePendingFuncPtr = Frame::fenceSignaled, const ftl::Optional<VsyncSource>& vsyncSourceOpt = std::nullopt) @@ -61,7 +67,8 @@ public: .vsyncId = vsyncId, .expectedVsyncTime = frameBeginTime + frameDuration, - .sfWorkDuration = 10ms}; + .sfWorkDuration = 10ms, + .hwcMinWorkDuration = kHwcMinWorkDuration}; testPtr->mTargeter.beginFrame(args, vsyncSourceOpt @@ -93,7 +100,7 @@ public: static bool fencePending(const FenceTimePtr&, int) { return true; } static bool fenceSignaled(const FenceTimePtr&, int) { return false; } - FrameTargeterTest* const testPtr; + FrameTargeterTestBase* const testPtr; TimePoint& frameBeginTime; const Period period; @@ -102,11 +109,24 @@ public: bool ended = false; }; + static constexpr Duration kHwcMinWorkDuration = std::chrono::nanoseconds(5ns); + private: FenceToFenceTimeMap mFenceMap; - static constexpr bool kBackpressureGpuComposition = true; - FrameTargeter mTargeter{PhysicalDisplayId::fromPort(13), kBackpressureGpuComposition}; + FrameTargeter mTargeter; +}; + +class FrameTargeterTest : public FrameTargeterTestBase { +public: + FrameTargeterTest() : FrameTargeterTestBase(Feature::kBackpressureGpuComposition) {} +}; + +class FrameTargeterWithExpectedPresentSupportTest : public FrameTargeterTestBase { +public: + FrameTargeterWithExpectedPresentSupportTest() + : FrameTargeterTestBase(FeatureFlags(Feature::kBackpressureGpuComposition) | + Feature::kExpectedPresentTime) {} }; TEST_F(FrameTargeterTest, targetsFrames) { @@ -208,7 +228,7 @@ TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAheadVrr) { TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) { constexpr Period kPeriod = (60_Hz).getPeriod(); EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), FenceTime::NO_FENCE); - EXPECT_FALSE(target().wouldPresentEarly(kPeriod)); + EXPECT_FALSE(wouldPresentEarly(kPeriod)); } TEST_F(FrameTargeterTest, detectsEarlyPresent) { @@ -220,7 +240,37 @@ TEST_F(FrameTargeterTest, detectsEarlyPresent) { // The target is not early while past present fences are pending. for (int n = 3; n-- > 0;) { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); - EXPECT_FALSE(target().wouldPresentEarly(kPeriod)); + EXPECT_FALSE(wouldPresentEarly(kPeriod)); + EXPECT_FALSE(target().earliestPresentTime()); + } + + // The target is early if the past present fence was signaled. + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + const auto fence = frame.end(); + fence->signalForTest(frameBeginTime.ns()); + + Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + + // `finalFrame` would present early, so it has an earliest present time. + EXPECT_TRUE(wouldPresentEarly(kPeriod)); + ASSERT_NE(std::nullopt, target().earliestPresentTime()); + EXPECT_EQ(*target().earliestPresentTime(), + target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration); +} + +// Same as `detectsEarlyPresent`, above, but verifies that we do not set an earliest present time +// when there is expected present time support. +TEST_F(FrameTargeterWithExpectedPresentSupportTest, detectsEarlyPresent) { + VsyncId vsyncId{333}; + TimePoint frameBeginTime(3000ms); + constexpr Fps kRefreshRate = 60_Hz; + constexpr Period kPeriod = kRefreshRate.getPeriod(); + + // The target is not early while past present fences are pending. + for (int n = 3; n-- > 0;) { + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + EXPECT_FALSE(wouldPresentEarly(kPeriod)); + EXPECT_FALSE(target().earliestPresentTime()); } // The target is early if the past present fence was signaled. @@ -228,7 +278,12 @@ TEST_F(FrameTargeterTest, detectsEarlyPresent) { const auto fence = frame.end(); fence->signalForTest(frameBeginTime.ns()); - EXPECT_TRUE(target().wouldPresentEarly(kPeriod)); + Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + + // `finalFrame` would present early, but we have expected present time support, so it has no + // earliest present time. + EXPECT_TRUE(wouldPresentEarly(kPeriod)); + ASSERT_EQ(std::nullopt, target().earliestPresentTime()); } TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) { @@ -240,7 +295,8 @@ TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) { // The target is not early while past present fences are pending. for (int n = 3; n-- > 0;) { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); - EXPECT_FALSE(target().wouldPresentEarly(kPeriod)); + EXPECT_FALSE(wouldPresentEarly(kPeriod)); + EXPECT_FALSE(target().earliestPresentTime()); } Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); @@ -248,12 +304,18 @@ TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) { fence->signalForTest(frameBeginTime.ns()); // The target is two VSYNCs ahead, so the past present fence is still pending. - EXPECT_FALSE(target().wouldPresentEarly(kPeriod)); + EXPECT_FALSE(wouldPresentEarly(kPeriod)); + EXPECT_FALSE(target().earliestPresentTime()); { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); } + Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); + // The target is early if the past present fence was signaled. - EXPECT_TRUE(target().wouldPresentEarly(kPeriod)); + EXPECT_TRUE(wouldPresentEarly(kPeriod)); + ASSERT_NE(std::nullopt, target().earliestPresentTime()); + EXPECT_EQ(*target().earliestPresentTime(), + target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration); } TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) { @@ -264,7 +326,10 @@ TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) { const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate, kRefreshRate); // The target is more than two VSYNCs ahead, but present fences are not tracked that far back. - EXPECT_TRUE(target().wouldPresentEarly(kPeriod)); + EXPECT_TRUE(wouldPresentEarly(kPeriod)); + EXPECT_TRUE(target().earliestPresentTime()); + EXPECT_EQ(*target().earliestPresentTime(), + target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration); } TEST_F(FrameTargeterTest, detectsMissedFrames) { diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 2503efcc07..c3bfb58786 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1108,7 +1108,7 @@ void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info outMode.peakRefreshRate = peakFps.getValue(); outMode.vsyncRate = mode->getVsyncRate().getValue(); - const auto vsyncConfigSet = mVsyncConfiguration->getConfigsForRefreshRate( + const auto vsyncConfigSet = mScheduler->getVsyncConfiguration().getConfigsForRefreshRate( Fps::fromValue(outMode.peakRefreshRate)); outMode.appVsyncOffset = vsyncConfigSet.late.appOffset; outMode.sfVsyncOffset = vsyncConfigSet.late.sfOffset; @@ -1267,7 +1267,7 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool mScheduler->modulateVsync(displayId, &VsyncModulator::onRefreshRateChangeInitiated); if (displayId == mActiveDisplayId) { - updatePhaseConfiguration(mode.fps); + mScheduler->updatePhaseConfiguration(mode.fps); } mScheduler->setModeChangePending(true); @@ -1276,8 +1276,7 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool mScheduler->setRenderRate(displayId, mode.fps); if (displayId == mActiveDisplayId) { - updatePhaseConfiguration(mode.fps); - mRefreshRateStats->setRefreshRate(mode.fps); + mScheduler->updatePhaseConfiguration(mode.fps); } if (emitEvent) { @@ -1369,8 +1368,7 @@ void SurfaceFlinger::finalizeDisplayModeChange(DisplayDevice& display) { activeMode.fps); if (displayId == mActiveDisplayId) { - mRefreshRateStats->setRefreshRate(activeMode.fps); - updatePhaseConfiguration(activeMode.fps); + mScheduler->updatePhaseConfiguration(activeMode.fps); } if (pendingModeOpt->emitEvent) { @@ -1399,7 +1397,7 @@ void SurfaceFlinger::applyActiveMode(const sp<DisplayDevice>& display) { mScheduler->setRenderRate(displayId, renderFps); if (displayId == mActiveDisplayId) { - updatePhaseConfiguration(renderFps); + mScheduler->updatePhaseConfiguration(renderFps); } } @@ -2683,6 +2681,8 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( if (const auto display = getCompositionDisplayLocked(id)) { refreshArgs.outputs.push_back(display); } + + refreshArgs.frameTargets.try_emplace(id, &targeter->target()); } std::vector<DisplayId> displayIds; @@ -2751,23 +2751,10 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay); } - const Period minFramePeriod = mScheduler->getVsyncSchedule()->minFramePeriod(); - - if (!getHwComposer().getComposer()->isSupported( - Hwc2::Composer::OptionalFeature::ExpectedPresentTime) && - pacesetterTarget.wouldPresentEarly(minFramePeriod)) { - const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration; - - // TODO(b/255601557): Calculate and pass per-display values for each FrameTarget. - refreshArgs.earliestPresentTime = - pacesetterTarget.previousFrameVsyncTime(minFramePeriod) - hwcMinWorkDuration; - } - const TimePoint expectedPresentTime = pacesetterTarget.expectedPresentTime(); // TODO(b/255601557) Update frameInterval per display refreshArgs.frameInterval = mScheduler->getNextFrameInterval(pacesetterId, expectedPresentTime); refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime(); - refreshArgs.expectedPresentTime = expectedPresentTime.ns(); refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0; { auto& notifyExpectedPresentData = mNotifyExpectedPresentMap[pacesetterId]; @@ -3064,7 +3051,8 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, const auto schedule = mScheduler->getVsyncSchedule(); const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime); const Period vsyncPeriod = schedule->period(); - const nsecs_t vsyncPhase = mVsyncConfiguration->getCurrentConfigs().late.sfOffset; + const nsecs_t vsyncPhase = + mScheduler->getVsyncConfiguration().getCurrentConfigs().late.sfOffset; const CompositorTiming compositorTiming(vsyncDeadline.ns(), vsyncPeriod.ns(), vsyncPhase, presentLatency.ns()); @@ -3741,7 +3729,7 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, // TODO(b/175678251) Call a listener instead. if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) { - resetPhaseConfiguration(display->getActiveMode().fps); + mScheduler->resetPhaseConfiguration(display->getActiveMode().fps); } } return; @@ -3777,15 +3765,6 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, } } -void SurfaceFlinger::resetPhaseConfiguration(Fps refreshRate) { - // Cancel the pending refresh rate change, if any, before updating the phase configuration. - mScheduler->vsyncModulator().cancelRefreshRateChange(); - - mVsyncConfiguration->reset(); - updatePhaseConfiguration(refreshRate); - mRefreshRateStats->setRefreshRate(refreshRate); -} - void SurfaceFlinger::processDisplayChangesLocked() { // here we take advantage of Vector's copy-on-write semantics to // improve performance by skipping the transaction entirely when @@ -4202,10 +4181,6 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { const auto activeMode = display->refreshRateSelector().getActiveMode(); const Fps activeRefreshRate = activeMode.fps; - mRefreshRateStats = - std::make_unique<RefreshRateStats>(*mTimeStats, activeRefreshRate, hal::PowerMode::OFF); - - mVsyncConfiguration = getFactory().createVsyncConfiguration(activeRefreshRate); FeatureFlags features; @@ -4231,12 +4206,14 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { if (mBackpressureGpuComposition) { features |= Feature::kBackpressureGpuComposition; } - - auto modulatorPtr = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs()); + if (getHwComposer().getComposer()->isSupported( + Hwc2::Composer::OptionalFeature::ExpectedPresentTime)) { + features |= Feature::kExpectedPresentTime; + } mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this), static_cast<ISchedulerCallback&>(*this), features, - std::move(modulatorPtr), + getFactory(), activeRefreshRate, *mTimeStats, static_cast<IVsyncTrackerCallback&>(*this)); mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector()); if (FlagManager::getInstance().vrr_config()) { @@ -4244,7 +4221,7 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { } mScheduler->startTimers(); - const auto configs = mVsyncConfiguration->getCurrentConfigs(); + const auto configs = mScheduler->getVsyncConfiguration().getCurrentConfigs(); mAppConnectionHandle = mScheduler->createEventThread(Scheduler::Cycle::Render, @@ -4266,12 +4243,6 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this); } -void SurfaceFlinger::updatePhaseConfiguration(Fps refreshRate) { - mVsyncConfiguration->setRefreshRateFps(refreshRate); - mScheduler->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs(), - refreshRate.getPeriod()); -} - void SurfaceFlinger::doCommitTransactions() { ATRACE_CALL(); @@ -6026,7 +5997,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: if (displayId == mActiveDisplayId) { mTimeStats->setPowerMode(mode); - mRefreshRateStats->setPowerMode(mode); + mScheduler->setActiveDisplayPowerModeForRefreshRateStats(mode); } mScheduler->setDisplayPowerMode(displayId, mode); @@ -6191,10 +6162,6 @@ void SurfaceFlinger::dumpScheduler(std::string& result) const { dumper.dump("debugDisplayModeSetByBackdoor"sv, mDebugDisplayModeSetByBackdoor); dumper.eol(); - mRefreshRateStats->dump(result); - dumper.eol(); - - mVsyncConfiguration->dump(result); StringAppendF(&result, " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n", @@ -8634,7 +8601,8 @@ uint32_t SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t ui } int SurfaceFlinger::getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const { - const auto vsyncConfig = mVsyncConfiguration->getConfigsForRefreshRate(refreshRate).late; + const auto vsyncConfig = + mScheduler->getVsyncConfiguration().getConfigsForRefreshRate(refreshRate).late; const auto presentLatency = vsyncConfig.appWorkDuration + vsyncConfig.sfWorkDuration; return calculateMaxAcquiredBufferCount(refreshRate, presentLatency); } @@ -8727,7 +8695,7 @@ void SurfaceFlinger::onActiveDisplayChangedLocked(const DisplayDevice* inactiveD mActiveDisplayId = activeDisplay.getPhysicalId(); activeDisplay.getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true); - resetPhaseConfiguration(activeDisplay.getActiveMode().fps); + mScheduler->resetPhaseConfiguration(activeDisplay.getActiveMode().fps); // TODO(b/255635711): Check for pending mode changes on other displays. mScheduler->setModeChangePending(false); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 72003cd65e..5846214d97 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -82,7 +82,6 @@ #include "MutexUtils.h" #include "Scheduler/ISchedulerCallback.h" #include "Scheduler/RefreshRateSelector.h" -#include "Scheduler/RefreshRateStats.h" #include "Scheduler/Scheduler.h" #include "SurfaceFlingerFactory.h" #include "ThreadContext.h" @@ -785,9 +784,6 @@ private: void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock); - void resetPhaseConfiguration(Fps) REQUIRES(mStateLock, kMainThreadContext); - void updatePhaseConfiguration(Fps) REQUIRES(mStateLock); - /* * Transactions */ @@ -1374,10 +1370,6 @@ private: scheduler::ConnectionHandle mAppConnectionHandle; scheduler::ConnectionHandle mSfConnectionHandle; - // Stores phase offsets configured per refresh rate. - std::unique_ptr<scheduler::VsyncConfiguration> mVsyncConfiguration; - - std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats; scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext); bool mLumaSampling = true; diff --git a/services/surfaceflinger/Utils/Dumper.h b/services/surfaceflinger/Utils/Dumper.h index ee942177e5..62d2ebb76d 100644 --- a/services/surfaceflinger/Utils/Dumper.h +++ b/services/surfaceflinger/Utils/Dumper.h @@ -35,6 +35,8 @@ public: void eol() { mOut += '\n'; } + std::string& out() { return mOut; } + void dump(std::string_view name, std::string_view value = {}) { using namespace std::string_view_literals; diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h index 4fc39cc912..fa79956131 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -224,20 +224,13 @@ namespace scheduler { class TestableScheduler : public Scheduler, private ICompositor { public: - TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr, - sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback, - IVsyncTrackerCallback& vsyncTrackerCallback) - : TestableScheduler(std::make_unique<android::mock::VsyncController>(), - std::make_shared<android::mock::VSyncTracker>(), selectorPtr, - std::move(modulatorPtr), callback, vsyncTrackerCallback) {} - TestableScheduler(std::unique_ptr<VsyncController> controller, VsyncSchedule::TrackerPtr tracker, std::shared_ptr<RefreshRateSelector> selectorPtr, - sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback, - IVsyncTrackerCallback& vsyncTrackerCallback) - : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr), - vsyncTrackerCallback) { + surfaceflinger::Factory& factory, TimeStats& timeStats, + ISchedulerCallback& callback, IVsyncTrackerCallback& vsyncTrackerCallback) + : Scheduler(*this, callback, Feature::kContentDetection, factory, + selectorPtr->getActiveMode().fps, timeStats, vsyncTrackerCallback) { const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); registerDisplayInternal(displayId, std::move(selectorPtr), std::shared_ptr<VsyncSchedule>( @@ -613,7 +606,14 @@ public: mFlinger->commitTransactions(); mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp)); - scheduler::FrameTargeter frameTargeter(displayId, mFdp.ConsumeBool()); + scheduler::FeatureFlags flags; + if (mFdp.ConsumeBool()) { + flags |= scheduler::Feature::kBackpressureGpuComposition; + } + if (mFdp.ConsumeBool()) { + flags |= scheduler::Feature::kExpectedPresentTime; + } + scheduler::FrameTargeter frameTargeter(displayId, flags); mFlinger->onCompositionPresented(displayId, ftl::init::map(displayId, &frameTargeter), mFdp.ConsumeIntegral<nsecs_t>()); } @@ -671,19 +671,11 @@ public: } mRefreshRateSelector = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60); - const auto fps = mRefreshRateSelector->getActiveMode().modePtr->getVsyncRate(); - mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps); - - mFlinger->mRefreshRateStats = - std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps, - hal::PowerMode::OFF); - - auto modulatorPtr = sp<scheduler::VsyncModulator>::make( - mFlinger->mVsyncConfiguration->getCurrentConfigs()); mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), mRefreshRateSelector, - std::move(modulatorPtr), *(callback ?: this), + mFactory, *mFlinger->mTimeStats, + *(callback ?: this), *(vsyncTrackerCallback ?: this)); mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index 649ad252d8..8a5500f167 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -24,6 +24,7 @@ #include "Scheduler/OneShotTimer.h" #include "Scheduler/RefreshRateSelector.h" +#include "Scheduler/RefreshRateStats.h" #include "Scheduler/VSyncDispatchTimerQueue.h" #include "Scheduler/VSyncPredictor.h" #include "Scheduler/VSyncReactor.h" @@ -425,7 +426,15 @@ void SchedulerFuzzer::fuzzPresentLatencyTracker() { } void SchedulerFuzzer::fuzzFrameTargeter() { - scheduler::FrameTargeter frameTargeter(kDisplayId, mFdp.ConsumeBool()); + scheduler::FeatureFlags flags; + if (mFdp.ConsumeBool()) { + flags |= scheduler::Feature::kBackpressureGpuComposition; + } + if (mFdp.ConsumeBool()) { + flags |= scheduler::Feature::kExpectedPresentTime; + } + + scheduler::FrameTargeter frameTargeter(kDisplayId, flags); const struct VsyncSource final : scheduler::IVsyncSource { explicit VsyncSource(FuzzedDataProvider& fuzzer) : fuzzer(fuzzer) {} @@ -441,7 +450,8 @@ void SchedulerFuzzer::fuzzFrameTargeter() { frameTargeter.beginFrame({.frameBeginTime = getFuzzedTimePoint(mFdp), .vsyncId = getFuzzedVsyncId(mFdp), .expectedVsyncTime = getFuzzedTimePoint(mFdp), - .sfWorkDuration = getFuzzedDuration(mFdp)}, + .sfWorkDuration = getFuzzedDuration(mFdp), + .hwcMinWorkDuration = getFuzzedDuration(mFdp)}, vsyncSource); frameTargeter.setPresentFence(makeFakeFence()); diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index c75f90222a..5809ea02f8 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -118,6 +118,7 @@ cc_test { "RefreshRateSelectorTest.cpp", "RefreshRateStatsTest.cpp", "RegionSamplingTest.cpp", + "TestableScheduler.cpp", "TimeStatsTest.cpp", "FrameTracerTest.cpp", "TransactionApplicationTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 4e8a609be6..45db0c56a9 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -55,6 +55,9 @@ constexpr PhysicalDisplayId DISPLAY_ID_64BIT = constexpr std::chrono::duration VSYNC_PERIOD(16ms); +constexpr int HDCP_V1 = 2; +constexpr int HDCP_V2 = 3; + } // namespace class EventThreadTest : public testing::Test, public IEventThreadCallback { @@ -833,6 +836,19 @@ TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) { expectVSyncCallbackScheduleReceived(true); } +TEST_F(EventThreadTest, postHcpLevelsChanged) { + setupEventThread(); + + mThread->onHdcpLevelsChanged(EXTERNAL_DISPLAY_ID, HDCP_V1, HDCP_V2); + auto args = mConnectionEventCallRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + const auto& event = std::get<0>(args.value()); + EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HDCP_LEVELS_CHANGE, event.header.type); + EXPECT_EQ(EXTERNAL_DISPLAY_ID, event.header.displayId); + EXPECT_EQ(HDCP_V1, event.hdcpLevelsChange.connectedLevel); + EXPECT_EQ(HDCP_V2, event.hdcpLevelsChange.maxLevel); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp index e9d231956f..734fddb2a3 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp @@ -167,10 +167,10 @@ protected: mock::SchedulerCallback mSchedulerCallback; mock::VsyncTrackerCallback mVsyncTrackerCallback; - TestableScheduler* mScheduler = - new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback); - TestableSurfaceFlinger mFlinger; + + TestableScheduler* mScheduler = + new TestableScheduler(mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback); }; namespace { diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index 0ae3ca33b1..9456e37f5c 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -148,10 +148,9 @@ protected: mock::SchedulerCallback mSchedulerCallback; mock::VsyncTrackerCallback mVsyncTrackerCallback; - TestableScheduler* mScheduler = - new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback); - TestableSurfaceFlinger mFlinger; + TestableScheduler* mScheduler = + new TestableScheduler(mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback); }; namespace { diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp index 07a522ac4e..22cfbd8d8a 100644 --- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp @@ -65,9 +65,9 @@ protected: DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; mock::VsyncTrackerCallback mVsyncTrackerCallback; - TestableScheduler* mScheduler = - new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback); TestableSurfaceFlinger mFlinger; + TestableScheduler* mScheduler = + new TestableScheduler(mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback); }; namespace { diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 7fdca716fb..6986689463 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -96,15 +96,14 @@ protected: mock::SchedulerCallback mSchedulerCallback; mock::VsyncTrackerCallback mVsyncTrackerCallback; + TestableSurfaceFlinger mFlinger; TestableScheduler* mScheduler = - new TestableScheduler{mSelector, mSchedulerCallback, mVsyncTrackerCallback}; + new TestableScheduler{mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback}; surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder; ConnectionHandle mConnectionHandle; MockEventThread* mEventThread; sp<MockEventThreadConnection> mEventThreadConnection; - - TestableSurfaceFlinger mFlinger; }; SchedulerTest::SchedulerTest() { @@ -575,7 +574,8 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) { TestableScheduler scheduler{std::make_unique<android::mock::VsyncController>(), vrrTracker, vrrSelectorPtr, - sp<VsyncModulator>::make(VsyncConfigSet{}), + mFlinger.getFactory(), + mFlinger.getTimeStats(), mSchedulerCallback, mVsyncTrackerCallback}; diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.cpp b/services/surfaceflinger/tests/unittests/TestableScheduler.cpp new file mode 100644 index 0000000000..e0b736677d --- /dev/null +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.cpp @@ -0,0 +1,31 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TestableScheduler.h" +#include "TestableSurfaceFlinger.h" + +namespace android::scheduler { + +TestableScheduler::TestableScheduler(RefreshRateSelectorPtr selectorPtr, + TestableSurfaceFlinger& testableSurfaceFlinger, + ISchedulerCallback& callback, + IVsyncTrackerCallback& vsyncTrackerCallback) + : TestableScheduler(std::make_unique<android::mock::VsyncController>(), + std::make_shared<android::mock::VSyncTracker>(), std::move(selectorPtr), + testableSurfaceFlinger.getFactory(), + testableSurfaceFlinger.getTimeStats(), callback, vsyncTrackerCallback) {} + +} // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 2a1b88e6fa..6213713f3a 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -32,25 +32,27 @@ #include "mock/MockVSyncTracker.h" #include "mock/MockVsyncController.h" +namespace android { +class TestableSurfaceFlinger; +} // namespace android + namespace android::scheduler { class TestableScheduler : public Scheduler, private ICompositor { public: - TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback, - IVsyncTrackerCallback& vsyncTrackerCallback) - : TestableScheduler(std::make_unique<mock::VsyncController>(), - std::make_shared<mock::VSyncTracker>(), std::move(selectorPtr), - sp<VsyncModulator>::make(VsyncConfigSet{}), callback, - vsyncTrackerCallback) {} + TestableScheduler(RefreshRateSelectorPtr selectorPtr, + TestableSurfaceFlinger& testableSurfaceFlinger, ISchedulerCallback& callback, + IVsyncTrackerCallback& vsyncTrackerCallback); TestableScheduler(std::unique_ptr<VsyncController> controller, std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr, - sp<VsyncModulator> modulatorPtr, ISchedulerCallback& schedulerCallback, + surfaceflinger::Factory& factory, TimeStats& timeStats, + ISchedulerCallback& schedulerCallback, IVsyncTrackerCallback& vsyncTrackerCallback) : Scheduler(*this, schedulerCallback, (FeatureFlags)Feature::kContentDetection | Feature::kSmallDirtyContentDetection, - std::move(modulatorPtr), vsyncTrackerCallback) { + factory, selectorPtr->getActiveMode().fps, timeStats, vsyncTrackerCallback) { const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); registerDisplay(displayId, std::move(selectorPtr), std::move(controller), std::move(tracker)); diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 27030d1027..f00eacce8b 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -216,6 +216,10 @@ public: using DisplayModesVariant = std::variant<DefaultDisplayMode, RefreshRateSelectorPtr>; + surfaceflinger::Factory& getFactory() { return mFactory; } + + TimeStats& getTimeStats() { return *mFlinger->mTimeStats; } + void setupScheduler( std::unique_ptr<scheduler::VsyncController> vsyncController, std::shared_ptr<scheduler::VSyncTracker> vsyncTracker, @@ -234,13 +238,6 @@ public: }, [](RefreshRateSelectorPtr selectorPtr) { return selectorPtr; }); - const auto fps = selectorPtr->getActiveMode().fps; - mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps); - - mFlinger->mRefreshRateStats = - std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps, - hal::PowerMode::OFF); - mTokenManager = std::make_unique<frametimeline::impl::TokenManager>(); using ISchedulerCallback = scheduler::ISchedulerCallback; @@ -254,23 +251,21 @@ public: ? static_cast<VsyncTrackerCallback&>(mNoOpVsyncTrackerCallback) : static_cast<VsyncTrackerCallback&>(mVsyncTrackerCallback); - auto modulatorPtr = sp<scheduler::VsyncModulator>::make( - mFlinger->mVsyncConfiguration->getCurrentConfigs()); - if (useNiceMock) { mScheduler = new testing::NiceMock<scheduler::TestableScheduler>(std::move(vsyncController), std::move(vsyncTracker), std::move(selectorPtr), - std::move(modulatorPtr), + mFactory, + *mFlinger->mTimeStats, schedulerCallback, vsyncTrackerCallback); } else { mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), - std::move(selectorPtr), - std::move(modulatorPtr), - schedulerCallback, vsyncTrackerCallback); + std::move(selectorPtr), mFactory, + *mFlinger->mTimeStats, schedulerCallback, + vsyncTrackerCallback); } mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), *mTokenManager, 0ms); @@ -391,13 +386,14 @@ public: LOG_ALWAYS_FATAL_IF(!displayIdOpt); const auto displayId = *displayIdOpt; - constexpr bool kBackpressureGpuComposition = true; - scheduler::FrameTargeter frameTargeter(displayId, kBackpressureGpuComposition); + scheduler::FrameTargeter frameTargeter(displayId, + scheduler::Feature::kBackpressureGpuComposition); frameTargeter.beginFrame({.frameBeginTime = frameTime, .vsyncId = vsyncId, .expectedVsyncTime = expectedVsyncTime, - .sfWorkDuration = 10ms}, + .sfWorkDuration = 10ms, + .hwcMinWorkDuration = 10ms}, *mScheduler->getVsyncSchedule()); scheduler::FrameTargets targets; diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 0df5e77181..6b3c37932d 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -1762,6 +1762,8 @@ VkResult CreateSwapchainKHR(VkDevice device, } int query_value; + // TODO: Now that we are calling into GPDSC2 directly, this query may be redundant + // the call to std::max(min_buffer_count, num_images) may be redundant as well err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); if (err != android::OK || query_value < 0) { @@ -1778,12 +1780,33 @@ VkResult CreateSwapchainKHR(VkDevice device, // with extra images (which they can't actually use!). const uint32_t min_buffer_count = min_undequeued_buffers + 1; - uint32_t num_images; - if (create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) { - num_images = std::max(3u, create_info->minImageCount); - } else { - num_images = create_info->minImageCount; - } + // Call into GPDSC2 to get the minimum and maximum allowable buffer count for the surface of + // interest. This step is only necessary if the app requests a number of images + // (create_info->minImageCount) that is less or more than the surface capabilities. + // An app should be calling GPDSC2 and using those values to set create_info, but in the + // event that the app has hard-coded image counts an error can occur + VkSurfacePresentModeEXT present_mode = { + VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT, + nullptr, + create_info->presentMode + }; + VkPhysicalDeviceSurfaceInfo2KHR surface_info2 = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR, + &present_mode, + create_info->surface + }; + VkSurfaceCapabilities2KHR surface_capabilities2 = { + VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR, + nullptr, + {}, + }; + result = GetPhysicalDeviceSurfaceCapabilities2KHR(GetData(device).driver_physical_device, + &surface_info2, &surface_capabilities2); + + uint32_t num_images = create_info->minImageCount; + num_images = std::clamp(num_images, + surface_capabilities2.surfaceCapabilities.minImageCount, + surface_capabilities2.surfaceCapabilities.maxImageCount); const uint32_t buffer_count = std::max(min_buffer_count, num_images); err = native_window_set_buffer_count(window, buffer_count); |