Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 393e9462 authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 11420186 from fa32f893 to 24Q2-release

Change-Id: I8a31951a2fb26962922ae369040d2f34dd477ac9
parents dd965703 fa32f893
Loading
Loading
Loading
Loading
+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
+1 −0
Original line number Diff line number Diff line
@@ -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" />

+15 −13
Original line number Diff line number Diff line
@@ -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);

@@ -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
@@ -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) {
@@ -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();
    }
@@ -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;
@@ -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;
+112 −52
Original line number Diff line number Diff line
@@ -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 ---
@@ -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()));
    }
}

@@ -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 =
@@ -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;
    }
@@ -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 {
@@ -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++) {
@@ -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) {
@@ -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) {
@@ -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);
@@ -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;
        }

@@ -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.
@@ -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);

@@ -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() ? "[]" : "]";
@@ -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);
@@ -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;
+9 −6
Original line number Diff line number Diff line
@@ -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