Get mouse cursor position from PointerController instead of WM

PointerController is the source of truth of the mouse cursor position.
When the VirtualDevice APIs queried the cursor position, we were
previously getting the position from WM. WM's values only update once
system_server's global monitor has processed the input event, which
leads us into a race condition.

Remove the race consition by querying the mouse cursor position directly
from PointerController.

Bug: 266687189
Test: atest VirtualMouseTest
Change-Id: Id0e0e20a0d547f969d3a27d43fdfdca34d0fa7c0
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index fedf58d..e748c69 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -262,19 +262,34 @@
 }
 
 void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
-    std::scoped_lock lock(getLock());
+    struct PointerDisplayChangeArgs {
+        int32_t displayId;
+        float x, y;
+    };
+    std::optional<PointerDisplayChangeArgs> pointerDisplayChanged;
 
-    bool getAdditionalMouseResources = false;
-    if (mLocked.presentation == PointerController::Presentation::POINTER ||
-        mLocked.presentation == PointerController::Presentation::STYLUS_HOVER) {
-        getAdditionalMouseResources = true;
-    }
-    mCursorController.setDisplayViewport(viewport, getAdditionalMouseResources);
-    if (viewport.displayId != mLocked.pointerDisplayId) {
-        float xPos, yPos;
-        mCursorController.getPosition(&xPos, &yPos);
-        mContext.getPolicy()->onPointerDisplayIdChanged(viewport.displayId, xPos, yPos);
-        mLocked.pointerDisplayId = viewport.displayId;
+    { // acquire lock
+        std::scoped_lock lock(getLock());
+
+        bool getAdditionalMouseResources = false;
+        if (mLocked.presentation == PointerController::Presentation::POINTER ||
+            mLocked.presentation == PointerController::Presentation::STYLUS_HOVER) {
+            getAdditionalMouseResources = true;
+        }
+        mCursorController.setDisplayViewport(viewport, getAdditionalMouseResources);
+        if (viewport.displayId != mLocked.pointerDisplayId) {
+            float xPos, yPos;
+            mCursorController.getPosition(&xPos, &yPos);
+            mLocked.pointerDisplayId = viewport.displayId;
+            pointerDisplayChanged = {viewport.displayId, xPos, yPos};
+        }
+    } // release lock
+
+    if (pointerDisplayChanged) {
+        // Notify the policy without holding the pointer controller lock.
+        mContext.getPolicy()->onPointerDisplayIdChanged(pointerDisplayChanged->displayId,
+                                                        pointerDisplayChanged->x,
+                                                        pointerDisplayChanged->y);
     }
 }
 
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index 4d03e44..7e990c6 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -96,7 +96,11 @@
      */
     public abstract int getVirtualMousePointerDisplayId();
 
-    /** Gets the current position of the mouse cursor. */
+    /**
+     * Gets the current position of the mouse cursor.
+     *
+     * Returns NaN-s as the coordinates if the cursor is not available.
+     */
     public abstract PointF getCursorPosition();
 
     /**
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index b2b22a0..efc4f11 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -2863,9 +2863,6 @@
 
         int getPointerDisplayId();
 
-        /** Gets the x and y coordinates of the cursor's current position. */
-        PointF getCursorPosition();
-
         /**
          * Notifies window manager that a {@link android.view.MotionEvent#ACTION_DOWN} pointer event
          * occurred on a window that did not have focus.
@@ -3189,7 +3186,11 @@
 
         @Override
         public PointF getCursorPosition() {
-            return mWindowManagerCallbacks.getCursorPosition();
+            final float[] p = mNative.getMouseCursorPosition();
+            if (p == null || p.length != 2) {
+                throw new IllegalStateException("Failed to get mouse cursor position");
+            }
+            return new PointF(p[0], p[1]);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index 22226e8..5395302d 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -224,6 +224,16 @@
     /** Set whether stylus button reporting through motion events should be enabled. */
     void setStylusButtonMotionEventsEnabled(boolean enabled);
 
