Add onPointerDownOutsideFocus for events outside the focused window (2/4)

Post the onPointerDownOutsideFocus command when the conditions are satisfied.
Added onPointerDownOutsideFocus to the InputDispatcherPolicyInterface so the
callback can be sent to the client.

Bug: 122535136
Test: InputDispatcherOnPointerDownOutsideFocus
Change-Id: Ifec75daf1fff4ef83e0c230829400abb5a065d7f
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 0d5bc15..4dc9111 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -2055,7 +2055,7 @@
             return; // skip the inconsistent event
         }
 
-        dispatchPointerDownOutsideFocusIfNecessary(motionEntry->source,
+        dispatchPointerDownOutsideFocus(motionEntry->source,
                 dispatchEntry->resolvedAction, inputTarget->inputChannel->getToken());
 
         break;
@@ -2073,10 +2073,11 @@
 
 }
 
-void InputDispatcher::dispatchPointerDownOutsideFocusIfNecessary(uint32_t source, int32_t action,
+void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
         const sp<IBinder>& newToken) {
     int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
-    if (source != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
+    uint32_t maskedSource = source & AINPUT_SOURCE_CLASS_MASK;
+    if (maskedSource != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
         return;
     }
 
@@ -2095,7 +2096,9 @@
         return;
     }
 
-    // Dispatch onPointerDownOutsideFocus to the policy.
+    CommandEntry* commandEntry = postCommandLocked(
+            & InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
+    commandEntry->newToken = newToken;
 }
 
 void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -3997,6 +4000,12 @@
     entry->release();
 }
 
+void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) {
+    mLock.unlock();
+    mPolicy->onPointerDownOutsideFocus(commandEntry->newToken);
+    mLock.lock();
+}
+
 void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
         CommandEntry* commandEntry) {
     sp<Connection> connection = commandEntry->connection;
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 3735a0b..32bc414 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -272,6 +272,13 @@
      */
     virtual bool checkInjectEventsPermissionNonReentrant(
             int32_t injectorPid, int32_t injectorUid) = 0;
+
+    /* Notifies the policy that a pointer down event has occurred outside the current focused
+     * window.
+     *
+     * The touchedToken passed as an argument is the window that received the input event.
+     */
+    virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) = 0;
 };
 
 
@@ -1149,7 +1156,7 @@
     void releaseDispatchEntry(DispatchEntry* dispatchEntry);
     static int handleReceiveCallback(int fd, int events, void* data);
     // The action sent should only be of type AMOTION_EVENT_*
-    void dispatchPointerDownOutsideFocusIfNecessary(uint32_t source, int32_t action,
+    void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
             const sp<IBinder>& newToken) REQUIRES(mLock);
 
     void synthesizeCancelationEventsForAllConnectionsLocked(
@@ -1204,6 +1211,8 @@
             DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) REQUIRES(mLock);
     void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
+    void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry)
+            REQUIRES(mLock);
 
     // Statistics gathering.
     void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 745fac0..f0c36e5 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -52,6 +52,7 @@
         mTime = -1;
         mAction = -1;
         mDisplayId = -1;
+        mOnPointerDownToken.clear();
     }
 
     void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyMotionArgs* args) {
@@ -87,11 +88,18 @@
                 << "Expected filterInputEvent() to not have been called.";
     }
 
+    void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
+        ASSERT_EQ(mOnPointerDownToken, touchedToken)
+                << "Expected token from onPointerDownOutsideFocus was not matched";
+        reset();
+    }
+
 private:
     bool mInputEventFiltered;
     nsecs_t mTime;
     int32_t mAction;
     int32_t mDisplayId;
+    sp<IBinder> mOnPointerDownToken;
 
     virtual void notifyConfigurationChanged(nsecs_t) {
     }
@@ -161,11 +169,16 @@
         return false;
     }
 
+    virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) {
+        mOnPointerDownToken = newToken;
+    }
+
     void reset() {
         mInputEventFiltered = false;
         mTime = -1;
         mAction = -1;
         mDisplayId = -1;
+        mOnPointerDownToken.clear();
     }
 };
 
@@ -432,10 +445,10 @@
     FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
         const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) :
             FakeInputReceiver(dispatcher, name, displayId),
