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

Commit 9998771e authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

SyncPointerCapture (5/n): Move Pointer Capture to InputDispatcher

This CL re-implements Pointer Capture in InputDispatcher, moving it away
for WindowManagerService.

The pipeline for Pointer Capture is changed so that requests to enable
and disable Pointer Capture from an app are redirected directly to
InputDispatcher by InputManagerService.

The new expected pipeline for Pointer Capture is as follows:
- App requests Pointer Capture through InputManagerService with its
window token.
- InputManagerService gets the InputWindowToken for the WindowToken, and
relays the Pointer Capture request to InputDispatcher.
- Pointer Capture is enabled in InputFlinger:
  1. InputDispatcher receives the Pointer Capture request, and ignores
  requests from windows that are not focused.
    InputDispatcherInterface::requestPointerCapture(windowToken,
        enabled)
  2. InputDispatcher sets the Pointer Capture state through its policy,
  which sends a configuration change to InputReader.
    InputDispatcherPolicyInterface::setPointerCapture(enabled)
  3. InputReader changes its configuration to enable/disable Pointer
  Capture, and sends notifyPointerCaptureChanged to InputDispatcher.
    InputListener::notifyPointerCaptureChanged(args)
  4. The Pointer Capture change notification is sent to the input
  channel.
- App receives a Pointer Capture change event through its InputChannel.

The window that has Pointer Capture is tracked in InputDispatcher. If
the window loses focus, Pointer Capture is disabled immediately, and
InputDispatcher synthesizes a Pointer Capture change event to dispatch
to the app before it sends the event signalling the focus loss.

Bug: 141749603
Test: atest inputflinger_tests
Test: atest PointerCaptureTest (CTS)
Test: manual: Pointer Capture works

Change-Id: Ie27f6e279af9b0d844160fb146476e1812fb02b3
parent 16abdd2a
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -96,6 +96,8 @@ private:

    void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}

    void setPointerCapture(bool enabled) override {}

    InputDispatcherConfiguration mConfig;
};

+16 −0
Original line number Diff line number Diff line
@@ -114,6 +114,22 @@ std::string FocusEntry::getDescription() const {
    return StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false");
}

// --- PointerCaptureChangedEntry ---

// PointerCaptureChanged notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER
// for all entries.
PointerCaptureChangedEntry::PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime,
                                                       bool hasPointerCapture)
      : EventEntry(id, Type::POINTER_CAPTURE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
        pointerCaptureEnabled(hasPointerCapture) {}

PointerCaptureChangedEntry::~PointerCaptureChangedEntry() {}

std::string PointerCaptureChangedEntry::getDescription() const {
    return StringPrintf("PointerCaptureChangedEvent(pointerCaptureEnabled=%s)",
                        pointerCaptureEnabled ? "true" : "false");
}

// --- KeyEntry ---

KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+13 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ struct EventEntry {
        FOCUS,
        KEY,
        MOTION,
        POINTER_CAPTURE_CHANGED,
    };

    static const char* typeToString(Type type) {
@@ -50,6 +51,8 @@ struct EventEntry {
                return "KEY";
            case Type::MOTION:
                return "MOTION";
            case Type::POINTER_CAPTURE_CHANGED:
                return "POINTER_CAPTURE_CHANGED";
        }
    }

@@ -115,6 +118,15 @@ struct FocusEntry : EventEntry {
    virtual ~FocusEntry();
};

struct PointerCaptureChangedEntry : EventEntry {
    bool pointerCaptureEnabled;

    PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, bool hasPointerCapture);
    std::string getDescription() const override;

    virtual ~PointerCaptureChangedEntry();
};

