Merge "Force input focus to the last focused non-proxy display." into udc-dev
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 0d973d9..a3b4a0f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -194,7 +194,8 @@
         AccessibilityUserState.ServiceInfoChangeListener,
         AccessibilityWindowManager.AccessibilityEventSender,
         AccessibilitySecurityPolicy.AccessibilityUserManager,
-        SystemActionPerformer.SystemActionsChangedListener, ProxyManager.SystemSupport{
+        SystemActionPerformer.SystemActionsChangedListener,
+        SystemActionPerformer.DisplayUpdateCallBack, ProxyManager.SystemSupport {
 
     private static final boolean DEBUG = false;
 
@@ -1219,7 +1220,7 @@
     private SystemActionPerformer getSystemActionPerformer() {
         if (mSystemActionPerformer == null) {
             mSystemActionPerformer =
-                    new SystemActionPerformer(mContext, mWindowManagerService, null, this);
+                    new SystemActionPerformer(mContext, mWindowManagerService, null, this, this);
         }
         return mSystemActionPerformer;
     }
@@ -1619,6 +1620,18 @@
         }
     }
 
+    @Override
+    // TODO(b/276459590): Remove when this is resolved at the virtual device/input level.
+    public void moveNonProxyTopFocusedDisplayToTopIfNeeded() {
+        mA11yWindowManager.moveNonProxyTopFocusedDisplayToTopIfNeeded();
+    }
+
+    @Override
+    // TODO(b/276459590): Remove when this is resolved at the virtual device/input level.
+    public int getLastNonProxyTopFocusedDisplayId() {
+        return mA11yWindowManager.getLastNonProxyTopFocusedDisplayId();
+    }
+
     @VisibleForTesting
     void notifySystemActionsChangedLocked(AccessibilityUserState userState) {
         for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index a8a5365..78f07e4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -103,6 +103,9 @@
     // The top focused display and window token updated with the callback of window lists change.
     private int mTopFocusedDisplayId;
     private IBinder mTopFocusedWindowToken;
+
+    // The non-proxy display that most recently had top focus.
+    private int mLastNonProxyTopFocusedDisplayId;
     // The display has the accessibility focused window currently.
     private int mAccessibilityFocusedDisplayId = Display.INVALID_DISPLAY;
 
@@ -451,6 +454,9 @@
                 }
                 if (shouldUpdateWindowsLocked(forceSend, windows)) {
                     mTopFocusedDisplayId = topFocusedDisplayId;
+                    if (!isProxyed(topFocusedDisplayId)) {
+                        mLastNonProxyTopFocusedDisplayId = topFocusedDisplayId;
+                    }
                     mTopFocusedWindowToken = topFocusedWindowToken;
                     if (DEBUG) {
                         Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): updating windows for "
@@ -1141,6 +1147,21 @@
         return false;
     }
 
