From 91443b10f76957eddfba32f6566678e0c65d21bd Mon Sep 17 00:00:00 2001 From: mincheli Date: Wed, 15 Dec 2021 00:50:31 +0800 Subject: [PATCH] Adds new magnificationController API: getCurrentMagnificationRegion, resetCurrentMagnification See go/b200769372 - the legacy public methods The legacy magnification controller methods of a11y service, getMagnificationRegion(), reset(), will keep the behavior before that control only full-screen magnification. To make the a11y service able to control the actiavted magnifier on the display, the service should use the new public APIs, 1. getCurrentMagnificationRegion(), 2. resetCurrentMagnification(), instead of the legacy APIs. Bug: 210069654 Test: atest AccessibilityMagnificationTest, atest MagnificationProcessorTest, atest AbstractAccessibilityServiceConnectionTest, Change-Id: I76bfee43cbb76a9aeec33574a8db18430903e08c --- core/api/current.txt | 2 + .../AccessibilityService.java | 80 +++++++++++++++++++ .../IAccessibilityServiceConnection.aidl | 4 + .../AccessibilityServiceConnectionImpl.java | 8 ++ ...bstractAccessibilityServiceConnection.java | 49 ++++++++++++ .../magnification/MagnificationProcessor.java | 34 +++++++- ...actAccessibilityServiceConnectionTest.java | 14 ++++ .../MagnificationProcessorTest.java | 36 ++++++++- 8 files changed, 223 insertions(+), 4 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 98d3a91c99446..2942428c0d012 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -3125,11 +3125,13 @@ package android.accessibilityservice { method public void addListener(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, @Nullable android.os.Handler); method public float getCenterX(); method public float getCenterY(); + method @NonNull public android.graphics.Region getCurrentMagnificationRegion(); method @Nullable public android.accessibilityservice.MagnificationConfig getMagnificationConfig(); method @NonNull public android.graphics.Region getMagnificationRegion(); method public float getScale(); method public boolean removeListener(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener); method public boolean reset(boolean); + method public boolean resetCurrentMagnification(boolean); method public boolean setCenter(float, float, boolean); method public boolean setMagnificationConfig(@NonNull android.accessibilityservice.MagnificationConfig, boolean); method public boolean setScale(float, boolean); diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 3d38551642742..d62797b42bd80 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -1503,6 +1503,12 @@ public abstract class AccessibilityService extends Service { * {@link AccessibilityService#onServiceConnected()} has not yet been * called) or the service has been disconnected, this method will * return an empty region. + *

+ *

+ * Note: This legacy API gets the magnification region of full-screen + * magnification. To get the magnification region of the current controlling magnifier, + * use {@link #getCurrentMagnificationRegion()} instead. + *

* * @return the region of the screen currently active for magnification, or an empty region * if magnification is not active. @@ -1523,6 +1529,45 @@ public abstract class AccessibilityService extends Service { return Region.obtain(); } + /** + * Returns the region of the screen currently active for magnification if the + * controlling magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN}. + * Returns the region of screen projected on the magnification window if the + * controlling magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW}. + * + *

+ * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN}, + * the returned region will be empty if the magnification is + * not active. And the magnification is active if magnification gestures are enabled + * or if a service is running that can control magnification. + *

+ * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW}, + * the returned region will be empty if the magnification is not activated. + *

+ * Note: If the service is not yet connected (e.g. + * {@link AccessibilityService#onServiceConnected()} has not yet been + * called) or the service has been disconnected, this method will + * return an empty region. + *

+ * + * @return the magnification region of the currently controlling magnification + */ + @NonNull + public Region getCurrentMagnificationRegion() { + final IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance(mService).getConnection( + mService.mConnectionId); + if (connection != null) { + try { + return connection.getCurrentMagnificationRegion(mDisplayId); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Failed to obtain the current magnified region", re); + re.rethrowFromSystemServer(); + } + } + return Region.obtain(); + } + /** * Resets magnification scale and center to their default (e.g. no * magnification) values. @@ -1531,6 +1576,11 @@ public abstract class AccessibilityService extends Service { * {@link AccessibilityService#onServiceConnected()} has not yet been * called) or the service has been disconnected, this method will have * no effect and return {@code false}. + *

+ * Note: This legacy API reset full-screen magnification. + * To reset the current controlling magnifier, use + * {@link #resetCurrentMagnification(boolean)} ()} instead. + *

* * @param animate {@code true} to animate from the current scale and * center or {@code false} to reset the scale and center @@ -1552,6 +1602,36 @@ public abstract class AccessibilityService extends Service { return false; } + /** + * Resets magnification scale and center of the controlling magnification + * to their default (e.g. no magnification) values. + *

+ * Note: If the service is not yet connected (e.g. + * {@link AccessibilityService#onServiceConnected()} has not yet been + * called) or the service has been disconnected, this method will have + * no effect and return {@code false}. + *

+ * + * @param animate {@code true} to animate from the current scale and + * center or {@code false} to reset the scale and center + * immediately + * @return {@code true} on success, {@code false} on failure + */ + public boolean resetCurrentMagnification(boolean animate) { + final IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance(mService).getConnection( + mService.mConnectionId); + if (connection != null) { + try { + return connection.resetCurrentMagnification(mDisplayId, animate); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Failed to reset", re); + re.rethrowFromSystemServer(); + } + } + return false; + } + /** * Sets the {@link MagnificationConfig}. The service controls the magnification by * setting the config. diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 93e69145f90c3..41a524f262c44 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -88,8 +88,12 @@ interface IAccessibilityServiceConnection { Region getMagnificationRegion(int displayId); + Region getCurrentMagnificationRegion(int displayId); + boolean resetMagnification(int displayId, boolean animate); + boolean resetCurrentMagnification(int displayId, boolean animate); + boolean setMagnificationConfig(int displayId, in MagnificationConfig config, boolean animate); void setMagnificationCallbackEnabled(int displayId, boolean enabled); diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index ad1f298e57a17..da19ff019686f 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -120,10 +120,18 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon return null; } + public Region getCurrentMagnificationRegion(int displayId) { + return null; + } + public boolean resetMagnification(int displayId, boolean animate) { return false; } + public boolean resetCurrentMagnification(int displayId, boolean animate) { + return false; + } + public boolean setMagnificationConfig(int displayId, @NonNull MagnificationConfig config, boolean animate) { return false; diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index f050b6622a5d4..1b1c96d437e9d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -1036,6 +1036,30 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } } + + @Override + public Region getCurrentMagnificationRegion(int displayId) { + if (svcConnTracingEnabled()) { + logTraceSvcConn("getCurrentMagnificationRegion", "displayId=" + displayId); + } + synchronized (mLock) { + final Region region = Region.obtain(); + if (!hasRightsToCurrentUserLocked()) { + return region; + } + MagnificationProcessor magnificationProcessor = + mSystemSupport.getMagnificationProcessor(); + final long identity = Binder.clearCallingIdentity(); + try { + magnificationProcessor.getCurrentMagnificationRegion(displayId, + region, mSecurityPolicy.canControlMagnification(this)); + return region; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } + @Override public float getMagnificationCenterX(int displayId) { if (svcConnTracingEnabled()) { @@ -1102,6 +1126,31 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } } + @Override + public boolean resetCurrentMagnification(int displayId, boolean animate) { + if (svcConnTracingEnabled()) { + logTraceSvcConn("resetCurrentMagnification", + "displayId=" + displayId + ";animate=" + animate); + } + synchronized (mLock) { + if (!hasRightsToCurrentUserLocked()) { + return false; + } + if (!mSecurityPolicy.canControlMagnification(this)) { + return false; + } + } + final long identity = Binder.clearCallingIdentity(); + try { + MagnificationProcessor magnificationProcessor = + mSystemSupport.getMagnificationProcessor(); + return (magnificationProcessor.resetCurrentMagnification(displayId, animate) + || !magnificationProcessor.isMagnifying(displayId)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override public boolean setMagnificationConfig(int displayId, @NonNull MagnificationConfig config, boolean animate) { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java index 40f77b04d5de4..8f15d5cf3e3d3 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java @@ -202,6 +202,36 @@ public class MagnificationProcessor { } } + /** + * Returns the region of the screen currently active for magnification if the + * controlling magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN}. + * Returns the region of screen projected on the magnification window if the controlling + * magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW}. + *

+ * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN}, + * the returned region will be empty if the magnification is + * not active. And the magnification is active if magnification gestures are enabled + * or if a service is running that can control magnification. + *

+ * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW}, + * the returned region will be empty if the magnification is not activated. + *

+ * + * @param displayId The logical display id + * @param outRegion the region to populate + * @param canControlMagnification Whether the service can control magnification + */ + public void getCurrentMagnificationRegion(int displayId, @NonNull Region outRegion, + boolean canControlMagnification) { + int currentMode = getControllingMode(displayId); + if (currentMode == MAGNIFICATION_MODE_FULLSCREEN) { + getFullscreenMagnificationRegion(displayId, outRegion, canControlMagnification); + } else if (currentMode == MAGNIFICATION_MODE_WINDOW) { + mController.getWindowMagnificationMgr().getMagnificationSourceBounds(displayId, + outRegion); + } + } + /** * Returns the magnification bounds of full-screen magnification on the given display. * @@ -224,8 +254,8 @@ public class MagnificationProcessor { } /** - * Resets the current magnification on the given display. The reset mode could be - * full-screen or window if it is activated. + * Resets the controlling magnifier on the given display. + * For resetting window magnifier, it disables the magnifier by setting the scale to 1. * * @param displayId The logical display id. * @param animate {@code true} to animate the transition, {@code false} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index d18030f10603d..97ebdd4ad7fac 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -594,6 +594,20 @@ public class AbstractAccessibilityServiceConnectionTest { assertThat(result.isEmpty(), is(true)); } + @Test + public void getCurrentMagnificationRegion_returnRegion() { + final int displayId = 1; + final Region region = new Region(10, 20, 100, 200); + doAnswer((invocation) -> { + ((Region) invocation.getArguments()[1]).set(region); + return null; + }).when(mMockMagnificationProcessor).getCurrentMagnificationRegion(eq(displayId), any(), + anyBoolean()); + + final Region result = mServiceConnection.getCurrentMagnificationRegion(displayId); + assertEquals(result, region); + } + @Test public void getMagnificationCenterX_serviceNotBelongCurrentUser_returnZero() { final int displayId = 1; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java index 99d6c2af6f75c..c4040b405d19a 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java @@ -183,6 +183,38 @@ public class MagnificationProcessorTest { verify(mMockFullScreenMagnificationController).unregister(TEST_DISPLAY); } + @Test + public void getCurrentMagnificationRegion_windowModeActivated_returnRegion() { + final Region region = new Region(10, 20, 100, 200); + setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW); + doAnswer((invocation) -> { + ((Region) invocation.getArguments()[1]).set(region); + return null; + }).when(mMockWindowMagnificationManager).getMagnificationSourceBounds(eq(TEST_DISPLAY), + any()); + + final Region result = new Region(); + mMagnificationProcessor.getCurrentMagnificationRegion(TEST_DISPLAY, + result, /* canControlMagnification= */true); + assertEquals(region, result); + } + + @Test + public void getCurrentMagnificationRegion_fullscreenModeActivated_returnRegion() { + final Region region = new Region(10, 20, 100, 200); + setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN); + doAnswer((invocation) -> { + ((Region) invocation.getArguments()[1]).set(region); + return null; + }).when(mMockFullScreenMagnificationController).getMagnificationRegion(eq(TEST_DISPLAY), + any()); + + final Region result = new Region(); + mMagnificationProcessor.getCurrentMagnificationRegion(TEST_DISPLAY, + result, /* canControlMagnification= */true); + assertEquals(region, result); + } + @Test public void getMagnificationCenterX_fullscreenModeNotRegistered_shouldRegisterThenUnregister() { final MagnificationConfig config = new MagnificationConfig.Builder() @@ -222,7 +254,7 @@ public class MagnificationProcessorTest { } @Test - public void reset_fullscreenMagnificationActivated() { + public void resetFullscreenMagnification_fullscreenMagnificationActivated() { setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN); mMagnificationProcessor.resetFullscreenMagnification(TEST_DISPLAY, /* animate= */false); @@ -231,7 +263,7 @@ public class MagnificationProcessorTest { } @Test - public void reset_windowMagnificationActivated() { + public void resetCurrentMagnification_windowMagnificationActivated() { setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW); mMagnificationProcessor.resetCurrentMagnification(TEST_DISPLAY, /* animate= */false);