diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 0d973d9702366..a3b4a0f51c755 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + @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 a8a5365900040..78f07e4f2692a 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 @@ public class AccessibilityWindowManager { // 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 @@ public class AccessibilityWindowManager { } 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 @@ public class AccessibilityWindowManager { 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 c89b9b8517423..a13df475d25d2 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 @@ public class SystemActionPerformer { } 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 class SystemActionPerformer { 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 @@ public class SystemActionPerformer { Context context, WindowManagerInternal windowManagerInternal, Supplier screenshotHelperSupplier) { - this(context, windowManagerInternal, screenshotHelperSupplier, null); + this(context, windowManagerInternal, screenshotHelperSupplier, null, null); } public SystemActionPerformer( Context context, WindowManagerInternal windowManagerInternal, Supplier screenshotHelperSupplier, - SystemActionsChangedListener listener) { + SystemActionsChangedListener listener, + DisplayUpdateCallBack callback) { mContext = context; mWindowManagerService = windowManagerInternal; mListener = listener; + mDisplayUpdateCallBack = callback; mScreenshotHelperSupplier = screenshotHelperSupplier; mLegacyHomeAction = new AccessibilityAction( @@ -245,6 +254,7 @@ public class SystemActionPerformer { 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 @@ public class SystemActionPerformer { 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 eb718853a6ce5..d9e25ef7dcdce 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 @@ public class TouchState { 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 492252314356b..792ec2e920835 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -443,6 +443,11 @@ public abstract class WindowManagerInternal { */ public abstract IBinder getFocusedWindowTokenFromWindowStates(); + /** + * Moves the given display to the top. + */ + public abstract void moveDisplayToTopIfAllowed(int displayId); + /** * @return Whether the keyguard is engaged. */ diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 62b3c7cd1daff..8822193ab522e 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7715,6 +7715,11 @@ public class WindowManagerService extends IWindowManager.Stub } } + @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 d9461aada4d3c..b62dbcd526cbc 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 @@ public class SystemActionPerformerTest { @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 @@ public class SystemActionPerformerTest { mMockContext, mMockWindowManagerInternal, () -> mMockScreenshotHelper, - mMockListener); + mMockListener, mMockCallback); } private void setupWithRealContext() { @@ -133,7 +134,7 @@ public class SystemActionPerformerTest { InstrumentationRegistry.getContext(), mMockWindowManagerInternal, () -> mMockScreenshotHelper, - mMockListener); + mMockListener, mMockCallback); } // We need below two help functions because AccessbilityAction.equals function only compares