+    private boolean isProxyed(int displayId) {
+        final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId);
+        return (observer != null && observer.mIsProxy);
+    }
+
+    void moveNonProxyTopFocusedDisplayToTopIfNeeded() {
+        if (mHasProxy
+                && (mLastNonProxyTopFocusedDisplayId != mTopFocusedDisplayId)) {
+            mWindowManagerInternal.moveDisplayToTopIfAllowed(mLastNonProxyTopFocusedDisplayId);
+        }
+    }
+    int getLastNonProxyTopFocusedDisplayId() {
+        return mLastNonProxyTopFocusedDisplayId;
+    }
+
     /**
      * Checks if we are tracking windows on specified display.
      *
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index c89b9b8..a13df47 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -72,6 +72,13 @@
     }
     private final SystemActionsChangedListener mListener;
 
+    interface DisplayUpdateCallBack {
+        void moveNonProxyTopFocusedDisplayToTopIfNeeded();
+
+        int getLastNonProxyTopFocusedDisplayId();
+    }
+    private final DisplayUpdateCallBack mDisplayUpdateCallBack;
+
     private final Object mSystemActionLock = new Object();
     // Resource id based ActionId -> RemoteAction
     @GuardedBy("mSystemActionLock")
@@ -94,7 +101,7 @@
     public SystemActionPerformer(
             Context context,
             WindowManagerInternal windowManagerInternal) {
-      this(context, windowManagerInternal, null, null);
+      this(context, windowManagerInternal, null, null, null);
     }
 
     // Used to mock ScreenshotHelper
@@ -103,17 +110,19 @@
             Context context,
             WindowManagerInternal windowManagerInternal,
             Supplier<ScreenshotHelper> screenshotHelperSupplier) {
-        this(context, windowManagerInternal, screenshotHelperSupplier, null);
+        this(context, windowManagerInternal, screenshotHelperSupplier, null, null);
     }
 
     public SystemActionPerformer(
             Context context,
             WindowManagerInternal windowManagerInternal,
             Supplier<ScreenshotHelper> screenshotHelperSupplier,
-            SystemActionsChangedListener listener) {
+            SystemActionsChangedListener listener,
+            DisplayUpdateCallBack callback) {
         mContext = context;
         mWindowManagerService = windowManagerInternal;
         mListener = listener;
+        mDisplayUpdateCallBack = callback;
         mScreenshotHelperSupplier = screenshotHelperSupplier;
 
         mLegacyHomeAction = new AccessibilityAction(
@@ -245,6 +254,7 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             synchronized (mSystemActionLock) {
+                mDisplayUpdateCallBack.moveNonProxyTopFocusedDisplayToTopIfNeeded();
                 // If a system action is registered with the given actionId, call the corresponding
                 // RemoteAction.
                 RemoteAction registeredAction = mRegisteredSystemActions.get(actionId);
@@ -341,7 +351,7 @@
             int source) {
         KeyEvent event = KeyEvent.obtain(downTime, time, action, keyCode, 0, 0,
                 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
-                source, null);
+                source, mDisplayUpdateCallBack.getLastNonProxyTopFocusedDisplayId(), null);
         mContext.getSystemService(InputManager.class)
                 .injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
         event.recycle();
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
index eb71885..d9e25ef 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -199,6 +199,9 @@
             case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT:
                 mLastTouchedWindowId = event.getWindowId();
                 break;
+            case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END:
+                mAms.moveNonProxyTopFocusedDisplayToTopIfNeeded();
+                break;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 4922523..792ec2e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -444,6 +444,11 @@
     public abstract IBinder getFocusedWindowTokenFromWindowStates();
 
     /**
+     * Moves the given display to the top.
+     */
+    public abstract void moveDisplayToTopIfAllowed(int displayId);
+
+    /**
      * @return Whether the keyguard is engaged.
      */
     public abstract boolean isKeyguardLocked();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 62b3c7c..8822193 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7716,6 +7716,11 @@
         }
 
         @Override
+        public void moveDisplayToTopIfAllowed(int displayId) {
+            WindowManagerService.this.moveDisplayToTopIfAllowed(displayId);
+        }
+
+        @Override
         public boolean isKeyguardLocked() {
             return WindowManagerService.this.isKeyguardLocked();
         }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
index d9461aa..b62dbcd 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
@@ -108,6 +108,7 @@
     @Mock private StatusBarManager mMockStatusBarManager;
     @Mock private ScreenshotHelper mMockScreenshotHelper;
     @Mock private SystemActionPerformer.SystemActionsChangedListener mMockListener;
+    @Mock private SystemActionPerformer.DisplayUpdateCallBack mMockCallback;
 
     @Before
     public void setup() {
@@ -125,7 +126,7 @@
                 mMockContext,
                 mMockWindowManagerInternal,
                 () -> mMockScreenshotHelper,
-                mMockListener);
+                mMockListener, mMockCallback);
     }
 
     private void setupWithRealContext() {
@@ -133,7 +134,7 @@
                 InstrumentationRegistry.getContext(),
                 mMockWindowManagerInternal,
                 () -> mMockScreenshotHelper,
-                mMockListener);
+                mMockListener, mMockCallback);
     }
 
     // We need below two help functions because AccessbilityAction.equals function only compares