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

Commit a6efb77a authored by Arthur Hung's avatar Arthur Hung Committed by Automerger Merge Worker
Browse files

Fix drag and drop failed while recording video am: abbb9d8a

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/15738384

Change-Id: I8f5ef602c7c04ec37c067b762506bb43b3e1210e
parents 07ef7db9 abbb9d8a
Loading
Loading
Loading
Loading
+48 −58
Original line number Diff line number Diff line
@@ -4789,6 +4789,18 @@ void InputDispatcher::setBlockUntrustedTouchesMode(BlockUntrustedTouchesMode mod
    mBlockUntrustedTouchesMode = mode;
}

std::pair<TouchState*, TouchedWindow*> InputDispatcher::findTouchStateAndWindowLocked(
        const sp<IBinder>& token) {
    for (auto& [displayId, state] : mTouchStatesByDisplay) {
        for (TouchedWindow& w : state.windows) {
            if (w.windowHandle->getToken() == token) {
                return std::make_pair(&state, &w);
            }
        }
    }
    return std::make_pair(nullptr, nullptr);
}

bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
                                         bool isDragDrop) {
    if (fromToken == toToken) {
@@ -4801,58 +4813,43 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<
    { // acquire lock
        std::scoped_lock _l(mLock);

        sp<WindowInfoHandle> fromWindowHandle = getWindowHandleLocked(fromToken);
        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken);
        if (fromWindowHandle == nullptr || toWindowHandle == nullptr) {
            ALOGW("Cannot transfer focus because from or to window not found.");
        // Find the target touch state and touched window by fromToken.
        auto [state, touchedWindow] = findTouchStateAndWindowLocked(fromToken);
        if (state == nullptr || touchedWindow == nullptr) {
            ALOGD("Focus transfer failed because from window is not being touched.");
            return false;
        }
        if (DEBUG_FOCUS) {
            ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
                  fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
        }
        if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) {
            if (DEBUG_FOCUS) {
                ALOGD("Cannot transfer focus because windows are on different displays.");
            }

        const int32_t displayId = state->displayId;
        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId);
        if (toWindowHandle == nullptr) {
            ALOGW("Cannot transfer focus because to window not found.");
            return false;
        }

        bool found = false;
        for (std::pair<const int32_t, TouchState>& pair : mTouchStatesByDisplay) {
            TouchState& state = pair.second;
            for (size_t i = 0; i < state.windows.size(); i++) {
                const TouchedWindow& touchedWindow = state.windows[i];
                if (touchedWindow.windowHandle == fromWindowHandle) {
                    int32_t oldTargetFlags = touchedWindow.targetFlags;
                    BitSet32 pointerIds = touchedWindow.pointerIds;
        if (DEBUG_FOCUS) {
            ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
                  touchedWindow->windowHandle->getName().c_str(),
                  toWindowHandle->getName().c_str());
        }

                    state.windows.erase(state.windows.begin() + i);
        // Erase old window.
        int32_t oldTargetFlags = touchedWindow->targetFlags;
        BitSet32 pointerIds = touchedWindow->pointerIds;
        state->removeWindowByToken(fromToken);

        // Add new window.
        int32_t newTargetFlags = oldTargetFlags &
                (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
                 InputTarget::FLAG_DISPATCH_AS_IS);
                    state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
        state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);

        // Store the dragging window.
        if (isDragDrop) {
            mDragState = std::make_unique<DragState>(toWindowHandle);
        }

                    found = true;
                    goto Found;
                }
            }
        }
    Found:

        if (!found) {
            if (DEBUG_FOCUS) {
                ALOGD("Focus transfer failed because from window did not have focus.");
            }
            return false;
        }

        // Synthesize cancel for old window and down for new window.
        sp<Connection> fromConnection = getConnectionLocked(fromToken);
        sp<Connection> toConnection = getConnectionLocked(toToken);
        if (fromConnection != nullptr && toConnection != nullptr) {
@@ -4880,27 +4877,20 @@ bool InputDispatcher::transferTouch(const sp<IBinder>& destChannelToken) {
    { // acquire lock
        std::scoped_lock _l(mLock);

        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken);
        if (toWindowHandle == nullptr) {
            ALOGW("Could not find window associated with token=%p", destChannelToken.get());
        auto it = std::find_if(mTouchStatesByDisplay.begin(), mTouchStatesByDisplay.end(),
                               [](const auto& pair) { return pair.second.windows.size() == 1; });
        if (it == mTouchStatesByDisplay.end()) {
            ALOGW("Cannot transfer touch state because there is no exact window being touched");
            return false;
        }

        const int32_t displayId = toWindowHandle->getInfo()->displayId;

        auto touchStateIt = mTouchStatesByDisplay.find(displayId);
        if (touchStateIt == mTouchStatesByDisplay.end()) {
            ALOGD("Could not transfer touch because the display %" PRId32 " is not being touched",
                  displayId);
        const int32_t displayId = it->first;
        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken, displayId);
        if (toWindowHandle == nullptr) {
            ALOGW("Could not find window associated with token=%p", destChannelToken.get());
            return false;
        }

        TouchState& state = touchStateIt->second;
        if (state.windows.size() != 1) {
            ALOGW("Cannot transfer touch state because there are %zu windows being touched",
                  state.windows.size());
            return false;
        }
        TouchState& state = it->second;
        const TouchedWindow& touchedWindow = state.windows[0];
        fromToken = touchedWindow.windowHandle->getToken();
    } // release lock
+4 −0
Original line number Diff line number Diff line
@@ -653,6 +653,10 @@ private:
    void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
    void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);

    // Find touched state and touched window by token.
    std::pair<TouchState*, TouchedWindow*> findTouchStateAndWindowLocked(const sp<IBinder>& token)
            REQUIRES(mLock);

    // Statistics gathering.
    LatencyAggregator mLatencyAggregator GUARDED_BY(mLock);
    LatencyTracker mLatencyTracker GUARDED_BY(mLock);
+139 −4
Original line number Diff line number Diff line
@@ -46,7 +46,8 @@ static const nsecs_t ARBITRARY_TIME = 1234;
static const int32_t DEVICE_ID = 1;

// An arbitrary display id.
static const int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
static constexpr int32_t SECOND_DISPLAY_ID = 1;

// An arbitrary injector pid / uid pair that has permission to inject events.
static const int32_t INJECTOR_PID = 999;
@@ -937,6 +938,15 @@ public:
        mInfo.displayId = displayId;
    }

    sp<FakeWindowHandle> clone(
            const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
            const sp<InputDispatcher>& dispatcher, int32_t displayId) {
        sp<FakeWindowHandle> handle =
                new FakeWindowHandle(inputApplicationHandle, dispatcher, mInfo.name + "(Mirror)",
                                     displayId, mInfo.token);
        return handle;
    }

    void setFocusable(bool focusable) { mInfo.focusable = focusable; }

    void setVisible(bool visible) { mInfo.visible = visible; }