struct KeyEntry : EventEntry {
    int32_t deviceId;
    uint32_t source;
@@ -254,6 +266,7 @@ struct CommandEntry {
    sp<IBinder> oldToken;
    sp<IBinder> newToken;
    std::string obscuringPackage;
    bool enabled;
};

} // namespace android::inputdispatcher
+212 −9
Original line number Diff line number Diff line
@@ -454,6 +454,8 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic
        mInTouchMode(true),
        mMaximumObscuringOpacityForTouch(1.0f),
        mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
        mFocusedWindowRequestedPointerCapture(false),
        mWindowTokenWithPointerCapture(nullptr),
        mCompatService(getCompatService()) {
    mLooper = new Looper(false);
    mReporter = createInputReporter();
@@ -713,6 +715,14 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
            break;
        }

        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
            const auto typedEntry =
                    std::static_pointer_cast<PointerCaptureChangedEntry>(mPendingEvent);
            dispatchPointerCaptureChangedLocked(currentTime, typedEntry, dropReason);
            done = true;
            break;
        }

        case EventEntry::Type::KEY: {
            std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
            if (isAppSwitchDue) {
@@ -862,7 +872,8 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE
            break;
        }
        case EventEntry::Type::CONFIGURATION_CHANGED:
        case EventEntry::Type::DEVICE_RESET: {
        case EventEntry::Type::DEVICE_RESET:
        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
            // nothing to do
            break;
        }
@@ -968,6 +979,10 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason
            ALOGI("Dropped event because it is stale.");
            reason = "inbound event was dropped because it is stale";
            break;
        case DropReason::NO_POINTER_CAPTURE:
            ALOGI("Dropped event because there is no window with Pointer Capture.");
            reason = "inbound event was dropped because there is no window with Pointer Capture";
            break;
        case DropReason::NOT_DROPPED: {
            LOG_ALWAYS_FATAL("Should not be dropping a NOT_DROPPED event");
            return;
@@ -991,6 +1006,9 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason
            }
            break;
        }
        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
            break;
        }
        case EventEntry::Type::FOCUS:
        case EventEntry::Type::CONFIGURATION_CHANGED:
        case EventEntry::Type::DEVICE_RESET: {
@@ -1176,6 +1194,55 @@ void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<F
    dispatchEventLocked(currentTime, entry, {target});
}

