From b68bbe05d72a6d10fc3b55b1c43f4f7e5727f107 Mon Sep 17 00:00:00 2001 From: ryanlwlin Date: Wed, 6 Oct 2021 14:49:29 +0800 Subject: [PATCH] Support individual magnification mode for each display Currently we only have a globale magnification mode which is stored in settings. It ends up the mode is shared between multi-displays. To provide better experience, we store the mode for each none-default display until the device reboot, and the default mode is full-screen. Test: atest com.android.server.accessibility atest com.android.systemui.accessibility Bug: 194667380 Change-Id: I099e1ee945938aac07cc079f68bccbc745d40b08 --- .../MagnificationModeSwitch.java | 26 ++- .../accessibility/ModeSwitchesController.java | 36 +++- .../accessibility/WindowMagnification.java | 3 + .../WindowMagnificationConnectionImpl.java | 10 + .../MagnificationModeSwitchTest.java | 47 +++-- .../ModeSwitchesControllerTest.java | 26 ++- .../WindowMagnificationTest.java | 8 +- .../AccessibilityManagerService.java | 114 +++++++---- .../accessibility/AccessibilityUserState.java | 36 ++-- ...FullScreenMagnificationGestureHandler.java | 4 +- .../MagnificationController.java | 19 +- .../WindowMagnificationManager.java | 11 +- .../AccessibilityManagerServiceTest.java | 181 ++++++++++++++---- .../AccessibilityUserStateTest.java | 15 +- .../MagnificationControllerTest.java | 21 +- 15 files changed, 418 insertions(+), 139 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java index e521c90961fba..052ec86d83985 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java @@ -28,7 +28,6 @@ import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Bundle; -import android.os.UserHandle; import android.provider.Settings; import android.util.MathUtils; import android.view.Gravity; @@ -75,6 +74,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; private int mMagnificationMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE; private final LayoutParams mParams; + private final SwitchListener mSwitchListener; @VisibleForTesting final Rect mDraggableWindowBounds = new Rect(); private boolean mIsVisible = false; @@ -82,17 +82,29 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL private boolean mSingleTapDetected = false; private boolean mToLeftScreenEdge = false; - MagnificationModeSwitch(@UiContext Context context) { - this(context, createView(context), new SfVsyncFrameCallbackProvider()); + public interface SwitchListener { + /** + * Called when the switch is clicked to change the magnification mode. + * @param displayId the display id of the display to which the view's window has been + * attached + * @param magnificationMode the magnification mode + */ + void onSwitch(int displayId, int magnificationMode); + } + + MagnificationModeSwitch(@UiContext Context context, + SwitchListener switchListener) { + this(context, createView(context), new SfVsyncFrameCallbackProvider(), switchListener); } @VisibleForTesting MagnificationModeSwitch(Context context, @NonNull ImageView imageView, - SfVsyncFrameCallbackProvider sfVsyncFrameProvider) { + SfVsyncFrameCallbackProvider sfVsyncFrameProvider, SwitchListener switchListener) { mContext = context; mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); mWindowManager = mContext.getSystemService(WindowManager.class); mSfVsyncFrameProvider = sfVsyncFrameProvider; + mSwitchListener = switchListener; mParams = createLayoutParams(context); mImageView = imageView; mImageView.setOnTouchListener(this::onTouch); @@ -364,11 +376,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL mMagnificationMode ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; mMagnificationMode = newMode; mImageView.setImageResource(getIconResId(newMode)); - Settings.Secure.putIntForUser( - mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, - newMode, - UserHandle.USER_CURRENT); + mSwitchListener.onSwitch(mContext.getDisplayId(), newMode); } private void handleSingleTap() { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java index 1a01ad85fccc2..5e48ee3d23664 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java @@ -18,6 +18,8 @@ package com.android.systemui.accessibility; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; +import static com.android.systemui.accessibility.MagnificationModeSwitch.SwitchListener; + import android.annotation.MainThread; import android.content.Context; import android.hardware.display.DisplayManager; @@ -27,21 +29,24 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.dagger.SysUISingleton; /** - * A class to control {@link MagnificationModeSwitch}. It should show the button UI with following + * A class to control {@link MagnificationModeSwitch}. It shows the button UI with following * conditions: *
    *
  1. Both full-screen and window magnification mode are capable.
  2. *
  3. The magnification scale is changed by a user.
  4. *
      + * The switch action will be handled by {@link #mSwitchListenerDelegate} which informs the system + * server about the changed mode. */ @SysUISingleton -public class ModeSwitchesController { +public class ModeSwitchesController implements SwitchListener { private final DisplayIdIndexSupplier mSwitchSupplier; + private SwitchListener mSwitchListenerDelegate; public ModeSwitchesController(Context context) { mSwitchSupplier = new SwitchSupplier(context, - context.getSystemService(DisplayManager.class)); + context.getSystemService(DisplayManager.class), this::onSwitch); } @VisibleForTesting @@ -50,8 +55,8 @@ public class ModeSwitchesController { } /** - * Shows a button that a user can click the button to switch magnification mode. And the - * button would be dismissed automatically after the button is displayed for a period of time. + * Shows a button that a user can click to switch magnification mode. And the button + * would be dismissed automatically after the button is displayed for a period of time. * * @param displayId The logical display id * @param mode The magnification mode @@ -93,24 +98,41 @@ public class ModeSwitchesController { switchController -> switchController.onConfigurationChanged(configDiff)); } + @Override + public void onSwitch(int displayId, int magnificationMode) { + if (mSwitchListenerDelegate != null) { + mSwitchListenerDelegate.onSwitch(displayId, magnificationMode); + } + } + + public void setSwitchListenerDelegate(SwitchListener switchListenerDelegate) { + mSwitchListenerDelegate = switchListenerDelegate; + } + private static class SwitchSupplier extends DisplayIdIndexSupplier { private final Context mContext; + private final SwitchListener mSwitchListener; /** + * Supplies the switch for the given display. + * * @param context Context * @param displayManager DisplayManager + * @param switchListener The callback that will run when the switch is clicked */ - SwitchSupplier(Context context, DisplayManager displayManager) { + SwitchSupplier(Context context, DisplayManager displayManager, + SwitchListener switchListener) { super(displayManager); mContext = context; + mSwitchListener = switchListener; } @Override protected MagnificationModeSwitch createInstance(Display display) { final Context uiContext = mContext.createWindowContext(display, TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null); - return new MagnificationModeSwitch(uiContext); + return new MagnificationModeSwitch(uiContext, mSwitchListener); } } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java index 32813479dc0a2..d34ac71735efd 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java @@ -243,12 +243,15 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall mWindowMagnificationConnectionImpl = new WindowMagnificationConnectionImpl(this, mHandler, mModeSwitchesController); } + mModeSwitchesController.setSwitchListenerDelegate( + mWindowMagnificationConnectionImpl::onChangeMagnificationMode); mAccessibilityManager.setWindowMagnificationConnection( mWindowMagnificationConnectionImpl); } private void clearWindowMagnificationConnection() { mAccessibilityManager.setWindowMagnificationConnection(null); + mModeSwitchesController.setSwitchListenerDelegate(null); //TODO: destroy controllers. } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java index 2d620ab9e3c92..92cd8b183b62b 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java @@ -130,4 +130,14 @@ class WindowMagnificationConnectionImpl extends IWindowMagnificationConnection.S } } } + + void onChangeMagnificationMode(int displayId, int mode) { + if (mConnectionCallback != null) { + try { + mConnectionCallback.onChangeMagnificationMode(displayId, mode); + } catch (RemoteException e) { + Log.e(TAG, "Failed to inform changing magnification mode", e); + } + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java index fe5633eb9df8d..6ddfbb2f430f6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java @@ -55,10 +55,9 @@ import android.graphics.Insets; import android.graphics.Rect; import android.os.Handler; import android.os.SystemClock; -import android.os.UserHandle; -import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.util.SparseIntArray; import android.view.Choreographer; import android.view.MotionEvent; import android.view.View; @@ -101,6 +100,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { private AccessibilityManager mAccessibilityManager; @Mock private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; + private SwitchListenerStub mSwitchListener; private TestableWindowManager mWindowManager; private ViewPropertyAnimator mViewPropertyAnimator; private MagnificationModeSwitch mMagnificationModeSwitch; @@ -112,6 +112,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); final WindowManager wm = mContext.getSystemService(WindowManager.class); + mSwitchListener = new SwitchListenerStub(); mWindowManager = spy(new TestableWindowManager(wm)); mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager); @@ -130,7 +131,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { }).when(mSfVsyncFrameProvider).postFrameCallback( any(Choreographer.FrameCallback.class)); mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView, - mSfVsyncFrameProvider); + mSfVsyncFrameProvider, mSwitchListener); assertNotNull(mTouchListener); } @@ -326,8 +327,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { public void performDragging_showMagnificationButton_updateViewLayout() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); resetAndStubMockImageViewAndAnimator(); - final int previousMode = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0, UserHandle.USER_CURRENT); // Perform dragging final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop() + 10; @@ -345,7 +344,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( downTime, downTime, ACTION_UP, 100 + offset, 100)); - assertModeUnchanged(previousMode); + assertModeUnchanged(); assertShowFadingAnimation(FADE_OUT_ALPHA); } @@ -353,8 +352,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { public void performSingleTapActionCanceled_showButtonAnimation() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); resetAndStubMockImageViewAndAnimator(); - final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0); final long downTime = SystemClock.uptimeMillis(); mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( @@ -363,7 +360,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( downTime, downTime, ACTION_CANCEL, 100, 100)); - assertModeUnchanged(previousMode); + assertModeUnchanged(); assertShowFadingAnimation(FADE_OUT_ALPHA); } @@ -371,8 +368,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { public void performDraggingActionCanceled_showButtonAnimation() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); resetAndStubMockImageViewAndAnimator(); - final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0); // Perform dragging final long downTime = SystemClock.uptimeMillis(); @@ -385,7 +380,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { mTouchListener.onTouch(mSpyImageView, obtainMotionEvent( downTime, downTime, ACTION_CANCEL, 100 + offset, 100)); - assertModeUnchanged(previousMode); + assertModeUnchanged(); assertShowFadingAnimation(FADE_OUT_ALPHA); } @@ -529,10 +524,9 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { assertEquals(expectedY, mWindowManager.getLayoutParamsFromAttachedView().y); } - private void assertModeUnchanged(int expectedMode) { - final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0); - assertEquals(expectedMode, actualMode); + private void assertModeUnchanged() { + assertEquals(SwitchListenerStub.MODE_INVALID, + mSwitchListener.getChangedMode(mContext.getDisplayId())); } private void assertShowFadingAnimation(float alpha) { @@ -594,9 +588,8 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { verify(mSpyImageView).setImageResource( getIconResId(expectedMode)); verify(mWindowManager).removeView(mSpyImageView); - final int actualMode = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0, UserHandle.USER_CURRENT); - assertEquals(expectedMode, actualMode); + final int changedMode = mSwitchListener.getChangedMode(mContext.getDisplayId()); + assertEquals(expectedMode, changedMode); } private MotionEvent obtainMotionEvent(long downTime, long eventTime, int action, float x, @@ -621,4 +614,20 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { assertEquals(expectedX, layoutParams.x); assertEquals(expectedY, layoutParams.y); } + + private static class SwitchListenerStub implements MagnificationModeSwitch.SwitchListener { + + private static final int MODE_INVALID = -1; + + private final SparseIntArray mModes = new SparseIntArray(); + + @Override + public void onSwitch(int displayId, int magnificationMode) { + mModes.put(displayId, magnificationMode); + } + + int getChangedMode(int displayId) { + return mModes.get(displayId, MODE_INVALID); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java index 9fa5b87af3b45..216f63fce8858 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java @@ -22,27 +22,32 @@ import android.content.pm.ActivityInfo; import android.hardware.display.DisplayManager; import android.provider.Settings; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.view.Display; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) /** Tests the ModeSwitchesController. */ +@TestableLooper.RunWithLooper(setAsMainLooper = true) public class ModeSwitchesControllerTest extends SysuiTestCase { private FakeSwitchSupplier mSupplier; - @Mock private MagnificationModeSwitch mModeSwitch; private ModeSwitchesController mModeSwitchesController; + @Mock + private MagnificationModeSwitch.SwitchListener mListener; @Before @@ -50,6 +55,13 @@ public class ModeSwitchesControllerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mSupplier = new FakeSwitchSupplier(mContext.getSystemService(DisplayManager.class)); mModeSwitchesController = new ModeSwitchesController(mSupplier); + mModeSwitchesController.setSwitchListenerDelegate(mListener); + mModeSwitch = Mockito.spy(new MagnificationModeSwitch(mContext, mModeSwitchesController)); + } + + @After + public void tearDown() { + mModeSwitchesController.removeButton(Display.DEFAULT_DISPLAY); } @Test @@ -79,6 +91,18 @@ public class ModeSwitchesControllerTest extends SysuiTestCase { verify(mModeSwitch).onConfigurationChanged(ActivityInfo.CONFIG_DENSITY); } + + @Test + public void testOnSwitchClick_showWindowModeButton_invokeListener() { + mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY, + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + + mModeSwitch.onSingleTap(); + + verify(mListener).onSwitch(mContext.getDisplayId(), + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); + } + private class FakeSwitchSupplier extends DisplayIdIndexSupplier { FakeSwitchSupplier(DisplayManager displayManager) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java index 6ef7cc3f0af8d..fb1716aed4748 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java @@ -22,6 +22,8 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_M import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -99,17 +101,19 @@ public class WindowMagnificationTest extends SysuiTestCase { } @Test - public void requestWindowMagnificationConnection_setWindowMagnificationConnection() { + public void requestWindowMagnificationConnection_setConnectionAndListener() { mCommandQueue.requestWindowMagnificationConnection(true); waitForIdleSync(); verify(mAccessibilityManager).setWindowMagnificationConnection(any( IWindowMagnificationConnection.class)); + verify(mModeSwitchesController).setSwitchListenerDelegate(notNull()); mCommandQueue.requestWindowMagnificationConnection(false); waitForIdleSync(); - verify(mAccessibilityManager).setWindowMagnificationConnection(null); + verify(mAccessibilityManager).setWindowMagnificationConnection(isNull()); + verify(mModeSwitchesController).setSwitchListenerDelegate(isNull()); } @Test diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 214769bbd9c98..0e39327991237 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -272,6 +272,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return getUserStateLocked(mCurrentUserId); } + /** + * Changes the magnification mode on the given display. + * + * @param displayId the logical display + * @param magnificationMode the target magnification mode + */ + public void changeMagnificationMode(int displayId, int magnificationMode) { + synchronized (mLock) { + if (displayId == Display.DEFAULT_DISPLAY) { + persistMagnificationModeSettingsLocked(magnificationMode); + } else { + final AccessibilityUserState userState = getCurrentUserStateLocked(); + final int currentMode = userState.getMagnificationModeLocked(displayId); + if (magnificationMode != currentMode) { + userState.setMagnificationModeLocked(displayId, magnificationMode); + updateMagnificationModeChangeSettingsLocked(userState, displayId); + } + } + } + } + public static final class Lifecycle extends SystemService { private final AccessibilityManagerService mService; @@ -299,7 +320,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub SystemActionPerformer systemActionPerformer, AccessibilityWindowManager a11yWindowManager, AccessibilityDisplayListener a11yDisplayListener, - MagnificationController magnificationController) { + MagnificationController magnificationController, + @Nullable AccessibilityInputFilter inputFilter) { mContext = context; mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mWindowManagerService = LocalServices.getService(WindowManagerInternal.class); @@ -314,6 +336,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mA11yDisplayListener = a11yDisplayListener; mMagnificationController = magnificationController; mMagnificationProcessor = new MagnificationProcessor(mMagnificationController); + if (inputFilter != null) { + mInputFilter = inputFilter; + mHasInputFilter = true; + } init(); } @@ -1794,47 +1820,51 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return relevantEventTypes; } - private void updateMagnificationModeChangeSettingsLocked(AccessibilityUserState userState) { + private void updateMagnificationModeChangeSettingsLocked(AccessibilityUserState userState, + int displayId) { if (userState.mUserId != mCurrentUserId) { return; } // New mode is invalid, so ignore and restore it. - if (fallBackMagnificationModeSettingsLocked(userState)) { + if (fallBackMagnificationModeSettingsLocked(userState, displayId)) { return; } mMagnificationController.transitionMagnificationModeLocked( - Display.DEFAULT_DISPLAY, userState.getMagnificationModeLocked(), + displayId, userState.getMagnificationModeLocked(displayId), this::onMagnificationTransitionEndedLocked); } /** - * Called when the magnification mode transition is completed. + * Called when the magnification mode transition is completed. If the given display is default + * display, we also need to fall back the mode in user settings. */ - void onMagnificationTransitionEndedLocked(boolean success) { + void onMagnificationTransitionEndedLocked(int displayId, boolean success) { final AccessibilityUserState userState = getCurrentUserStateLocked(); - final int previousMode = userState.getMagnificationModeLocked() + final int previousMode = userState.getMagnificationModeLocked(displayId) ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; if (!success && previousMode != 0) { - userState.setMagnificationModeLocked(previousMode); - persistMagnificationModeSettingLocked(previousMode); + userState.setMagnificationModeLocked(displayId, previousMode); + if (displayId == Display.DEFAULT_DISPLAY) { + persistMagnificationModeSettingsLocked(previousMode); + } } else { mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::notifyRefreshMagnificationModeToInputFilter, - this)); + this, displayId)); } } - private void notifyRefreshMagnificationModeToInputFilter() { + private void notifyRefreshMagnificationModeToInputFilter(int displayId) { synchronized (mLock) { if (!mHasInputFilter) { return; } - // TODO: notify the mode change on specified display. final ArrayList displays = getValidDisplayList(); for (int i = 0; i < displays.size(); i++) { final Display display = displays.get(i); - if (display != null) { + if (display != null && display.getDisplayId() == displayId) { mInputFilter.refreshMagnificationMode(display); + return; } } } @@ -2245,12 +2275,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub scheduleUpdateClientsIfNeededLocked(userState); updateAccessibilityShortcutKeyTargetsLocked(userState); updateAccessibilityButtonTargetsLocked(userState); - // Update the capabilities before the mode. + // Update the capabilities before the mode because we will check the current mode is + // invalid or not.. updateMagnificationCapabilitiesSettingsChangeLocked(userState); - updateMagnificationModeChangeSettingsLocked(userState); + updateMagnificationModeChangeSettingsForAllDisplaysLocked(userState); updateFocusAppearanceDataLocked(userState); } + private void updateMagnificationModeChangeSettingsForAllDisplaysLocked( + AccessibilityUserState userState) { + final ArrayList displays = getValidDisplayList(); + for (int i = 0; i < displays.size(); i++) { + final int displayId = displays.get(i).getDisplayId(); + updateMagnificationModeChangeSettingsLocked(userState, displayId); + } + } + private void updateWindowsForAccessibilityCallbackLocked(AccessibilityUserState userState) { // We observe windows for accessibility only if there is at least // one bound service that can retrieve window content that specified @@ -2348,7 +2388,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub somethingChanged |= readAccessibilityButtonTargetsLocked(userState); somethingChanged |= readAccessibilityButtonTargetComponentLocked(userState); somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState); - somethingChanged |= readMagnificationModeLocked(userState); + somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState); somethingChanged |= readMagnificationCapabilitiesLocked(userState); return somethingChanged; } @@ -3878,8 +3918,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub || mUserInteractiveUiTimeoutUri.equals(uri)) { readUserRecommendedUiTimeoutSettingsLocked(userState); } else if (mMagnificationModeUri.equals(uri)) { - if (readMagnificationModeLocked(userState)) { - updateMagnificationModeChangeSettingsLocked(userState); + if (readMagnificationModeForDefaultDisplayLocked(userState)) { + updateMagnificationModeChangeSettingsLocked(userState, + Display.DEFAULT_DISPLAY); } } else if (mMagnificationCapabilityUri.equals(uri)) { if (readMagnificationCapabilitiesLocked(userState)) { @@ -3892,8 +3933,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void updateMagnificationCapabilitiesSettingsChangeLocked( AccessibilityUserState userState) { - if (fallBackMagnificationModeSettingsLocked(userState)) { - updateMagnificationModeChangeSettingsLocked(userState); + final ArrayList displays = getValidDisplayList(); + for (int i = 0; i < displays.size(); i++) { + final int displayId = displays.get(i).getDisplayId(); + if (fallBackMagnificationModeSettingsLocked(userState, displayId)) { + updateMagnificationModeChangeSettingsLocked(userState, displayId); + } } updateWindowMagnificationConnectionIfNeeded(userState); // Remove magnification button UI when the magnification capability is not all mode or @@ -3902,7 +3947,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub || userState.isShortcutMagnificationEnabledLocked()) || userState.getMagnificationCapabilitiesLocked() != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) { - final ArrayList displays = getValidDisplayList(); + for (int i = 0; i < displays.size(); i++) { final int displayId = displays.get(i).getDisplayId(); getWindowMagnificationMgr().removeMagnificationButton(displayId); @@ -3910,18 +3955,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private boolean fallBackMagnificationModeSettingsLocked(AccessibilityUserState userState) { - if (userState.isValidMagnificationModeLocked()) { + private boolean fallBackMagnificationModeSettingsLocked(AccessibilityUserState userState, + int displayId) { + if (userState.isValidMagnificationModeLocked(displayId)) { return false; } - Slog.w(LOG_TAG, "invalid magnification mode:" + userState.getMagnificationModeLocked()); + Slog.w(LOG_TAG, "displayId " + displayId + ", invalid magnification mode:" + + userState.getMagnificationModeLocked(displayId)); final int capabilities = userState.getMagnificationCapabilitiesLocked(); - userState.setMagnificationModeLocked(capabilities); - persistMagnificationModeSettingLocked(capabilities); + userState.setMagnificationModeLocked(displayId, capabilities); + if (displayId == Display.DEFAULT_DISPLAY) { + persistMagnificationModeSettingsLocked(capabilities); + } return true; } - private void persistMagnificationModeSettingLocked(int mode) { + private void persistMagnificationModeSettingsLocked(int mode) { BackgroundThread.getHandler().post(() -> { final long identity = Binder.clearCallingIdentity(); try { @@ -3933,7 +3982,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub }); } - //TODO: support multi-display. /** * Gets the magnification mode of the specified display. * @@ -3943,17 +3991,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ public int getMagnificationMode(int displayId) { synchronized (mLock) { - return getCurrentUserStateLocked().getMagnificationModeLocked(); + return getCurrentUserStateLocked().getMagnificationModeLocked(displayId); } } - private boolean readMagnificationModeLocked(AccessibilityUserState userState) { + // Only the value of the default display is from user settings because not each of displays has + // a unique id. + private boolean readMagnificationModeForDefaultDisplayLocked(AccessibilityUserState userState) { final int magnificationMode = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, userState.mUserId); - if (magnificationMode != userState.getMagnificationModeLocked()) { - userState.setMagnificationModeLocked(magnificationMode); + if (magnificationMode != userState.getMagnificationModeLocked(Display.DEFAULT_DISPLAY)) { + userState.setMagnificationModeLocked(Display.DEFAULT_DISPLAY, magnificationMode); return true; } return false; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java index c70bf73fc7f5d..8c3ca3430a3aa 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -23,6 +23,7 @@ import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD; import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; import static android.view.accessibility.AccessibilityManager.ShortcutType; @@ -42,6 +43,7 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.ArraySet; import android.util.Slog; +import android.util.SparseIntArray; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManagerClient; @@ -122,8 +124,8 @@ class AccessibilityUserState { /** {@code true} if the device config supports magnification area. */ private final boolean mSupportMagnificationArea; - // The magnification mode of default display. - private int mMagnificationMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; + // The magnification modes on displays. + private final SparseIntArray mMagnificationModes = new SparseIntArray(); // The magnification capabilities used to know magnification mode could be switched. private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; @@ -141,12 +143,13 @@ class AccessibilityUserState { @SoftKeyboardShowMode private int mSoftKeyboardShowMode = SHOW_MODE_AUTO; - boolean isValidMagnificationModeLocked() { + boolean isValidMagnificationModeLocked(int displayId) { + final int mode = getMagnificationModeLocked(displayId); if (!mSupportMagnificationArea - && mMagnificationMode == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) { + && mode == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) { return false; } - return (mMagnificationCapabilities & mMagnificationMode) != 0; + return (mMagnificationCapabilities & mode) != 0; } interface ServiceInfoChangeListener { @@ -203,7 +206,7 @@ class AccessibilityUserState { mIsAutoclickEnabled = false; mUserNonInteractiveUiTimeout = 0; mUserInteractiveUiTimeout = 0; - mMagnificationMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; + mMagnificationModes.clear(); mFocusStrokeWidth = mFocusStrokeWidthDefaultValue; mFocusColor = mFocusColorDefaultValue; } @@ -500,7 +503,7 @@ class AccessibilityUserState { pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveUiTimeout)); pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveUiTimeout)); pw.append(", installedServiceCount=").append(String.valueOf(mInstalledServices.size())); - pw.append(", magnificationMode=").append(String.valueOf(mMagnificationMode)); + pw.append(", magnificationModes=").append(String.valueOf(mMagnificationModes)); pw.append(", magnificationCapabilities=") .append(String.valueOf(mMagnificationCapabilities)); pw.append("}"); @@ -635,14 +638,19 @@ class AccessibilityUserState { } /** - * Gets the magnification mode of default display. + * Gets the magnification mode for the given display. * @return magnification mode * * @see Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN * @see Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW */ - public int getMagnificationModeLocked() { - return mMagnificationMode; + public int getMagnificationModeLocked(int displayId) { + int mode = mMagnificationModes.get(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_NONE); + if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_NONE) { + mode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; + setMagnificationModeLocked(displayId, mode); + } + return mode; } @@ -671,11 +679,13 @@ class AccessibilityUserState { } /** - * Sets the magnification mode of default display. + * Sets the magnification mode to the given display. + * + * @param displayId The display id. * @param mode The magnification mode. */ - public void setMagnificationModeLocked(int mode) { - mMagnificationMode = mode; + public void setMagnificationModeLocked(int displayId, int mode) { + mMagnificationModes.put(displayId, mode); } /** diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index 935df9934dcbb..966d887d11f7a 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -208,8 +208,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH } mPromptController.onDestroy(); // Check if need to reset when MagnificationGestureHandler is the last magnifying service. - mFullScreenMagnificationController.resetAllIfNeeded( - AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); + mFullScreenMagnificationController.resetIfNeeded( + mDisplayId, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); clearAndTransitionToStateDetecting(); } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index 3708c7a422a0d..6473bf5ffc3ef 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -92,14 +92,16 @@ public class MagnificationController implements WindowMagnificationManager.Callb private long mFullScreenModeEnabledTime = 0; /** - * A callback to inform the magnification transition result. + * A callback to inform the magnification transition result on the given display. */ public interface TransitionCallBack { /** * Invoked when the transition ends. + * + * @param displayId The display id. * @param success {@code true} if the transition success. */ - void onResult(boolean success); + void onResult(int displayId, boolean success); } public MagnificationController(AccessibilityManagerService ams, Object lock, @@ -179,7 +181,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb final DisableMagnificationCallback animationCallback = getDisableMagnificationEndRunnableLocked(displayId); if (magnificationCenter == null && animationCallback == null) { - transitionCallBack.onResult(true); + transitionCallBack.onResult(displayId, true); return; } @@ -195,7 +197,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb if (magnificationCenter == null) { Slog.w(TAG, "Invalid center, ignore it"); - transitionCallBack.onResult(true); + transitionCallBack.onResult(displayId, true); return; } final FullScreenMagnificationController screenMagnificationController = @@ -251,6 +253,11 @@ public class MagnificationController implements WindowMagnificationManager.Callb updateMagnificationButton(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); } + @Override + public void onChangeMagnificationMode(int displayId, int magnificationMode) { + mAms.changeMagnificationMode(displayId, magnificationMode); + } + private void disableFullScreenMagnificationIfNeeded(int displayId) { final FullScreenMagnificationController fullScreenMagnificationController = getFullScreenMagnificationController(); @@ -514,7 +521,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb applyMagnificationModeLocked(mTargetMode); } updateMagnificationButton(mDisplayId, mTargetMode); - mTransitionCallBack.onResult(success); + mTransitionCallBack.onResult(mDisplayId, success); } } @@ -539,7 +546,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb setExpiredAndRemoveFromListLocked(); applyMagnificationModeLocked(mCurrentMode); updateMagnificationButton(mDisplayId, mCurrentMode); - mTransitionCallBack.onResult(true); + mTransitionCallBack.onResult(mDisplayId, true); } } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java index ce7ba7568b6e4..bfaab9a0aa709 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java @@ -106,6 +106,15 @@ public class WindowMagnificationManager implements * @param activated {@code true} if the magnification is activated, otherwise {@code false}. */ void onWindowMagnificationActivationState(int displayId, boolean activated); + + /** + * Called from {@link IWindowMagnificationConnection} to request changing the magnification + * mode on the given display. + * + * @param displayId the logical display id + * @param magnificationMode the target magnification mode + */ + void onChangeMagnificationMode(int displayId, int magnificationMode); } private final Callback mCallback; @@ -535,7 +544,7 @@ public class WindowMagnificationManager implements FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK, "displayId=" + displayId + ";mode=" + magnificationMode); } - //TODO: Uses this method to change the magnification mode on non-default display. + mCallback.onChangeMagnificationMode(displayId, magnificationMode); } @Override diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index ae2b8dc2add7a..3ade9ff617356 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -16,13 +16,22 @@ package com.android.server.accessibility; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE; +import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -38,17 +47,23 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; -import android.content.res.Resources; import android.graphics.drawable.Icon; +import android.hardware.display.DisplayManagerGlobal; import android.os.IBinder; import android.provider.Settings; -import android.test.suitebuilder.annotation.SmallTest; +import android.testing.TestableContext; +import android.view.Display; +import android.view.DisplayAdjustments; +import android.view.DisplayInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import com.android.compatibility.common.util.TestUtils; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener; +import com.android.server.accessibility.magnification.FullScreenMagnificationController; import com.android.server.accessibility.magnification.MagnificationController; import com.android.server.accessibility.magnification.WindowMagnificationManager; import com.android.server.accessibility.test.MessageCapturingHandler; @@ -58,10 +73,16 @@ import com.android.server.wm.WindowManagerInternal; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; + /** * APCT tests for {@link AccessibilityManagerService}. */ @@ -72,7 +93,8 @@ public class AccessibilityManagerServiceTest { private static final String INTENT_ACTION = "TESTACTION"; private static final String DESCRIPTION = "description"; private static final PendingIntent TEST_PENDING_INTENT = PendingIntent.getBroadcast( - InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION), PendingIntent.FLAG_MUTABLE_UNAUDITED); + InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION), + PendingIntent.FLAG_MUTABLE_UNAUDITED); private static final RemoteAction TEST_ACTION = new RemoteAction( Icon.createWithContentUri("content://test"), LABEL, @@ -81,11 +103,12 @@ public class AccessibilityManagerServiceTest { private static final AccessibilityAction NEW_ACCESSIBILITY_ACTION = new AccessibilityAction(ACTION_ID, LABEL); + private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY + 1; + static final ComponentName COMPONENT_NAME = new ComponentName( "com.android.server.accessibility", "AccessibilityManagerServiceTest"); static final int SERVICE_ID = 42; - @Mock private Context mMockContext; @Mock private AccessibilityServiceInfo mMockServiceInfo; @Mock private ResolveInfo mMockResolveInfo; @Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport; @@ -102,12 +125,15 @@ public class AccessibilityManagerServiceTest { @Mock private IAccessibilityServiceClient mMockServiceClient; @Mock private WindowMagnificationManager mMockWindowMagnificationMgr; @Mock private MagnificationController mMockMagnificationController; - @Mock private Resources mMockResources; + @Mock private FullScreenMagnificationController mMockFullScreenMagnificationController; - private AccessibilityUserState mUserState; + @Rule + public final TestableContext mTestableContext = new TestableContext( + getInstrumentation().getTargetContext(), null); private MessageCapturingHandler mHandler = new MessageCapturingHandler(null); private AccessibilityServiceConnection mAccessibilityServiceConnection; + private AccessibilityInputFilter mInputFilter; private AccessibilityManagerService mA11yms; @Before @@ -122,43 +148,54 @@ public class AccessibilityManagerServiceTest { ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal); LocalServices.addService( UserManagerInternal.class, mMockUserManagerInternal); + mInputFilter = Mockito.mock(FakeInputFilter.class); when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn( mMockWindowMagnificationMgr); + when(mMockMagnificationController.getFullScreenMagnificationController()).thenReturn( + mMockFullScreenMagnificationController); when(mMockWindowManagerService.getAccessibilityController()).thenReturn( mMockA11yController); when(mMockA11yController.isAccessibilityTracingEnabled()).thenReturn(false); when(mMockUserManagerInternal.isUserUnlockingOrUnlocked(anyInt())).thenReturn(true); + + final ArrayList displays = new ArrayList<>(); + final Display defaultDisplay = new Display(DisplayManagerGlobal.getInstance(), + Display.DEFAULT_DISPLAY, new DisplayInfo(), + DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); + final Display testDisplay = new Display(DisplayManagerGlobal.getInstance(), TEST_DISPLAY, + new DisplayInfo(), DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); + displays.add(defaultDisplay); + displays.add(testDisplay); + when(mMockA11yDisplayListener.getValidDisplayList()).thenReturn(displays); + mA11yms = new AccessibilityManagerService( - InstrumentationRegistry.getContext(), - mMockPackageManager, - mMockSecurityPolicy, - mMockSystemActionPerformer, - mMockA11yWindowManager, - mMockA11yDisplayListener, - mMockMagnificationController); + mTestableContext, + mMockPackageManager, + mMockSecurityPolicy, + mMockSystemActionPerformer, + mMockA11yWindowManager, + mMockA11yDisplayListener, + mMockMagnificationController, + mInputFilter); - mMockResources = mock(Resources.class); - when(mMockContext.getResources()).thenReturn(mMockResources); - - mUserState = new AccessibilityUserState( - mA11yms.getCurrentUserIdLocked(), mMockContext, mA11yms); - mA11yms.mUserStates.put(mA11yms.getCurrentUserIdLocked(), mUserState); + final AccessibilityUserState userState = new AccessibilityUserState( + mA11yms.getCurrentUserIdLocked(), mTestableContext, mA11yms); + mA11yms.mUserStates.put(mA11yms.getCurrentUserIdLocked(), userState); } private void setupAccessibilityServiceConnection() { - when(mMockContext.getSystemService(Context.DISPLAY_SERVICE)).thenReturn( - InstrumentationRegistry.getContext().getSystemService( - Context.DISPLAY_SERVICE)); - + final AccessibilityUserState userState = mA11yms.mUserStates.get( + mA11yms.getCurrentUserIdLocked()); when(mMockServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo); mMockResolveInfo.serviceInfo = mock(ServiceInfo.class); mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class); when(mMockBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient); + mTestableContext.addMockService(COMPONENT_NAME, mMockBinder); mAccessibilityServiceConnection = new AccessibilityServiceConnection( - mUserState, - mMockContext, + userState, + mTestableContext, COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, @@ -172,7 +209,6 @@ public class AccessibilityManagerServiceTest { mMockA11yWindowManager, mMockActivityTaskManagerInternal); mAccessibilityServiceConnection.bindLocked(); - mAccessibilityServiceConnection.onServiceConnected(COMPONENT_NAME, mMockBinder); } @SmallTest @@ -220,7 +256,10 @@ public class AccessibilityManagerServiceTest { @Test public void testOnSystemActionsChanged() throws Exception { setupAccessibilityServiceConnection(); - mA11yms.notifySystemActionsChangedLocked(mUserState); + final AccessibilityUserState userState = mA11yms.mUserStates.get( + mA11yms.getCurrentUserIdLocked()); + + mA11yms.notifySystemActionsChangedLocked(userState); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); verify(mMockServiceClient).onSystemActionsChanged(); } @@ -228,24 +267,89 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test public void testOnMagnificationTransitionFailed_capabilitiesIsAll_fallBackToPreviousMode() { + final AccessibilityUserState userState = mA11yms.mUserStates.get( + mA11yms.getCurrentUserIdLocked()); + userState.setMagnificationCapabilitiesLocked(ACCESSIBILITY_MAGNIFICATION_MODE_ALL); + userState.setMagnificationModeLocked(Display.DEFAULT_DISPLAY, + ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN + ); + + mA11yms.onMagnificationTransitionEndedLocked(Display.DEFAULT_DISPLAY, false); + + Assert.assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, + userState.getMagnificationModeLocked(Display.DEFAULT_DISPLAY)); + } + + @SmallTest + @Test + public void testOnMagnificationTransitionSuccess_capabilitiesIsAll_inputFilterRefreshMode() { final AccessibilityUserState userState = mA11yms.mUserStates.get( mA11yms.getCurrentUserIdLocked()); userState.setMagnificationCapabilitiesLocked( - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL); - userState.setMagnificationModeLocked( - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); + ACCESSIBILITY_MAGNIFICATION_MODE_ALL); + userState.setMagnificationModeLocked(Display.DEFAULT_DISPLAY, + ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN + ); - mA11yms.onMagnificationTransitionEndedLocked(false); + mA11yms.onMagnificationTransitionEndedLocked(Display.DEFAULT_DISPLAY, true); - Assert.assertEquals(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, - userState.getMagnificationModeLocked()); + ArgumentCaptor displayCaptor = ArgumentCaptor.forClass(Display.class); + verify(mInputFilter, timeout(100)).refreshMagnificationMode(displayCaptor.capture()); + Assert.assertEquals(Display.DEFAULT_DISPLAY, displayCaptor.getValue().getDisplayId()); + } + + @SmallTest + @Test + public void testChangeMagnificationModeOnDefaultDisplay_capabilitiesIsAll_persistChangedMode() + throws Exception { + final AccessibilityUserState userState = mA11yms.mUserStates.get( + mA11yms.getCurrentUserIdLocked()); + userState.setMagnificationCapabilitiesLocked( + ACCESSIBILITY_MAGNIFICATION_MODE_ALL); + userState.setMagnificationModeLocked(Display.DEFAULT_DISPLAY, + ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN + ); + + mA11yms.changeMagnificationMode(Display.DEFAULT_DISPLAY, + ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + + TestUtils.waitUntil("magnification mode " + ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW + + " is not persisted in setting", 1, + () -> { + final int userMode = Settings.Secure.getIntForUser( + mTestableContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, + ACCESSIBILITY_MAGNIFICATION_MODE_NONE, + mA11yms.getCurrentUserIdLocked()); + return userMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; + }); + } + + @SmallTest + @Test + public void testChangeMagnificationModeOnTestDisplay_capabilitiesIsAll_transitMode() { + final AccessibilityUserState userState = mA11yms.mUserStates.get( + mA11yms.getCurrentUserIdLocked()); + userState.setMagnificationCapabilitiesLocked( + ACCESSIBILITY_MAGNIFICATION_MODE_ALL); + userState.setMagnificationModeLocked(TEST_DISPLAY, + ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN + ); + + mA11yms.changeMagnificationMode(TEST_DISPLAY, + ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + + verify(mMockMagnificationController).transitionMagnificationModeLocked(eq(TEST_DISPLAY), + eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW), ArgumentMatchers.isNotNull()); } @SmallTest @Test public void testOnClientChange_magnificationEnabledAndCapabilityAll_requestConnection() { - mUserState.mAccessibilityShortcutKeyTargets.add(MAGNIFICATION_CONTROLLER_NAME); - mUserState.setMagnificationCapabilitiesLocked( + final AccessibilityUserState userState = mA11yms.mUserStates.get( + mA11yms.getCurrentUserIdLocked()); + userState.mAccessibilityShortcutKeyTargets.add(MAGNIFICATION_CONTROLLER_NAME); + userState.setMagnificationCapabilitiesLocked( Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL); // Invokes client change to trigger onUserStateChanged. @@ -265,4 +369,11 @@ public class AccessibilityManagerServiceTest { verify(mMockWindowMagnificationMgr).requestConnection(true); } + + public static class FakeInputFilter extends AccessibilityInputFilter { + FakeInputFilter(Context context, + AccessibilityManagerService service) { + super(context, service); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java index 81ca92cc3c8cf..b9d94edc5981f 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java @@ -48,6 +48,7 @@ import android.provider.Settings; import android.test.mock.MockContentResolver; import android.testing.DexmakerShareClassLoaderRule; import android.util.ArraySet; +import android.view.Display; import androidx.test.InstrumentationRegistry; @@ -79,6 +80,8 @@ public class AccessibilityUserStateTest { private static final int USER_ID = 42; + private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY; + // Mock package-private class AccessibilityServiceConnection @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = new DexmakerShareClassLoaderRule(); @@ -143,7 +146,8 @@ public class AccessibilityUserStateTest { mUserState.setAutoclickEnabledLocked(true); mUserState.setUserNonInteractiveUiTimeoutLocked(30); mUserState.setUserInteractiveUiTimeoutLocked(30); - mUserState.setMagnificationModeLocked(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + mUserState.setMagnificationModeLocked(TEST_DISPLAY, + ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); mUserState.setFocusAppearanceLocked(20, Color.BLUE); mUserState.onSwitchToAnotherUserLocked(); @@ -165,7 +169,7 @@ public class AccessibilityUserStateTest { assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked()); assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked()); assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, - mUserState.getMagnificationModeLocked()); + mUserState.getMagnificationModeLocked(TEST_DISPLAY)); assertEquals(mFocusStrokeWidthDefaultValue, mUserState.getFocusStrokeWidthLocked()); assertEquals(mFocusColorDefaultValue, mUserState.getFocusColorLocked()); } @@ -360,12 +364,13 @@ public class AccessibilityUserStateTest { @Test public void setWindowMagnificationMode_returnExpectedMagnificationMode() { assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, - mUserState.getMagnificationModeLocked()); + mUserState.getMagnificationModeLocked(TEST_DISPLAY)); - mUserState.setMagnificationModeLocked(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + mUserState.setMagnificationModeLocked(TEST_DISPLAY, + ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, - mUserState.getMagnificationModeLocked()); + mUserState.getMagnificationModeLocked(TEST_DISPLAY)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index 69061c14c70e0..8a521d8a74900 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -150,7 +150,7 @@ public class MagnificationControllerTest { MODE_WINDOW, mTransitionCallBack); - verify(mTransitionCallBack).onResult(true); + verify(mTransitionCallBack).onResult(TEST_DISPLAY, true); verify(mScreenMagnificationController, never()).reset(anyInt(), any(MagnificationAnimationCallback.class)); verify(mMockConnection.getConnection(), never()).enableWindowMagnification(anyInt(), @@ -171,7 +171,7 @@ public class MagnificationControllerTest { mCallbackArgumentCaptor.capture()); mCallbackArgumentCaptor.getValue().onResult(true); mMockConnection.invokeCallbacks(); - verify(mTransitionCallBack).onResult(true); + verify(mTransitionCallBack).onResult(TEST_DISPLAY, true); assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0); assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0); } @@ -189,7 +189,7 @@ public class MagnificationControllerTest { mTransitionCallBack); mMockConnection.invokeCallbacks(); - verify(mTransitionCallBack).onResult(true); + verify(mTransitionCallBack).onResult(TEST_DISPLAY, true); assertEquals(MAGNIFIED_CENTER_X, mWindowMagnificationManager.getCenterX(TEST_DISPLAY), 0); assertEquals(MAGNIFIED_CENTER_Y, mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 0); } @@ -225,7 +225,7 @@ public class MagnificationControllerTest { verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, true, MAGNIFICATION_GESTURE_HANDLER_ID); - verify(mTransitionCallBack).onResult(true); + verify(mTransitionCallBack).onResult(TEST_DISPLAY, true); } @Test @@ -245,7 +245,7 @@ public class MagnificationControllerTest { verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, magnificationBounds.exactCenterX(), magnificationBounds.exactCenterY(), true, MAGNIFICATION_GESTURE_HANDLER_ID); - verify(mTransitionCallBack).onResult(true); + verify(mTransitionCallBack).onResult(TEST_DISPLAY, true); } @Test @@ -265,7 +265,7 @@ public class MagnificationControllerTest { 0); assertEquals(MAGNIFIED_CENTER_Y, mScreenMagnificationController.getCenterY(TEST_DISPLAY), 0); - verify(mTransitionCallBack).onResult(true); + verify(mTransitionCallBack).onResult(TEST_DISPLAY, true); } @Test @@ -285,7 +285,7 @@ public class MagnificationControllerTest { verify(mScreenMagnificationController, never()).setScaleAndCenter(TEST_DISPLAY, DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y, true, MAGNIFICATION_GESTURE_HANDLER_ID); - verify(mTransitionCallBack).onResult(false); + verify(mTransitionCallBack).onResult(TEST_DISPLAY, false); } @Test @@ -595,6 +595,13 @@ public class MagnificationControllerTest { verify(mScaleProvider).onUserRemoved(SECOND_USER_ID); } + @Test + public void onChangeMagnificationMode_delegateToService() { + mMagnificationController.onChangeMagnificationMode(TEST_DISPLAY, MODE_WINDOW); + + verify(mService).changeMagnificationMode(TEST_DISPLAY, MODE_WINDOW); + } + private void setMagnificationEnabled(int mode) throws RemoteException { setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y); }