Loading cmds/evemu-record/README.md 0 → 100644 +48 −0 Original line number Diff line number Diff line # `evemu-record` This is a Rust implementation of the `evemu-record` command from the [FreeDesktop project's evemu suite][FreeDesktop]. It records the descriptor and events produced by a single input device in a [simple text-based format][format] that can be replayed using the [`uinput` command on Android][uinput] or the FreeDesktop evemu tools on other Linux-based platforms. It is included by default with `userdebug` and `eng` builds of Android. The command-line interface is the same as that of the FreeDesktop version, except for Android-specific features. For usage instructions, run `evemu-record --help`. ## Usage example From a computer connected to the device over ADB, you can start a recording: ``` $ adb shell evemu-record > my-recording.evemu Available devices: /dev/input/event0: gpio_keys /dev/input/event1: s2mpg12-power-keys /dev/input/event2: NVTCapacitiveTouchScreen /dev/input/event3: NVTCapacitivePen /dev/input/event4: uinput-folio /dev/input/event5: ACME Touchpad Select the device event number [0-5]: 5 ``` ...then use the input device for a while, and press Ctrl+C to finish. You will now have a `my-recording.evemu` file that you can examine in a text editor. To replay it, use the [`uinput` command][uinput]: ``` $ adb shell uinput - < my-recording.evemu ``` ## Android-specific features ### Timestamp bases By default, event timestamps are recorded relative to the time of the first event received during the recording. Passing `--timestamp-base=boot` causes the timestamps to be recorded relative to the system boot time instead. While this does not affect the playback of the recording, it can be useful for matching recorded events with other logs that use such timestamps, such as `dmesg` or the touchpad gesture debug logs emitted by `TouchpadInputMapper`. [FreeDesktop]: https://gitlab.freedesktop.org/libevdev/evemu [format]: https://gitlab.freedesktop.org/libevdev/evemu#device-description-format [uinput]: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/cmds/uinput/README.md data/etc/wearable_core_hardware.xml +1 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ <feature name="android.hardware.security.model.compatible" /> <!-- basic system services --> <feature name="android.software.credentials" /> <feature name="android.software.home_screen" /> <feature name="android.software.secure_lock_screen" /> Loading libs/gui/BufferQueueProducer.cpp +15 −13 Original line number Diff line number Diff line Loading @@ -887,6 +887,9 @@ status_t BufferQueueProducer::queueBuffer(int slot, int callbackTicket = 0; uint64_t currentFrameNumber = 0; BufferItem item; int connectedApi; sp<Fence> lastQueuedFence; { // Autolock scope std::lock_guard<std::mutex> lock(mCore->mMutex); Loading Loading @@ -1056,6 +1059,13 @@ status_t BufferQueueProducer::queueBuffer(int slot, callbackTicket = mNextCallbackTicket++; VALIDATE_CONSISTENCY(); connectedApi = mCore->mConnectedApi; lastQueuedFence = std::move(mLastQueueBufferFence); mLastQueueBufferFence = std::move(acquireFence); mLastQueuedCrop = item.mCrop; mLastQueuedTransform = item.mTransform; } // Autolock scope // It is okay not to clear the GraphicBuffer when the consumer is SurfaceFlinger because Loading @@ -1079,9 +1089,6 @@ status_t BufferQueueProducer::queueBuffer(int slot, // Call back without the main BufferQueue lock held, but with the callback // lock held so we can ensure that callbacks occur in order int connectedApi; sp<Fence> lastQueuedFence; { // scope for the lock std::unique_lock<std::mutex> lock(mCallbackMutex); while (callbackTicket != mCurrentCallbackTicket) { Loading @@ -1094,13 +1101,6 @@ status_t BufferQueueProducer::queueBuffer(int slot, frameReplacedListener->onFrameReplaced(item); } connectedApi = mCore->mConnectedApi; lastQueuedFence = std::move(mLastQueueBufferFence); mLastQueueBufferFence = std::move(acquireFence); mLastQueuedCrop = item.mCrop; mLastQueuedTransform = item.mTransform; ++mCurrentCallbackTicket; mCallbackCondition.notify_all(); } Loading Loading @@ -1653,9 +1653,10 @@ status_t BufferQueueProducer::setLegacyBufferDrop(bool drop) { status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]) { ATRACE_CALL(); BQ_LOGV("getLastQueuedBuffer"); std::lock_guard<std::mutex> lock(mCore->mMutex); BQ_LOGV("getLastQueuedBuffer, slot=%d", mCore->mLastQueuedSlot); if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) { *outBuffer = nullptr; *outFence = Fence::NO_FENCE; Loading @@ -1679,10 +1680,11 @@ status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, Rect* outRect, uint32_t* outTransform) { ATRACE_CALL(); BQ_LOGV("getLastQueuedBuffer"); std::lock_guard<std::mutex> lock(mCore->mMutex); if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) { BQ_LOGV("getLastQueuedBuffer, slot=%d", mCore->mLastQueuedSlot); if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT || mSlots[mCore->mLastQueuedSlot].mBufferState.isDequeued()) { *outBuffer = nullptr; *outFence = Fence::NO_FENCE; return NO_ERROR; Loading services/inputflinger/dispatcher/InputDispatcher.cpp +112 −52 Original line number Diff line number Diff line Loading @@ -804,6 +804,20 @@ int32_t getUserActivityEventType(const EventEntry& eventEntry) { } } std::pair<bool /*cancelPointers*/, bool /*cancelNonPointers*/> expandCancellationMode( CancelationOptions::Mode mode) { switch (mode) { case CancelationOptions::Mode::CANCEL_ALL_EVENTS: return {true, true}; case CancelationOptions::Mode::CANCEL_POINTER_EVENTS: return {true, false}; case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS: return {false, true}; case CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS: return {false, true}; } } } // namespace // --- InputDispatcher --- Loading Loading @@ -2078,7 +2092,9 @@ void InputDispatcher::cancelEventsForAnrLocked(const std::shared_ptr<Connection> if (connection->status == Connection::Status::NORMAL) { CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "application not responding"); synthesizeCancelationEventsForConnectionLocked(connection, options); synthesizeCancelationEventsForConnectionLocked(connection, options, getWindowHandleLocked( connection->getToken())); } } Loading Loading @@ -3328,7 +3344,13 @@ void InputDispatcher::enqueueDispatchEntryAndStartDispatchCycleLocked( void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connection>& connection, std::shared_ptr<const EventEntry> eventEntry, const InputTarget& inputTarget) { // TODO(b/210460522): Verify all targets excluding global monitors are associated with a window. const bool isKeyOrMotion = eventEntry->type == EventEntry::Type::KEY || eventEntry->type == EventEntry::Type::MOTION; if (isKeyOrMotion && !inputTarget.windowHandle && !connection->monitor) { LOG(FATAL) << "All InputTargets for non-monitors must be associated with a window; target: " << inputTarget << " connection: " << connection->getInputChannelName() << " entry: " << eventEntry->getDescription(); } // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. std::unique_ptr<DispatchEntry> dispatchEntry = Loading Loading @@ -3977,22 +3999,78 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( const CancelationOptions& options) { for (const auto& [token, connection] : mConnectionsByToken) { synthesizeCancelationEventsForConnectionLocked(connection, options); // Cancel windows (i.e. non-monitors). // A channel must have at least one window to receive any input. If a window was removed, the // event streams directed to the window will already have been canceled during window removal. // So there is no need to generate cancellations for connections without any windows. const auto [cancelPointers, cancelNonPointers] = expandCancellationMode(options.mode); // Generate cancellations for touched windows first. This is to avoid generating cancellations // through a non-touched window if there are more than one window for an input channel. if (cancelPointers) { for (const auto& [displayId, touchState] : mTouchStatesByDisplay) { if (options.displayId.has_value() && options.displayId != displayId) { continue; } for (const auto& touchedWindow : touchState.windows) { synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options); } } } // Follow up by generating cancellations for all windows, because we don't explicitly track // the windows that have an ongoing focus event stream. if (cancelNonPointers) { for (const auto& [_, handles] : mWindowHandlesByDisplay) { for (const auto& windowHandle : handles) { synthesizeCancelationEventsForWindowLocked(windowHandle, options); } } } // Cancel monitors. synthesizeCancelationEventsForMonitorsLocked(options); } void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( const CancelationOptions& options) { for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) { for (const Monitor& monitor : monitors) { synthesizeCancelationEventsForConnectionLocked(monitor.connection, options); synthesizeCancelationEventsForConnectionLocked(monitor.connection, options, /*window=*/nullptr); } } } void InputDispatcher::synthesizeCancelationEventsForWindowLocked( const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options, const std::shared_ptr<Connection>& connection) { if (windowHandle == nullptr) { LOG(FATAL) << __func__ << ": Window handle must not be null"; } if (connection) { // The connection can be optionally provided to avoid multiple lookups. if (windowHandle->getToken() != connection->getToken()) { LOG(FATAL) << __func__ << ": Wrong connection provided for window: " << windowHandle->getName(); } } std::shared_ptr<Connection> resolvedConnection = connection ? connection : getConnectionLocked(windowHandle->getToken()); if (!resolvedConnection) { LOG(DEBUG) << __func__ << "No connection found for window: " << windowHandle->getName(); return; } synthesizeCancelationEventsForConnectionLocked(resolvedConnection, options, windowHandle); } void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( const std::shared_ptr<Connection>& connection, const CancelationOptions& options) { const std::shared_ptr<Connection>& connection, const CancelationOptions& options, const sp<WindowInfoHandle>& window) { if (!connection->monitor && window == nullptr) { LOG(FATAL) << __func__ << ": Cannot send event to non-monitor channel without a window - channel: " << connection->getInputChannelName(); } if (connection->status != Connection::Status::NORMAL) { return; } Loading Loading @@ -4029,10 +4107,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( switch (cancelationEventEntry->type) { case EventEntry::Type::KEY: { const auto& keyEntry = static_cast<const KeyEntry&>(*cancelationEventEntry); const std::optional<int32_t> targetDisplay = keyEntry.displayId != ADISPLAY_ID_NONE ? std::make_optional(keyEntry.displayId) : std::nullopt; if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) { if (window) { addWindowTargetLocked(window, InputTarget::DispatchMode::AS_IS, /*targetFlags=*/{}, keyEntry.downTime, targets); } else { Loading @@ -4043,11 +4118,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } case EventEntry::Type::MOTION: { const auto& motionEntry = static_cast<const MotionEntry&>(*cancelationEventEntry); const std::optional<int32_t> targetDisplay = motionEntry.displayId != ADISPLAY_ID_NONE ? std::make_optional(motionEntry.displayId) : std::nullopt; if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) { if (window) { std::bitset<MAX_POINTER_ID + 1> pointerIds; for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.getPointerCount(); pointerIndex++) { Loading Loading @@ -4121,7 +4192,12 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( connection->getInputChannelName().c_str(), downEvents.size()); } sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(connection->getToken()); const auto [_, touchedWindowState, displayId] = findTouchStateWindowAndDisplayLocked(connection->getToken()); if (touchedWindowState == nullptr) { LOG(FATAL) << __func__ << ": Touch state is out of sync: No touched window for token"; } const auto& windowHandle = touchedWindowState->windowHandle; const bool wasEmpty = connection->outboundQueue.empty(); for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) { Loading Loading @@ -4175,17 +4251,6 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( } } void InputDispatcher::synthesizeCancelationEventsForWindowLocked( const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options) { if (windowHandle != nullptr) { std::shared_ptr<Connection> wallpaperConnection = getConnectionLocked(windowHandle->getToken()); if (wallpaperConnection != nullptr) { synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, options); } } } std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( const MotionEntry& originalMotionEntry, std::bitset<MAX_POINTER_ID + 1> pointerIds, nsecs_t splitDownTime) { Loading Loading @@ -5216,19 +5281,16 @@ void InputDispatcher::setInputWindowsLocked( if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) { LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName() << " in display %" << displayId; std::shared_ptr<Connection> touchedConnection = getConnectionLocked(touchedWindow.windowHandle->getToken()); if (touchedConnection != nullptr) { CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "touched window was removed"); synthesizeCancelationEventsForConnectionLocked(touchedConnection, options); synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options); // Since we are about to drop the touch, cancel the events for the wallpaper as // well. if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) && touchedWindow.windowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow(); synthesizeCancelationEventsForWindowLocked(wallpaper, options); if (const auto& ww = state.getWallpaperWindow(); ww) { synthesizeCancelationEventsForWindowLocked(ww, options); } } state.windows.erase(state.windows.begin() + i); Loading Loading @@ -5515,9 +5577,10 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< } const int32_t deviceId = *deviceIds.begin(); sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId); if (toWindowHandle == nullptr) { ALOGW("Cannot transfer touch because to window not found."); const sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle; const sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId); if (!toWindowHandle) { ALOGW("Cannot transfer touch because the transfer target window was not found."); return false; } Loading @@ -5530,7 +5593,6 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< // Erase old window. ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow->targetFlags; std::vector<PointerProperties> pointers = touchedWindow->getTouchingPointers(deviceId); sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle; state->removeWindowByToken(fromToken); // Add new window. Loading Loading @@ -5562,7 +5624,7 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< fromConnection->inputState.mergePointerStateTo(toConnection->inputState); CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "transferring touch from this window to another window"); synthesizeCancelationEventsForConnectionLocked(fromConnection, options); synthesizeCancelationEventsForWindowLocked(fromWindowHandle, options, fromConnection); synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection, newTargetFlags); Loading Loading @@ -6044,12 +6106,10 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { std::string canceledWindows; for (const TouchedWindow& w : state.windows) { const std::shared_ptr<Connection> connection = getConnectionLocked(w.windowHandle->getToken()); if (connection != nullptr && connection->getToken() != token) { synthesizeCancelationEventsForConnectionLocked(connection, options); if (w.windowHandle->getToken() != token) { synthesizeCancelationEventsForWindowLocked(w.windowHandle, options); canceledWindows += canceledWindows.empty() ? "[" : ", "; canceledWindows += connection->getInputChannelName(); canceledWindows += w.windowHandle->getName(); } } canceledWindows += canceledWindows.empty() ? "[]" : "]"; Loading Loading @@ -6463,7 +6523,7 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl "or is no longer a foreground target, " "canceling previously dispatched fallback key"); options.keyCode = *fallbackKeyCode; synthesizeCancelationEventsForConnectionLocked(connection, options); synthesizeCancelationEventsForWindowLocked(windowHandle, options, connection); } } connection->inputState.removeFallbackKey(originalKeyCode); Loading Loading @@ -6545,7 +6605,7 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS, "canceling fallback, policy no longer desires it"); options.keyCode = *fallbackKeyCode; synthesizeCancelationEventsForConnectionLocked(connection, options); synthesizeCancelationEventsForWindowLocked(windowHandle, options, connection); } fallback = false; Loading services/inputflinger/dispatcher/InputDispatcher.h +9 −6 Original line number Diff line number Diff line Loading @@ -615,18 +615,21 @@ private: REQUIRES(mLock); void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options) REQUIRES(mLock); void synthesizeCancelationEventsForConnectionLocked( const std::shared_ptr<Connection>& connection, const CancelationOptions& options) void synthesizeCancelationEventsForWindowLocked(const sp<gui::WindowInfoHandle>&, const CancelationOptions&, const std::shared_ptr<Connection>& = nullptr) REQUIRES(mLock); // This is a convenience function used to generate cancellation for a connection without having // to check whether it's a monitor or a window. For non-monitors, the window handle must not be // null. Always prefer the "-ForWindow" method above when explicitly dealing with windows. void synthesizeCancelationEventsForConnectionLocked( const std::shared_ptr<Connection>& connection, const CancelationOptions& options, const sp<gui::WindowInfoHandle>& window) REQUIRES(mLock); void synthesizePointerDownEventsForConnectionLocked( const nsecs_t downTime, const std::shared_ptr<Connection>& connection, ftl::Flags<InputTarget::Flags> targetFlags) REQUIRES(mLock); void synthesizeCancelationEventsForWindowLocked( const sp<android::gui::WindowInfoHandle>& windowHandle, const CancelationOptions& options) REQUIRES(mLock); // Splitting motion events across windows. When splitting motion event for a target, // splitDownTime refers to the time of first 'down' event on that particular target std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry, Loading Loading
cmds/evemu-record/README.md 0 → 100644 +48 −0 Original line number Diff line number Diff line # `evemu-record` This is a Rust implementation of the `evemu-record` command from the [FreeDesktop project's evemu suite][FreeDesktop]. It records the descriptor and events produced by a single input device in a [simple text-based format][format] that can be replayed using the [`uinput` command on Android][uinput] or the FreeDesktop evemu tools on other Linux-based platforms. It is included by default with `userdebug` and `eng` builds of Android. The command-line interface is the same as that of the FreeDesktop version, except for Android-specific features. For usage instructions, run `evemu-record --help`. ## Usage example From a computer connected to the device over ADB, you can start a recording: ``` $ adb shell evemu-record > my-recording.evemu Available devices: /dev/input/event0: gpio_keys /dev/input/event1: s2mpg12-power-keys /dev/input/event2: NVTCapacitiveTouchScreen /dev/input/event3: NVTCapacitivePen /dev/input/event4: uinput-folio /dev/input/event5: ACME Touchpad Select the device event number [0-5]: 5 ``` ...then use the input device for a while, and press Ctrl+C to finish. You will now have a `my-recording.evemu` file that you can examine in a text editor. To replay it, use the [`uinput` command][uinput]: ``` $ adb shell uinput - < my-recording.evemu ``` ## Android-specific features ### Timestamp bases By default, event timestamps are recorded relative to the time of the first event received during the recording. Passing `--timestamp-base=boot` causes the timestamps to be recorded relative to the system boot time instead. While this does not affect the playback of the recording, it can be useful for matching recorded events with other logs that use such timestamps, such as `dmesg` or the touchpad gesture debug logs emitted by `TouchpadInputMapper`. [FreeDesktop]: https://gitlab.freedesktop.org/libevdev/evemu [format]: https://gitlab.freedesktop.org/libevdev/evemu#device-description-format [uinput]: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/cmds/uinput/README.md
data/etc/wearable_core_hardware.xml +1 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ <feature name="android.hardware.security.model.compatible" /> <!-- basic system services --> <feature name="android.software.credentials" /> <feature name="android.software.home_screen" /> <feature name="android.software.secure_lock_screen" /> Loading
libs/gui/BufferQueueProducer.cpp +15 −13 Original line number Diff line number Diff line Loading @@ -887,6 +887,9 @@ status_t BufferQueueProducer::queueBuffer(int slot, int callbackTicket = 0; uint64_t currentFrameNumber = 0; BufferItem item; int connectedApi; sp<Fence> lastQueuedFence; { // Autolock scope std::lock_guard<std::mutex> lock(mCore->mMutex); Loading Loading @@ -1056,6 +1059,13 @@ status_t BufferQueueProducer::queueBuffer(int slot, callbackTicket = mNextCallbackTicket++; VALIDATE_CONSISTENCY(); connectedApi = mCore->mConnectedApi; lastQueuedFence = std::move(mLastQueueBufferFence); mLastQueueBufferFence = std::move(acquireFence); mLastQueuedCrop = item.mCrop; mLastQueuedTransform = item.mTransform; } // Autolock scope // It is okay not to clear the GraphicBuffer when the consumer is SurfaceFlinger because Loading @@ -1079,9 +1089,6 @@ status_t BufferQueueProducer::queueBuffer(int slot, // Call back without the main BufferQueue lock held, but with the callback // lock held so we can ensure that callbacks occur in order int connectedApi; sp<Fence> lastQueuedFence; { // scope for the lock std::unique_lock<std::mutex> lock(mCallbackMutex); while (callbackTicket != mCurrentCallbackTicket) { Loading @@ -1094,13 +1101,6 @@ status_t BufferQueueProducer::queueBuffer(int slot, frameReplacedListener->onFrameReplaced(item); } connectedApi = mCore->mConnectedApi; lastQueuedFence = std::move(mLastQueueBufferFence); mLastQueueBufferFence = std::move(acquireFence); mLastQueuedCrop = item.mCrop; mLastQueuedTransform = item.mTransform; ++mCurrentCallbackTicket; mCallbackCondition.notify_all(); } Loading Loading @@ -1653,9 +1653,10 @@ status_t BufferQueueProducer::setLegacyBufferDrop(bool drop) { status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]) { ATRACE_CALL(); BQ_LOGV("getLastQueuedBuffer"); std::lock_guard<std::mutex> lock(mCore->mMutex); BQ_LOGV("getLastQueuedBuffer, slot=%d", mCore->mLastQueuedSlot); if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) { *outBuffer = nullptr; *outFence = Fence::NO_FENCE; Loading @@ -1679,10 +1680,11 @@ status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, Rect* outRect, uint32_t* outTransform) { ATRACE_CALL(); BQ_LOGV("getLastQueuedBuffer"); std::lock_guard<std::mutex> lock(mCore->mMutex); if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) { BQ_LOGV("getLastQueuedBuffer, slot=%d", mCore->mLastQueuedSlot); if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT || mSlots[mCore->mLastQueuedSlot].mBufferState.isDequeued()) { *outBuffer = nullptr; *outFence = Fence::NO_FENCE; return NO_ERROR; Loading
services/inputflinger/dispatcher/InputDispatcher.cpp +112 −52 Original line number Diff line number Diff line Loading @@ -804,6 +804,20 @@ int32_t getUserActivityEventType(const EventEntry& eventEntry) { } } std::pair<bool /*cancelPointers*/, bool /*cancelNonPointers*/> expandCancellationMode( CancelationOptions::Mode mode) { switch (mode) { case CancelationOptions::Mode::CANCEL_ALL_EVENTS: return {true, true}; case CancelationOptions::Mode::CANCEL_POINTER_EVENTS: return {true, false}; case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS: return {false, true}; case CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS: return {false, true}; } } } // namespace // --- InputDispatcher --- Loading Loading @@ -2078,7 +2092,9 @@ void InputDispatcher::cancelEventsForAnrLocked(const std::shared_ptr<Connection> if (connection->status == Connection::Status::NORMAL) { CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "application not responding"); synthesizeCancelationEventsForConnectionLocked(connection, options); synthesizeCancelationEventsForConnectionLocked(connection, options, getWindowHandleLocked( connection->getToken())); } } Loading Loading @@ -3328,7 +3344,13 @@ void InputDispatcher::enqueueDispatchEntryAndStartDispatchCycleLocked( void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connection>& connection, std::shared_ptr<const EventEntry> eventEntry, const InputTarget& inputTarget) { // TODO(b/210460522): Verify all targets excluding global monitors are associated with a window. const bool isKeyOrMotion = eventEntry->type == EventEntry::Type::KEY || eventEntry->type == EventEntry::Type::MOTION; if (isKeyOrMotion && !inputTarget.windowHandle && !connection->monitor) { LOG(FATAL) << "All InputTargets for non-monitors must be associated with a window; target: " << inputTarget << " connection: " << connection->getInputChannelName() << " entry: " << eventEntry->getDescription(); } // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. std::unique_ptr<DispatchEntry> dispatchEntry = Loading Loading @@ -3977,22 +3999,78 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( const CancelationOptions& options) { for (const auto& [token, connection] : mConnectionsByToken) { synthesizeCancelationEventsForConnectionLocked(connection, options); // Cancel windows (i.e. non-monitors). // A channel must have at least one window to receive any input. If a window was removed, the // event streams directed to the window will already have been canceled during window removal. // So there is no need to generate cancellations for connections without any windows. const auto [cancelPointers, cancelNonPointers] = expandCancellationMode(options.mode); // Generate cancellations for touched windows first. This is to avoid generating cancellations // through a non-touched window if there are more than one window for an input channel. if (cancelPointers) { for (const auto& [displayId, touchState] : mTouchStatesByDisplay) { if (options.displayId.has_value() && options.displayId != displayId) { continue; } for (const auto& touchedWindow : touchState.windows) { synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options); } } } // Follow up by generating cancellations for all windows, because we don't explicitly track // the windows that have an ongoing focus event stream. if (cancelNonPointers) { for (const auto& [_, handles] : mWindowHandlesByDisplay) { for (const auto& windowHandle : handles) { synthesizeCancelationEventsForWindowLocked(windowHandle, options); } } } // Cancel monitors. synthesizeCancelationEventsForMonitorsLocked(options); } void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( const CancelationOptions& options) { for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) { for (const Monitor& monitor : monitors) { synthesizeCancelationEventsForConnectionLocked(monitor.connection, options); synthesizeCancelationEventsForConnectionLocked(monitor.connection, options, /*window=*/nullptr); } } } void InputDispatcher::synthesizeCancelationEventsForWindowLocked( const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options, const std::shared_ptr<Connection>& connection) { if (windowHandle == nullptr) { LOG(FATAL) << __func__ << ": Window handle must not be null"; } if (connection) { // The connection can be optionally provided to avoid multiple lookups. if (windowHandle->getToken() != connection->getToken()) { LOG(FATAL) << __func__ << ": Wrong connection provided for window: " << windowHandle->getName(); } } std::shared_ptr<Connection> resolvedConnection = connection ? connection : getConnectionLocked(windowHandle->getToken()); if (!resolvedConnection) { LOG(DEBUG) << __func__ << "No connection found for window: " << windowHandle->getName(); return; } synthesizeCancelationEventsForConnectionLocked(resolvedConnection, options, windowHandle); } void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( const std::shared_ptr<Connection>& connection, const CancelationOptions& options) { const std::shared_ptr<Connection>& connection, const CancelationOptions& options, const sp<WindowInfoHandle>& window) { if (!connection->monitor && window == nullptr) { LOG(FATAL) << __func__ << ": Cannot send event to non-monitor channel without a window - channel: " << connection->getInputChannelName(); } if (connection->status != Connection::Status::NORMAL) { return; } Loading Loading @@ -4029,10 +4107,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( switch (cancelationEventEntry->type) { case EventEntry::Type::KEY: { const auto& keyEntry = static_cast<const KeyEntry&>(*cancelationEventEntry); const std::optional<int32_t> targetDisplay = keyEntry.displayId != ADISPLAY_ID_NONE ? std::make_optional(keyEntry.displayId) : std::nullopt; if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) { if (window) { addWindowTargetLocked(window, InputTarget::DispatchMode::AS_IS, /*targetFlags=*/{}, keyEntry.downTime, targets); } else { Loading @@ -4043,11 +4118,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } case EventEntry::Type::MOTION: { const auto& motionEntry = static_cast<const MotionEntry&>(*cancelationEventEntry); const std::optional<int32_t> targetDisplay = motionEntry.displayId != ADISPLAY_ID_NONE ? std::make_optional(motionEntry.displayId) : std::nullopt; if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) { if (window) { std::bitset<MAX_POINTER_ID + 1> pointerIds; for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.getPointerCount(); pointerIndex++) { Loading Loading @@ -4121,7 +4192,12 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( connection->getInputChannelName().c_str(), downEvents.size()); } sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(connection->getToken()); const auto [_, touchedWindowState, displayId] = findTouchStateWindowAndDisplayLocked(connection->getToken()); if (touchedWindowState == nullptr) { LOG(FATAL) << __func__ << ": Touch state is out of sync: No touched window for token"; } const auto& windowHandle = touchedWindowState->windowHandle; const bool wasEmpty = connection->outboundQueue.empty(); for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) { Loading Loading @@ -4175,17 +4251,6 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( } } void InputDispatcher::synthesizeCancelationEventsForWindowLocked( const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options) { if (windowHandle != nullptr) { std::shared_ptr<Connection> wallpaperConnection = getConnectionLocked(windowHandle->getToken()); if (wallpaperConnection != nullptr) { synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, options); } } } std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( const MotionEntry& originalMotionEntry, std::bitset<MAX_POINTER_ID + 1> pointerIds, nsecs_t splitDownTime) { Loading Loading @@ -5216,19 +5281,16 @@ void InputDispatcher::setInputWindowsLocked( if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) { LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName() << " in display %" << displayId; std::shared_ptr<Connection> touchedConnection = getConnectionLocked(touchedWindow.windowHandle->getToken()); if (touchedConnection != nullptr) { CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "touched window was removed"); synthesizeCancelationEventsForConnectionLocked(touchedConnection, options); synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options); // Since we are about to drop the touch, cancel the events for the wallpaper as // well. if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) && touchedWindow.windowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow(); synthesizeCancelationEventsForWindowLocked(wallpaper, options); if (const auto& ww = state.getWallpaperWindow(); ww) { synthesizeCancelationEventsForWindowLocked(ww, options); } } state.windows.erase(state.windows.begin() + i); Loading Loading @@ -5515,9 +5577,10 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< } const int32_t deviceId = *deviceIds.begin(); sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId); if (toWindowHandle == nullptr) { ALOGW("Cannot transfer touch because to window not found."); const sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle; const sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId); if (!toWindowHandle) { ALOGW("Cannot transfer touch because the transfer target window was not found."); return false; } Loading @@ -5530,7 +5593,6 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< // Erase old window. ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow->targetFlags; std::vector<PointerProperties> pointers = touchedWindow->getTouchingPointers(deviceId); sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle; state->removeWindowByToken(fromToken); // Add new window. Loading Loading @@ -5562,7 +5624,7 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< fromConnection->inputState.mergePointerStateTo(toConnection->inputState); CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "transferring touch from this window to another window"); synthesizeCancelationEventsForConnectionLocked(fromConnection, options); synthesizeCancelationEventsForWindowLocked(fromWindowHandle, options, fromConnection); synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection, newTargetFlags); Loading Loading @@ -6044,12 +6106,10 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { std::string canceledWindows; for (const TouchedWindow& w : state.windows) { const std::shared_ptr<Connection> connection = getConnectionLocked(w.windowHandle->getToken()); if (connection != nullptr && connection->getToken() != token) { synthesizeCancelationEventsForConnectionLocked(connection, options); if (w.windowHandle->getToken() != token) { synthesizeCancelationEventsForWindowLocked(w.windowHandle, options); canceledWindows += canceledWindows.empty() ? "[" : ", "; canceledWindows += connection->getInputChannelName(); canceledWindows += w.windowHandle->getName(); } } canceledWindows += canceledWindows.empty() ? "[]" : "]"; Loading Loading @@ -6463,7 +6523,7 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl "or is no longer a foreground target, " "canceling previously dispatched fallback key"); options.keyCode = *fallbackKeyCode; synthesizeCancelationEventsForConnectionLocked(connection, options); synthesizeCancelationEventsForWindowLocked(windowHandle, options, connection); } } connection->inputState.removeFallbackKey(originalKeyCode); Loading Loading @@ -6545,7 +6605,7 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS, "canceling fallback, policy no longer desires it"); options.keyCode = *fallbackKeyCode; synthesizeCancelationEventsForConnectionLocked(connection, options); synthesizeCancelationEventsForWindowLocked(windowHandle, options, connection); } fallback = false; Loading
services/inputflinger/dispatcher/InputDispatcher.h +9 −6 Original line number Diff line number Diff line Loading @@ -615,18 +615,21 @@ private: REQUIRES(mLock); void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options) REQUIRES(mLock); void synthesizeCancelationEventsForConnectionLocked( const std::shared_ptr<Connection>& connection, const CancelationOptions& options) void synthesizeCancelationEventsForWindowLocked(const sp<gui::WindowInfoHandle>&, const CancelationOptions&, const std::shared_ptr<Connection>& = nullptr) REQUIRES(mLock); // This is a convenience function used to generate cancellation for a connection without having // to check whether it's a monitor or a window. For non-monitors, the window handle must not be // null. Always prefer the "-ForWindow" method above when explicitly dealing with windows. void synthesizeCancelationEventsForConnectionLocked( const std::shared_ptr<Connection>& connection, const CancelationOptions& options, const sp<gui::WindowInfoHandle>& window) REQUIRES(mLock); void synthesizePointerDownEventsForConnectionLocked( const nsecs_t downTime, const std::shared_ptr<Connection>& connection, ftl::Flags<InputTarget::Flags> targetFlags) REQUIRES(mLock); void synthesizeCancelationEventsForWindowLocked( const sp<android::gui::WindowInfoHandle>& windowHandle, const CancelationOptions& options) REQUIRES(mLock); // Splitting motion events across windows. When splitting motion event for a target, // splitDownTime refers to the time of first 'down' event on that particular target std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry, Loading