-            mFocused(false) {
+            mFocused(false), mFrame(Rect(0, 0, WIDTH, HEIGHT)), mLayoutParamFlags(0) {
             mServerChannel->setToken(new BBinder());
             mDispatcher->registerInputChannel(mServerChannel, displayId);
- 
+
             inputApplicationHandle->updateInfo();
             mInfo.applicationInfo = *inputApplicationHandle->getInfo();
     }
@@ -443,15 +456,15 @@
     virtual bool updateInfo() {
         mInfo.token = mServerChannel ? mServerChannel->getToken() : nullptr;
         mInfo.name = mName;
-        mInfo.layoutParamsFlags = 0;
+        mInfo.layoutParamsFlags = mLayoutParamFlags;
         mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
         mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
-        mInfo.frameLeft = 0;
-        mInfo.frameTop = 0;
-        mInfo.frameRight = WIDTH;
-        mInfo.frameBottom = HEIGHT;
+        mInfo.frameLeft = mFrame.left;
+        mInfo.frameTop = mFrame.top;
+        mInfo.frameRight = mFrame.right;
+        mInfo.frameBottom = mFrame.bottom;
         mInfo.globalScaleFactor = 1.0;
-        mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+        mInfo.addTouchableRegion(mFrame);
         mInfo.visible = true;
         mInfo.canReceiveKeys = true;
         mInfo.hasFocus = mFocused;
@@ -470,6 +483,14 @@
         mFocused = true;
     }
 
+    void setFrame(const Rect& frame) {
+        mFrame.set(frame);
+    }
+
+    void setLayoutParamFlags(int32_t flags) {
+        mLayoutParamFlags = flags;
+    }
+
     void releaseChannel() {
         mServerChannel.clear();
         InputWindowHandle::releaseChannel();
@@ -480,6 +501,8 @@
     }
 
     bool mFocused;
+    Rect mFrame;
+    int32_t mLayoutParamFlags;
 };
 
 static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
@@ -500,7 +523,7 @@
 }
 
 static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
-        int32_t displayId) {
+        int32_t displayId, int32_t x = 100, int32_t y = 200) {
     MotionEvent event;
     PointerProperties pointerProperties[1];
     PointerCoords pointerCoords[1];
@@ -510,13 +533,13 @@
     pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
 
     pointerCoords[0].clear();
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
 
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     // Define a valid motion down event.
     event.initialize(DEVICE_ID, source, displayId,
-            AMOTION_EVENT_ACTION_DOWN, /* actionButton */0, /* flags */ 0, /* edgeFlags */ 0,
+            AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0,
             AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
             /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
             /* yPrecision */ 0, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties,
@@ -907,4 +930,98 @@
     testNotifyKey(/*expectToBeFiltered*/ false);
 }
 
+class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest {
+    virtual void SetUp() {
+        InputDispatcherTest::SetUp();
+
+        sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+        mUnfocusedWindow = new FakeWindowHandle(application, mDispatcher, "Top",
+                ADISPLAY_ID_DEFAULT);
+        mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
+        // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
+        // window.
+        mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+
+        mWindowFocused = new FakeWindowHandle(application, mDispatcher, "Second",
+                ADISPLAY_ID_DEFAULT);
+        mWindowFocused->setFrame(Rect(50, 50, 100, 100));
+        mWindowFocused->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+        mWindowFocusedTouchPoint = 60;
+
+        // Set focused application.
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+        mWindowFocused->setFocus();
+
+        // Expect one focus window exist in display.
+        std::vector<sp<InputWindowHandle>> inputWindowHandles;
+        inputWindowHandles.push_back(mUnfocusedWindow);
+        inputWindowHandles.push_back(mWindowFocused);
+        mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    }
+
+    virtual void TearDown() {
+        InputDispatcherTest::TearDown();
+
+        mUnfocusedWindow.clear();
+        mWindowFocused.clear();
+    }
+
+protected:
+    sp<FakeWindowHandle> mUnfocusedWindow;
+    sp<FakeWindowHandle> mWindowFocused;
+    int32_t mWindowFocusedTouchPoint;
+};
+
+// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
+// DOWN on the window that doesn't have focus. Ensure the window that didn't have focus received
+// the onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) {
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    // Call monitor to wait for the command queue to get flushed.
+    mDispatcher->monitor();
+
+    mFakePolicy->assertOnPointerDownEquals(mUnfocusedWindow->getToken());
+}
+
+// Have two windows, one with focus. Inject MotionEvent with source TRACKBALL and action
+// DOWN on the window that doesn't have focus. Ensure no window received the
+// onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) {
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+            AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    // Call monitor to wait for the command queue to get flushed.
+    mDispatcher->monitor();
+
+    mFakePolicy->assertOnPointerDownEquals(nullptr);
+}
+
+// Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
+// have focus. Ensure no window received the onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    // Call monitor to wait for the command queue to get flushed.
+    mDispatcher->monitor();
+
+    mFakePolicy->assertOnPointerDownEquals(nullptr);
+}
+
+// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
+// DOWN on the window that already has focus. Ensure no window received the
+// onPointerDownOutsideFocus callback.
+TEST_F(InputDispatcherOnPointerDownOutsideFocus,
+        OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, mWindowFocusedTouchPoint,
+            mWindowFocusedTouchPoint))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    // Call monitor to wait for the command queue to get flushed.
+    mDispatcher->monitor();
+
+    mFakePolicy->assertOnPointerDownEquals(nullptr);
+}
+
 } // namespace android