void InputDispatcher::dispatchPointerCaptureChangedLocked(
        nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
        DropReason& dropReason) {
    const bool haveWindowWithPointerCapture = mWindowTokenWithPointerCapture != nullptr;
    if (entry->pointerCaptureEnabled == haveWindowWithPointerCapture) {
        LOG_ALWAYS_FATAL_IF(mFocusedWindowRequestedPointerCapture,
                            "The Pointer Capture state has already been dispatched to the window.");
        // Pointer capture was already forcefully disabled because of focus change.
        dropReason = DropReason::NOT_DROPPED;
        return;
    }

    // Set drop reason for early returns
    dropReason = DropReason::NO_POINTER_CAPTURE;

    sp<IBinder> token;
    if (entry->pointerCaptureEnabled) {
        // Enable Pointer Capture
        if (!mFocusedWindowRequestedPointerCapture) {
            // This can happen if a window requests capture and immediately releases capture.
            ALOGW("No window requested Pointer Capture.");
            return;
        }
        token = getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
        LOG_ALWAYS_FATAL_IF(!token, "Cannot find focused window for Pointer Capture.");
        mWindowTokenWithPointerCapture = token;
    } else {
        // Disable Pointer Capture
        token = mWindowTokenWithPointerCapture;
        mWindowTokenWithPointerCapture = nullptr;
        mFocusedWindowRequestedPointerCapture = false;
    }

    auto channel = getInputChannelLocked(token);
    if (channel == nullptr) {
        // Window has gone away, clean up Pointer Capture state.
        mWindowTokenWithPointerCapture = nullptr;
        mFocusedWindowRequestedPointerCapture = false;
        return;
    }
    InputTarget target;
    target.inputChannel = channel;
    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
    entry->dispatchInProgress = true;
    dispatchEventLocked(currentTime, entry, {target});

    dropReason = DropReason::NOT_DROPPED;
}

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // Preprocessing.
@@ -1483,6 +1550,7 @@ int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) {
            displayId = motionEntry.displayId;
            break;
        }
        case EventEntry::Type::POINTER_CAPTURE_CHANGED:
        case EventEntry::Type::FOCUS:
        case EventEntry::Type::CONFIGURATION_CHANGED:
        case EventEntry::Type::DEVICE_RESET: {
@@ -2366,8 +2434,10 @@ std::string InputDispatcher::getApplicationWindowLabel(
}

void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
    if (eventEntry.type == EventEntry::Type::FOCUS) {
        // Focus events are passed to apps, but do not represent user activity.
    if (eventEntry.type == EventEntry::Type::FOCUS ||
        eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED) {
        // Focus or pointer capture changed events are passed to apps, but do not represent user
        // activity.
        return;
    }
    int32_t displayId = getTargetDisplayId(eventEntry);
@@ -2405,7 +2475,8 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
        }
        case EventEntry::Type::FOCUS:
        case EventEntry::Type::CONFIGURATION_CHANGED:
        case EventEntry::Type::DEVICE_RESET: {
        case EventEntry::Type::DEVICE_RESET:
        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
            LOG_ALWAYS_FATAL("%s events are not user activity",
                             EventEntry::typeToString(eventEntry.type));
            break;
@@ -2617,7 +2688,8 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio

            break;
        }
        case EventEntry::Type::FOCUS: {
        case EventEntry::Type::FOCUS:
        case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
            break;
        }
        case EventEntry::Type::CONFIGURATION_CHANGED:
@@ -2821,6 +2893,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
                reportTouchEventForStatistics(motionEntry);
                break;
            }

            case EventEntry::Type::FOCUS: {
                const FocusEntry& focusEntry = static_cast<const FocusEntry&>(eventEntry);
                status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq,
@@ -2830,6 +2903,15 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
                break;
            }

            case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
                const auto& captureEntry =
                        static_cast<const PointerCaptureChangedEntry&>(eventEntry);
                status = connection->inputPublisher
                                 .publishCaptureEvent(dispatchEntry->seq, captureEntry.id,
                                                      captureEntry.pointerCaptureEnabled);
                break;
            }

            case EventEntry::Type::CONFIGURATION_CHANGED:
            case EventEntry::Type::DEVICE_RESET: {
                LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
@@ -3124,8 +3206,10 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
                                         static_cast<const MotionEntry&>(*cancelationEventEntry));
                break;
            }
            case EventEntry::Type::FOCUS: {
                LOG_ALWAYS_FATAL("Canceling focus events is not supported");
            case EventEntry::Type::FOCUS:
            case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
                LOG_ALWAYS_FATAL("Canceling %s events is not supported",
                                 EventEntry::typeToString(cancelationEventEntry->type));
                break;
            }
            case EventEntry::Type::CONFIGURATION_CHANGED:
@@ -3185,7 +3269,8 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
            case EventEntry::Type::KEY:
            case EventEntry::Type::FOCUS:
            case EventEntry::Type::CONFIGURATION_CHANGED:
            case EventEntry::Type::DEVICE_RESET: {
            case EventEntry::Type::DEVICE_RESET:
            case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
                LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
                                     EventEntry::typeToString(downEventEntry->type));
                break;
@@ -3562,7 +3647,17 @@ void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChan
          args->enabled ? "true" : "false");
#endif

    // TODO(prabirmsp): Implement.
    bool needWake;
    { // acquire lock
        std::scoped_lock _l(mLock);
        auto entry = std::make_unique<PointerCaptureChangedEntry>(args->id, args->eventTime,
                                                                  args->enabled);
        needWake = enqueueInboundEventLocked(std::move(entry));
    } // release lock

    if (needWake) {
        mLooper->wake();
    }
}