@@ -1996,6 +2006,134 @@ TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) {
    secondWindow->assertNoEvents();
}

// This case will create two windows and one mirrored window on the default display and mirror
// two windows on the second display. It will test if 'transferTouchFocus' works fine if we put
// the windows info of second display before default display.
TEST_F(InputDispatcherTest, TransferTouchFocus_CloneSurface) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> firstWindowInPrimary =
            new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
    firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
    firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
    sp<FakeWindowHandle> secondWindowInPrimary =
            new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);

    sp<FakeWindowHandle> mirrorWindowInPrimary =
            firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
    mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
    mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);

    sp<FakeWindowHandle> firstWindowInSecondary =
            firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
    firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
    firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);

    sp<FakeWindowHandle> secondWindowInSecondary =
            secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);

    // Update window info, let it find window handle of second display first.
    mDispatcher->setInputWindows(
            {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
             {ADISPLAY_ID_DEFAULT,
              {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});

    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                               {50, 50}))
            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";

    // Window should receive motion event.
    firstWindowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);

    // Transfer touch focus
    ASSERT_TRUE(mDispatcher->transferTouchFocus(firstWindowInPrimary->getToken(),
                                                secondWindowInPrimary->getToken()));
    // The first window gets cancel.
    firstWindowInPrimary->consumeMotionCancel();
    secondWindowInPrimary->consumeMotionDown();

    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
                                ADISPLAY_ID_DEFAULT, {150, 50}))
            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
    firstWindowInPrimary->assertNoEvents();
    secondWindowInPrimary->consumeMotionMove();

    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                             {150, 50}))
            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
    firstWindowInPrimary->assertNoEvents();
    secondWindowInPrimary->consumeMotionUp();
}

// Same as TransferTouchFocus_CloneSurface, but this touch on the secondary display and use
// 'transferTouch' api.
TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> firstWindowInPrimary =
            new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
    firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
    firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
    sp<FakeWindowHandle> secondWindowInPrimary =
            new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);

    sp<FakeWindowHandle> mirrorWindowInPrimary =
            firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
    mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
    mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);

    sp<FakeWindowHandle> firstWindowInSecondary =
            firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
    firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
    firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);

    sp<FakeWindowHandle> secondWindowInSecondary =
            secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);

    // Update window info, let it find window handle of second display first.
    mDispatcher->setInputWindows(
            {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
             {ADISPLAY_ID_DEFAULT,
              {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});

    // Touch on second display.
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {50, 50}))
            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";

    // Window should receive motion event.
    firstWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);

    // Transfer touch focus
    ASSERT_TRUE(mDispatcher->transferTouch(secondWindowInSecondary->getToken()));

    // The first window gets cancel.
    firstWindowInPrimary->consumeMotionCancel(SECOND_DISPLAY_ID);
    secondWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);

    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
                                SECOND_DISPLAY_ID, {150, 50}))
            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
    firstWindowInPrimary->assertNoEvents();
    secondWindowInPrimary->consumeMotionMove(SECOND_DISPLAY_ID);

    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50}))
            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
    firstWindowInPrimary->assertNoEvents();
    secondWindowInPrimary->consumeMotionUp(SECOND_DISPLAY_ID);
}

TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> window =
@@ -2916,7 +3054,6 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEvent
/* Test InputDispatcher for MultiDisplay */
class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
public:
    static constexpr int32_t SECOND_DISPLAY_ID = 1;
    virtual void SetUp() override {
        InputDispatcherTest::SetUp();

@@ -3079,8 +3216,6 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CanFocusWindowOnUnfocusedDisplay)

class InputFilterTest : public InputDispatcherTest {
protected:
    static constexpr int32_t SECOND_DISPLAY_ID = 1;

    void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
        NotifyMotionArgs motionArgs;