+    /**
+     * Get the current position of the mouse cursor.
+     *
+     * If the mouse cursor is not currently shown, the coordinate values will be NaN-s.
+     *
+     * NOTE: This will grab the PointerController's lock, so we must be careful about calling this
+     * from the InputReader or Display threads, which may result in a deadlock.
+     */
+    float[] getMouseCursorPosition();
+
     /** The native implementation of InputManagerService methods. */
     class NativeImpl implements NativeInputManagerService {
         /** Pointer to native input manager service object, used by native code. */
@@ -465,5 +475,8 @@
 
         @Override
         public native void setStylusButtonMotionEventsEnabled(boolean enabled);
+
+        @Override
+        public native float[] getMouseCursorPosition();
     }
 }
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 1e9d451..0a47fe0 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -25,7 +25,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.graphics.PointF;
 import android.os.Debug;
 import android.os.IBinder;
 import android.util.Slog;
@@ -221,11 +220,6 @@
     }
 
     @Override
-    public PointF getCursorPosition() {
-        return mService.getLatestMousePosition();
-    }
-
-    @Override
     public void onPointerDownOutsideFocus(IBinder touchedToken) {
         mService.mH.obtainMessage(ON_POINTER_DOWN_OUTSIDE_FOCUS, touchedToken).sendToTarget();
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 45cdacd..42d23e7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -186,7 +186,6 @@
 import android.graphics.Bitmap;
 import android.graphics.Matrix;
 import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.configstore.V1_0.OptionalBool;
@@ -7361,14 +7360,6 @@
                     .setPointerIconType(PointerIcon.TYPE_DEFAULT);
         }
     }
-
-    PointF getLatestMousePosition() {
-        synchronized (mMousePositionTracker) {
-            return new PointF(mMousePositionTracker.mLatestMouseX,
-                    mMousePositionTracker.mLatestMouseY);
-        }
-    }
-
     void setMousePointerDisplayId(int displayId) {
         mMousePositionTracker.setPointerDisplayId(displayId);
     }
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index b4e2fb6..dc43949 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -308,6 +308,7 @@
     void setMotionClassifierEnabled(bool enabled);
     std::optional<std::string> getBluetoothAddress(int32_t deviceId);
     void setStylusButtonMotionEventsEnabled(bool enabled);
+    std::array<float, 2> getMouseCursorPosition();
 
     /* --- InputReaderPolicyInterface implementation --- */
 
@@ -1655,6 +1656,16 @@
     return static_cast<bool>(enabled);
 }
 
+std::array<float, 2> NativeInputManager::getMouseCursorPosition() {
+    AutoMutex _l(mLock);
+    const auto pc = mLocked.pointerController.lock();
+    if (!pc) return {{std::nanf(""), std::nanf("")}};
+
+    std::array<float, 2> p{};
+    pc->getPosition(&p[0], &p[1]);
+    return p;
+}
+
 // ----------------------------------------------------------------------------
 
 static NativeInputManager* getNativeInputManager(JNIEnv* env, jobject clazz) {
@@ -2547,6 +2558,14 @@
     im->setStylusButtonMotionEventsEnabled(enabled);
 }
 
+static jfloatArray nativeGetMouseCursorPosition(JNIEnv* env, jobject nativeImplObj) {
+    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+    const std::array<float, 2> p = im->getMouseCursorPosition();
+    jfloatArray outArr = env->NewFloatArray(2);
+    env->SetFloatArrayRegion(outArr, 0, p.size(), p.data());
+    return outArr;
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gInputManagerMethods[] = {
@@ -2640,6 +2659,7 @@
         {"getBluetoothAddress", "(I)Ljava/lang/String;", (void*)nativeGetBluetoothAddress},
         {"setStylusButtonMotionEventsEnabled", "(Z)V",
          (void*)nativeSetStylusButtonMotionEventsEnabled},
+        {"getMouseCursorPosition", "()[F", (void*)nativeGetMouseCursorPosition},
 };
 
 #define FIND_CLASS(var, className) \