InputEventInjectionResult InputDispatcher::injectInputEvent(
@@ -4456,6 +4551,24 @@ std::string InputDispatcher::dumpPendingFocusRequestsLocked() {
    return dump;
}

std::string InputDispatcher::dumpPointerCaptureStateLocked() {
    std::string dump;

    dump += StringPrintf(INDENT "FocusedWindowRequestedPointerCapture: %s\n",
                         toString(mFocusedWindowRequestedPointerCapture));

    std::string windowName = "None";
    if (mWindowTokenWithPointerCapture) {
        const sp<InputWindowHandle> captureWindowHandle =
                getWindowHandleLocked(mWindowTokenWithPointerCapture);
        windowName = captureWindowHandle ? captureWindowHandle->getName().c_str()
                                         : "token has capture without window";
    }
    dump += StringPrintf(INDENT "CurrentWindowWithPointerCapture: %s\n", windowName.c_str());

    return dump;
}

void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
    dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled));
    dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen));
@@ -4479,6 +4592,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {

    dump += dumpFocusedWindowsLocked();
    dump += dumpPendingFocusRequestsLocked();
    dump += dumpPointerCaptureStateLocked();

    if (!mTouchStatesByDisplay.empty()) {
        dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
@@ -4860,6 +4974,39 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) {
    return OK;
}

void InputDispatcher::requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) {
    { // acquire lock
        std::scoped_lock _l(mLock);
        if (DEBUG_FOCUS) {
            const sp<InputWindowHandle> windowHandle = getWindowHandleLocked(windowToken);
            ALOGI("Request to %s Pointer Capture from: %s.", enabled ? "enable" : "disable",
                  windowHandle != nullptr ? windowHandle->getName().c_str()
                                          : "token without window");
        }

        const sp<IBinder> focusedToken =
                getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
        if (focusedToken != windowToken) {
            ALOGW("Ignoring request to %s Pointer Capture: window does not have focus.",
                  enabled ? "enable" : "disable");
            return;
        }

        if (enabled == mFocusedWindowRequestedPointerCapture) {
            ALOGW("Ignoring request to %s Pointer Capture: "
                  "window has %s requested pointer capture.",
                  enabled ? "enable" : "disable", enabled ? "already" : "not");
            return;
        }

        mFocusedWindowRequestedPointerCapture = enabled;
        setPointerCaptureLocked(enabled);
    } // release lock

    // Wake the thread to process command entries.
    mLooper->wake();
}

std::optional<int32_t> InputDispatcher::findGestureMonitorDisplayByTokenLocked(
        const sp<IBinder>& token) {
    for (const auto& it : mGestureMonitorsByDisplay) {
@@ -5578,11 +5725,50 @@ void InputDispatcher::onFocusChangedLocked(const sp<IBinder>& oldFocusedToken,
        enqueueFocusEventLocked(newFocusedToken, true /*hasFocus*/, reason);
    }

    // If a window has pointer capture, then it must have focus. We need to ensure that this
    // contract is upheld when pointer capture is being disabled due to a loss of window focus.
    // If the window loses focus before it loses pointer capture, then the window can be in a state
    // where it has pointer capture but not focus, violating the contract. Therefore we must
    // dispatch the pointer capture event before the focus event. Since focus events are added to
    // the front of the queue (above), we add the pointer capture event to the front of the queue
    // after the focus events are added. This ensures the pointer capture event ends up at the
    // front.
    disablePointerCaptureForcedLocked();

    if (mFocusedDisplayId == displayId) {
        notifyFocusChangedLocked(oldFocusedToken, newFocusedToken);
    }
}

void InputDispatcher::disablePointerCaptureForcedLocked() {
    if (!mFocusedWindowRequestedPointerCapture && !mWindowTokenWithPointerCapture) {
        return;
    }

    ALOGD_IF(DEBUG_FOCUS, "Disabling Pointer Capture because the window lost focus.");

    if (mFocusedWindowRequestedPointerCapture) {
        mFocusedWindowRequestedPointerCapture = false;
        setPointerCaptureLocked(false);
    }

    if (!mWindowTokenWithPointerCapture) {
        // No need to send capture changes because no window has capture.
        return;
    }

    if (mPendingEvent != nullptr) {
        // Move the pending event to the front of the queue. This will give the chance
        // for the pending event to be dropped if it is a captured event.
        mInboundQueue.push_front(mPendingEvent);
        mPendingEvent = nullptr;
    }

    auto entry = std::make_unique<PointerCaptureChangedEntry>(mIdGenerator.nextId(), now(),
                                                              false /* hasCapture */);
    mInboundQueue.push_front(std::move(entry));
}

/**
 * Checks if the window token can be focused on a display. The token can be focused if there is
 * at least one window handle that is visible with the same token and all window handles with the
@@ -5626,4 +5812,21 @@ InputDispatcher::FocusResult InputDispatcher::checkTokenFocusableLocked(const sp

    return FocusResult::OK;
}

void InputDispatcher::setPointerCaptureLocked(bool enabled) {
    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
            &InputDispatcher::doSetPointerCaptureLockedInterruptible);
    commandEntry->enabled = enabled;
    postCommandLocked(std::move(commandEntry));
}

void InputDispatcher::doSetPointerCaptureLockedInterruptible(
        android::inputdispatcher::CommandEntry* commandEntry) {
    mLock.unlock();

    mPolicy->setPointerCapture(commandEntry->enabled);

    mLock.lock();
}

} // namespace android::inputdispatcher
+22 −0
Original line number Diff line number Diff line
@@ -127,6 +127,7 @@ public:
            int32_t displayId, bool isGestureMonitor, const std::string& name) override;
    virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
    virtual status_t pilferPointers(const sp<IBinder>& token) override;
    virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;

    std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;

@@ -138,6 +139,7 @@ private:
        DISABLED,
        BLOCKED,
        STALE,
        NO_POINTER_CAPTURE,
    };

    enum class FocusResult {
@@ -351,6 +353,21 @@ private:
    // Top focused display.
    int32_t mFocusedDisplayId GUARDED_BY(mLock);

    // Whether the focused window on the focused display has requested Pointer Capture.
    // The state of this variable should always be in sync with the state of Pointer Capture in the
    // policy, which is updated through setPointerCaptureLocked(enabled).
    bool mFocusedWindowRequestedPointerCapture GUARDED_BY(mLock);

    // The window token that has Pointer Capture.
    // This should be in sync with PointerCaptureChangedEvents dispatched to the input channel.
    sp<IBinder> mWindowTokenWithPointerCapture GUARDED_BY(mLock);

    // Disable Pointer Capture as a result of loss of window focus.
    void disablePointerCaptureForcedLocked() REQUIRES(mLock);

    // Set the Pointer Capture state in the Policy.
    void setPointerCaptureLocked(bool enabled) REQUIRES(mLock);

    // Dispatcher state at time of last ANR.
    std::string mLastAnrState GUARDED_BY(mLock);

@@ -370,6 +387,9 @@ private:
                              DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
    void dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry)
            REQUIRES(mLock);
    void dispatchPointerCaptureChangedLocked(
            nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
            DropReason& dropReason) REQUIRES(mLock);
    void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> entry,
                             const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);

@@ -533,6 +553,7 @@ private:
    void logDispatchStateLocked() REQUIRES(mLock);
    std::string dumpFocusedWindowsLocked() REQUIRES(mLock);
    std::string dumpPendingFocusRequestsLocked() REQUIRES(mLock);
    std::string dumpPointerCaptureStateLocked() REQUIRES(mLock);

    // Registration.
    void removeMonitorChannelLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
@@ -575,6 +596,7 @@ private:
    void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
            REQUIRES(mLock);
    void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
    void doSetPointerCaptureLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
    bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
                                          DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
                                          bool handled) REQUIRES(mLock);
Loading