From 70439244ba1abc4f32357d2ee8ff38f73158fe37 Mon Sep 17 00:00:00 2001 From: Phil Weaver Date: Thu, 10 Mar 2016 15:15:49 -0800 Subject: [PATCH] Fix several accessibility magnification issues. Clarifying region used for magnification as "magnificationRegion", both in the public API and in the code. There's been significant confusion about what "magnfifiedRegion" means. Removing "availableRegion" from everywhere except where it's required, as that region was identical to magnified/magnification region. Trying to shut down magnification was a complex situation where animations in progress and new magnification requests were tricky to handle correctly. It was not possible to guarantee that the magnification callbacks were unregistered consistently. There were at least two situations that led to phone restarts: 1. If a triple tap was detected between unregistering the callbacks and shutting down the input filter. In this case the magnification request would go through. 2. If an animation had just started when magnification was turned off, so the current magnification was 1.0 but the animator was about to change it. In this case the callbacks would be unregistered, and then the animator would start changing the magnification. This change makes registering and unregistering magnification atomic. It also makes MagnificationController stick around indefinitely once it is created, registering and unregistering as needed to support magnification gestures and services that control magnification. Services that merely query the status of magnification no longer register for callbacks. One part of shutting down is turning off the animation and guaranteeing that it won't try to make further changes. Adding a flag to SpecAnimationBridge and a lock in that class so we can guarantee that nothing happens when we aren't registered for magnification callbacks. Also reconfiguring all accessibility options when a service stops to make sure that only the features required by the current configuration are enabled. Bug: 27497138 Bug: 27821103 Change-Id: If697cbd34b117d82c8eee1ba7d0254089ee4241d --- api/current.txt | 2 +- api/system-current.txt | 2 +- api/test-current.txt | 2 +- .../AccessibilityService.java | 24 +- .../android/view/WindowManagerInternal.java | 17 +- .../AccessibilityManagerService.java | 54 ++- .../MagnificationController.java | 317 +++++++++++------- .../MagnificationGestureHandler.java | 28 +- .../server/wm/AccessibilityController.java | 102 +++--- .../server/wm/WindowManagerService.java | 6 +- 10 files changed, 313 insertions(+), 241 deletions(-) diff --git a/api/current.txt b/api/current.txt index 5df8750fcb7c4..ea9deb1cdb41e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2699,7 +2699,7 @@ package android.accessibilityservice { method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, android.os.Handler); method public float getCenterX(); method public float getCenterY(); - method public android.graphics.Region getMagnifiedRegion(); + method public android.graphics.Region getMagnificationRegion(); method public float getScale(); method public boolean removeListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener); method public boolean reset(boolean); diff --git a/api/system-current.txt b/api/system-current.txt index acb699d26a050..af95d33b54664 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -2803,7 +2803,7 @@ package android.accessibilityservice { method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, android.os.Handler); method public float getCenterX(); method public float getCenterY(); - method public android.graphics.Region getMagnifiedRegion(); + method public android.graphics.Region getMagnificationRegion(); method public float getScale(); method public boolean removeListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener); method public boolean reset(boolean); diff --git a/api/test-current.txt b/api/test-current.txt index e4b11bd2d4b5e..f007d00ca7b0a 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -2699,7 +2699,7 @@ package android.accessibilityservice { method public void addListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener, android.os.Handler); method public float getCenterX(); method public float getCenterY(); - method public android.graphics.Region getMagnifiedRegion(); + method public android.graphics.Region getMagnificationRegion(); method public float getScale(); method public boolean removeListener(android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener); method public boolean reset(boolean); diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 56728ad881e13..8b277b2a12ea4 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -925,18 +925,25 @@ public abstract class AccessibilityService extends Service { } /** - * Returns the region of the screen currently being magnified. If - * magnification is not enabled, the returned region will be empty. + * Returns the region of the screen currently active for magnification. Changes to + * magnification scale and center only affect this portion of the screen. The rest of the + * screen, for example input methods, cannot be magnified. This region is relative to the + * unscaled screen and is independent of the scale and center point. + *

+ * The returned region will be empty if magnification is not active. Magnification is active + * if magnification gestures are enabled or if a service is running that can control + * magnification. *

* 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 screen-relative bounds of the magnified region + * @return the region of the screen currently active for magnification, or an empty region + * if magnification is not active. */ @NonNull - public Region getMagnifiedRegion() { + public Region getMagnificationRegion() { final IAccessibilityServiceConnection connection = AccessibilityInteractionClient.getInstance().getConnection( mService.mConnectionId); @@ -1049,11 +1056,12 @@ public abstract class AccessibilityService extends Service { * Called when the magnified region, scale, or center changes. * * @param controller the magnification controller - * @param region the new magnified region, may be empty if - * magnification is not enabled (e.g. scale is 1) + * @param region the magnification region * @param scale the new scale - * @param centerX the new X coordinate around which magnification is focused - * @param centerY the new Y coordinate around which magnification is focused + * @param centerX the new X coordinate, in unscaled coordinates, around which + * magnification is focused + * @param centerY the new Y coordinate, in unscaled coordinates, around which + * magnification is focused */ void onMagnificationChanged(@NonNull MagnificationController controller, @NonNull Region region, float scale, float centerX, float centerY); diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java index 3ad730b085904..4b188c4e35c1b 100644 --- a/core/java/android/view/WindowManagerInternal.java +++ b/core/java/android/view/WindowManagerInternal.java @@ -54,13 +54,12 @@ public abstract class WindowManagerInternal { public interface MagnificationCallbacks { /** - * Called when the bounds of the screen content that is magnified changed. - * Note that not the entire screen is magnified. + * Called when the region where magnification operates changes. Note that this isn't the + * entire screen. For example, IMEs are not magnified. * - * @param magnifiedBounds the currently magnified region - * @param availableBounds the region available for magnification + * @param magnificationRegion the current magnification region */ - public void onMagnifiedBoundsChanged(Region magnifiedBounds, Region availableBounds); + public void onMagnificationRegionChanged(Region magnificationRegion); /** * Called when an application requests a rectangle on the screen to allow @@ -158,13 +157,11 @@ public abstract class WindowManagerInternal { public abstract void setMagnificationSpec(MagnificationSpec spec); /** - * Obtains the magnified and available regions. + * Obtains the magnification regions. * - * @param outMagnified the currently magnified region - * @param outAvailable the region available for magnification + * @param magnificationRegion the current magnification region */ - public abstract void getMagnificationRegions(@NonNull Region outMagnified, - @NonNull Region outAvailable); + public abstract void getMagnificationRegion(@NonNull Region magnificationRegion); /** * Gets the magnification and translation applied to a window given its token. diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index d900b378c2c89..0311082074d42 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -153,7 +153,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private static final int WINDOW_ID_UNKNOWN = -1; - private static int sIdCounter = 0; + // Each service has an ID. Also provide one for magnification gesture handling + public static final int MAGNIFICATION_GESTURE_HANDLER_ID = 0; + + private static int sIdCounter = MAGNIFICATION_GESTURE_HANDLER_ID + 1; private static int sNextWindowId; @@ -185,8 +188,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private MagnificationController mMagnificationController; - private boolean mUnregisterMagnificationOnReset; - private InteractionBridge mInteractionBridge; private AlertDialog mEnableTouchExplorationDialog; @@ -786,11 +787,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { float scale, float centerX, float centerY) { synchronized (mLock) { notifyMagnificationChangedLocked(region, scale, centerX, centerY); - - if (mUnregisterMagnificationOnReset && scale == 1.0f) { - mUnregisterMagnificationOnReset = false; - mMagnificationController.unregister(); - } } } @@ -1738,25 +1734,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } private void updateMagnificationLocked(UserState userState) { - final int userId = userState.mUserId; - if (userId == mCurrentUserId && mMagnificationController != null) { - if (userState.mIsDisplayMagnificationEnabled || - userHasMagnificationServicesLocked(userState)) { - mMagnificationController.setUserId(userState.mUserId); - } else { - // If the user no longer has any magnification-controlling - // services and is not using magnification gestures, then - // reset the state to normal. - if (mMagnificationController.resetIfNeeded(true)) { - // Animations are still running, so wait until we receive a - // callback verifying that we've reset magnification. - mUnregisterMagnificationOnReset = true; - } else { - mUnregisterMagnificationOnReset = false; - mMagnificationController.unregister(); - mMagnificationController = null; - } - } + if (userState.mUserId != mCurrentUserId) { + return; + } + + if (userState.mIsDisplayMagnificationEnabled || + userHasMagnificationServicesLocked(userState)) { + // Initialize the magnification controller if necessary + getMagnificationController(); + mMagnificationController.register(); + } else if (mMagnificationController != null) { + mMagnificationController.unregister(); } } @@ -2154,7 +2142,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { synchronized (mLock) { if (mMagnificationController == null) { mMagnificationController = new MagnificationController(mContext, this, mLock); - mMagnificationController.register(); mMagnificationController.setUserId(mCurrentUserId); } return mMagnificationController; @@ -2883,7 +2870,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final long identity = Binder.clearCallingIdentity(); try { final Region region = Region.obtain(); - getMagnificationController().getMagnifiedRegion(region); + getMagnificationController().getMagnificationRegion(region); return region; } finally { Binder.restoreCallingIdentity(identity); @@ -2954,7 +2941,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final long identity = Binder.clearCallingIdentity(); try { return getMagnificationController().setScaleAndCenter( - scale, centerX, centerY, animate); + scale, centerX, centerY, animate, mId); } finally { Binder.restoreCallingIdentity(identity); } @@ -3087,10 +3074,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mInstalledServices.remove(mAccessibilityServiceInfo); userState.mEnabledServices.remove(mComponentName); userState.destroyUiAutomationService(); - if (readConfigurationForUserStateLocked(userState)) { - onUserStateChangedLocked(userState); - } } + if (mId == getMagnificationController().getIdOfLastServiceToMagnify()) { + getMagnificationController().resetIfNeeded(true); + } + onUserStateChangedLocked(userState); } } diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java index f1b3722eed8ee..027b6e2c83e25 100644 --- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java @@ -17,6 +17,7 @@ package com.android.server.accessibility; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.os.SomeArgs; import com.android.server.LocalServices; @@ -60,6 +61,8 @@ class MagnificationController { private static final int DEFAULT_SCREEN_MAGNIFICATION_AUTO_UPDATE = 1; + private static final int INVALID_ID = -1; + private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f; private static final float MIN_SCALE = 1.0f; @@ -80,9 +83,8 @@ class MagnificationController { */ private final MagnificationSpec mCurrentMagnificationSpec = MagnificationSpec.obtain(); - private final Region mMagnifiedRegion = Region.obtain(); - private final Region mAvailableRegion = Region.obtain(); - private final Rect mMagnifiedBounds = new Rect(); + private final Region mMagnificationRegion = Region.obtain(); + private final Rect mMagnificationBounds = new Rect(); private final Rect mTempRect = new Rect(); private final Rect mTempRect1 = new Rect(); @@ -97,35 +99,67 @@ class MagnificationController { private int mUserId; + private int mIdOfLastServiceToMagnify = INVALID_ID; + + // Flag indicating that we are registered with window manager. + private boolean mRegistered; + + private boolean mUnregisterPending; + public MagnificationController(Context context, AccessibilityManagerService ams, Object lock) { mAms = ams; mContentResolver = context.getContentResolver(); mScreenStateObserver = new ScreenStateObserver(context, this); mWindowStateObserver = new WindowStateObserver(context, this); - mSpecAnimationBridge = new SpecAnimationBridge(context); mLock = lock; + mSpecAnimationBridge = new SpecAnimationBridge(context, mLock); } /** - * Registers magnification-related observers. + * Start tracking the magnification region for services that control magnification and the + * magnification gesture handler. + * + * This tracking imposes a cost on the system, so we avoid tracking this data + * unless it's required. */ public void register() { - mScreenStateObserver.register(); - mWindowStateObserver.register(); - - // Obtain initial state. - mWindowStateObserver.getRegions(mMagnifiedRegion, mAvailableRegion); - mMagnifiedRegion.getBounds(mMagnifiedBounds); + synchronized (mLock) { + if (!mRegistered) { + mScreenStateObserver.register(); + mWindowStateObserver.register(); + mSpecAnimationBridge.setEnabled(true); + // Obtain initial state. + mWindowStateObserver.getMagnificationRegion(mMagnificationRegion); + mMagnificationRegion.getBounds(mMagnificationBounds); + mRegistered = true; + } + } } /** - * Unregisters magnification-related observers. + * Stop requiring tracking the magnification region. We may remain registered while we + * reset magnification. */ public void unregister() { - mSpecAnimationBridge.cancel(); + synchronized (mLock) { + if (!isMagnifying()) { + unregisterInternalLocked(); + } else { + mUnregisterPending = true; + resetLocked(true); + } + } + } - mScreenStateObserver.unregister(); - mWindowStateObserver.unregister(); + private void unregisterInternalLocked() { + if (mRegistered) { + mSpecAnimationBridge.setEnabled(false); + mScreenStateObserver.unregister(); + mWindowStateObserver.unregister(); + mMagnificationRegion.setEmpty(); + mRegistered = false; + } + mUnregisterPending = false; } /** @@ -137,24 +171,22 @@ class MagnificationController { } /** - * Sets the magnified and available regions. + * Update our copy of the current magnification region * * @param magnified the magnified region - * @param available the region available for magnification * @param updateSpec {@code true} to update the scale and center based on * the region bounds, {@code false} to leave them as-is */ - private void setMagnifiedRegion(Region magnified, Region available, boolean updateSpec) { + private void onMagnificationRegionChanged(Region magnified, boolean updateSpec) { synchronized (mLock) { boolean magnificationChanged = false; boolean boundsChanged = false; - if (!mMagnifiedRegion.equals(magnified)) { - mMagnifiedRegion.set(magnified); - mMagnifiedRegion.getBounds(mMagnifiedBounds); + if (!mMagnificationRegion.equals(magnified)) { + mMagnificationRegion.set(magnified); + mMagnificationRegion.getBounds(mMagnificationBounds); boundsChanged = true; } - mAvailableRegion.set(available); if (updateSpec) { final MagnificationSpec sentSpec = mSpecAnimationBridge.mSentMagnificationSpec; final float scale = sentSpec.scale; @@ -162,12 +194,12 @@ class MagnificationController { final float offsetY = sentSpec.offsetY; // Compute the new center and update spec as needed. - final float centerX = (mMagnifiedBounds.width() / 2.0f - + mMagnifiedBounds.left - offsetX) / scale; - final float centerY = (mMagnifiedBounds.height() / 2.0f - + mMagnifiedBounds.top - offsetY) / scale; + final float centerX = (mMagnificationBounds.width() / 2.0f + + mMagnificationBounds.left - offsetX) / scale; + final float centerY = (mMagnificationBounds.height() / 2.0f + + mMagnificationBounds.top - offsetY) / scale; magnificationChanged = setScaleAndCenterLocked( - scale, centerX, centerY, false); + scale, centerX, centerY, false, INVALID_ID); } // If magnification changed we already notified for the change. @@ -178,7 +210,7 @@ class MagnificationController { } /** - * Returns whether the magnified region contains the specified + * Returns whether the magnification region contains the specified * screen-relative coordinates. * * @param x the screen-relative X coordinate to check @@ -186,51 +218,36 @@ class MagnificationController { * @return {@code true} if the coordinate is contained within the * magnified region, or {@code false} otherwise */ - public boolean magnifiedRegionContains(float x, float y) { + public boolean magnificationRegionContains(float x, float y) { synchronized (mLock) { - return mMagnifiedRegion.contains((int) x, (int) y); - } - } - - /** - * Returns whether the region available for magnification contains the - * specified screen-relative coordinates. - * - * @param x the screen-relative X coordinate to check - * @param y the screen-relative Y coordinate to check - * @return {@code true} if the coordinate is contained within the - * region available for magnification, or {@code false} otherwise - */ - private boolean availableRegionContains(float x, float y) { - synchronized (mLock) { - return mAvailableRegion.contains((int) x, (int) y); + return mMagnificationRegion.contains((int) x, (int) y); } } /** * Populates the specified rect with the screen-relative bounds of the - * magnified region. If magnification is not enabled, the returned + * magnification region. If magnification is not enabled, the returned * bounds will be empty. * * @param outBounds rect to populate with the bounds of the magnified * region */ - public void getMagnifiedBounds(@NonNull Rect outBounds) { + public void getMagnificationBounds(@NonNull Rect outBounds) { synchronized (mLock) { - outBounds.set(mMagnifiedBounds); + outBounds.set(mMagnificationBounds); } } /** - * Populates the specified region with the screen-relative magnified + * Populates the specified region with the screen-relative magnification * region. If magnification is not enabled, then the returned region * will be empty. * * @param outRegion the region to populate */ - public void getMagnifiedRegion(@NonNull Region outRegion) { + public void getMagnificationRegion(@NonNull Region outRegion) { synchronized (mLock) { - outRegion.set(mMagnifiedRegion); + outRegion.set(mMagnificationRegion); } } @@ -263,8 +280,8 @@ class MagnificationController { */ public float getCenterX() { synchronized (mLock) { - return (mMagnifiedBounds.width() / 2.0f - + mMagnifiedBounds.left - getOffsetX()) / getScale(); + return (mMagnificationBounds.width() / 2.0f + + mMagnificationBounds.left - getOffsetX()) / getScale(); } } @@ -286,8 +303,8 @@ class MagnificationController { */ public float getCenterY() { synchronized (mLock) { - return (mMagnifiedBounds.height() / 2.0f - + mMagnifiedBounds.top - getOffsetY()) / getScale(); + return (mMagnificationBounds.height() / 2.0f + + mMagnificationBounds.top - getOffsetY()) / getScale(); } } @@ -335,17 +352,25 @@ class MagnificationController { */ public boolean reset(boolean animate) { synchronized (mLock) { - final MagnificationSpec spec = mCurrentMagnificationSpec; - final boolean changed = !spec.isNop(); - if (changed) { - spec.clear(); - onMagnificationChangedLocked(); - } - mSpecAnimationBridge.updateSentSpec(spec, animate); - return changed; + return resetLocked(animate); } } + private boolean resetLocked(boolean animate) { + if (!mRegistered) { + return false; + } + final MagnificationSpec spec = mCurrentMagnificationSpec; + final boolean changed = !spec.isNop(); + if (changed) { + spec.clear(); + onMagnificationChangedLocked(); + } + mIdOfLastServiceToMagnify = INVALID_ID; + mSpecAnimationBridge.updateSentSpec(spec, animate); + return changed; + } + /** * Scales the magnified region around the specified pivot point, * optionally animating the transition. If animation is disabled, the @@ -356,16 +381,20 @@ class MagnificationController { * @param pivotY the screen-relative Y coordinate around which to scale * @param animate {@code true} to animate the transition, {@code false} * to transition immediately + * @param id the ID of the service requesting the change * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ - public boolean setScale(float scale, float pivotX, float pivotY, boolean animate) { + public boolean setScale(float scale, float pivotX, float pivotY, boolean animate, int id) { synchronized (mLock) { + if (!mRegistered) { + return false; + } // Constrain scale immediately for use in the pivot calculations. scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); final Rect viewport = mTempRect; - mMagnifiedRegion.getBounds(viewport); + mMagnificationRegion.getBounds(viewport); final MagnificationSpec spec = mCurrentMagnificationSpec; final float oldScale = spec.scale; final float oldCenterX = (viewport.width() / 2.0f - spec.offsetX) / oldScale; @@ -376,7 +405,8 @@ class MagnificationController { final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale); final float centerX = normPivotX + offsetX; final float centerY = normPivotY + offsetY; - return setScaleAndCenterLocked(scale, centerX, centerY, animate); + mIdOfLastServiceToMagnify = id; + return setScaleAndCenterLocked(scale, centerX, centerY, animate, id); } } @@ -390,12 +420,16 @@ class MagnificationController { * center * @param animate {@code true} to animate the transition, {@code false} * to transition immediately + * @param id the ID of the service requesting the change * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ - public boolean setCenter(float centerX, float centerY, boolean animate) { + public boolean setCenter(float centerX, float centerY, boolean animate, int id) { synchronized (mLock) { - return setScaleAndCenterLocked(Float.NaN, centerX, centerY, animate); + if (!mRegistered) { + return false; + } + return setScaleAndCenterLocked(Float.NaN, centerX, centerY, animate, id); } } @@ -411,19 +445,27 @@ class MagnificationController { * center and scale, or {@link Float#NaN} to leave unchanged * @param animate {@code true} to animate the transition, {@code false} * to transition immediately + * @param id the ID of the service requesting the change * @return {@code true} if the magnification spec changed, {@code false} if * the spec did not change */ - public boolean setScaleAndCenter(float scale, float centerX, float centerY, boolean animate) { + public boolean setScaleAndCenter( + float scale, float centerX, float centerY, boolean animate, int id) { synchronized (mLock) { - return setScaleAndCenterLocked(scale, centerX, centerY, animate); + if (!mRegistered) { + return false; + } + return setScaleAndCenterLocked(scale, centerX, centerY, animate, id); } } private boolean setScaleAndCenterLocked(float scale, float centerX, float centerY, - boolean animate) { + boolean animate, int id) { final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY); mSpecAnimationBridge.updateSentSpec(mCurrentMagnificationSpec, animate); + if (isMagnifying() && (id != INVALID_ID)) { + mIdOfLastServiceToMagnify = id; + } return changed; } @@ -432,22 +474,42 @@ class MagnificationController { * * @param offsetX the amount in pixels to offset the X center * @param offsetY the amount in pixels to offset the Y center + * @param id the ID of the service requesting the change */ - public void offsetMagnifiedRegionCenter(float offsetX, float offsetY) { + public void offsetMagnifiedRegionCenter(float offsetX, float offsetY, int id) { synchronized (mLock) { + if (!mRegistered) { + return; + } + final MagnificationSpec currSpec = mCurrentMagnificationSpec; final float nonNormOffsetX = currSpec.offsetX - offsetX; currSpec.offsetX = MathUtils.constrain(nonNormOffsetX, getMinOffsetXLocked(), 0); final float nonNormOffsetY = currSpec.offsetY - offsetY; currSpec.offsetY = MathUtils.constrain(nonNormOffsetY, getMinOffsetYLocked(), 0); + if (id != INVALID_ID) { + mIdOfLastServiceToMagnify = id; + } mSpecAnimationBridge.updateSentSpec(currSpec, false); } } + /** + * Get the ID of the last service that changed the magnification spec. + * + * @return The id + */ + public int getIdOfLastServiceToMagnify() { + return mIdOfLastServiceToMagnify; + } + private void onMagnificationChangedLocked() { mAms.onMagnificationStateChanged(); - mAms.notifyMagnificationChanged(mMagnifiedRegion, + mAms.notifyMagnificationChanged(mMagnificationRegion, getScale(), getCenterX(), getCenterY()); + if (mUnregisterPending && !isMagnifying()) { + unregisterInternalLocked(); + } } /** @@ -503,8 +565,8 @@ class MagnificationController { scale = getScale(); } - // Ensure requested center is within the available region. - if (!availableRegionContains(centerX, centerY)) { + // Ensure requested center is within the magnification region. + if (!magnificationRegionContains(centerX, centerY)) { return false; } @@ -518,16 +580,16 @@ class MagnificationController { changed = true; } - final float nonNormOffsetX = mMagnifiedBounds.width() / 2.0f - + mMagnifiedBounds.left - centerX * scale; + final float nonNormOffsetX = mMagnificationBounds.width() / 2.0f + + mMagnificationBounds.left - centerX * scale; final float offsetX = MathUtils.constrain(nonNormOffsetX, getMinOffsetXLocked(), 0); if (Float.compare(currSpec.offsetX, offsetX) != 0) { currSpec.offsetX = offsetX; changed = true; } - final float nonNormOffsetY = mMagnifiedBounds.height() / 2.0f - + mMagnifiedBounds.top - centerY * scale; + final float nonNormOffsetY = mMagnificationBounds.height() / 2.0f + + mMagnificationBounds.top - centerY * scale; final float offsetY = MathUtils.constrain(nonNormOffsetY, getMinOffsetYLocked(), 0); if (Float.compare(currSpec.offsetY, offsetY) != 0) { currSpec.offsetY = offsetY; @@ -542,12 +604,12 @@ class MagnificationController { } private float getMinOffsetXLocked() { - final float viewportWidth = mMagnifiedBounds.width(); + final float viewportWidth = mMagnificationBounds.width(); return viewportWidth - viewportWidth * mCurrentMagnificationSpec.scale; } private float getMinOffsetYLocked() { - final float viewportHeight = mMagnifiedBounds.height(); + final float viewportHeight = mMagnificationBounds.height(); return viewportHeight - viewportHeight * mCurrentMagnificationSpec.scale; } @@ -595,7 +657,7 @@ class MagnificationController { final float scale = getSentScale(); final float offsetX = getSentOffsetX(); final float offsetY = getSentOffsetY(); - getMagnifiedBounds(outFrame); + getMagnificationBounds(outFrame); outFrame.offset((int) -offsetX, (int) -offsetY); outFrame.scale(1.0f / scale); } @@ -603,7 +665,7 @@ class MagnificationController { private void requestRectangleOnScreen(int left, int top, int right, int bottom) { synchronized (mLock) { final Rect magnifiedFrame = mTempRect; - getMagnifiedBounds(magnifiedFrame); + getMagnificationBounds(magnifiedFrame); if (!magnifiedFrame.intersects(left, top, right, bottom)) { return; } @@ -640,7 +702,7 @@ class MagnificationController { } final float scale = getScale(); - offsetMagnifiedRegionCenter(scrollX * scale, scrollY * scale); + offsetMagnifiedRegionCenter(scrollX * scale, scrollY * scale, INVALID_ID); } } @@ -656,7 +718,7 @@ class MagnificationController { /** * The magnification spec that was sent to the window manager. This should - * only be accessed and modified on the main (e.g. animation) thread. + * only be accessed with the lock held. */ private final MagnificationSpec mSentMagnificationSpec = MagnificationSpec.obtain(); @@ -667,8 +729,13 @@ class MagnificationController { private final ValueAnimator mTransformationAnimator; private final long mMainThreadId; + private final Object mLock; - private SpecAnimationBridge(Context context) { + @GuardedBy("mLock") + private boolean mEnabled = false; + + private SpecAnimationBridge(Context context, Object lock) { + mLock = lock; final Looper mainLooper = context.getMainLooper(); mMainThreadId = mainLooper.getThread().getId(); @@ -685,9 +752,19 @@ class MagnificationController { mTransformationAnimator.setInterpolator(new DecelerateInterpolator(2.5f)); } - public void cancel() { - if (mTransformationAnimator != null && mTransformationAnimator.isRunning()) { - mTransformationAnimator.cancel(); + /** + * Enabled means the bridge will accept input. When not enabled, the output of the animator + * will be ignored + */ + public void setEnabled(boolean enabled) { + synchronized (mLock) { + if (enabled != mEnabled) { + mEnabled = enabled; + if (!mEnabled) { + mSentMagnificationSpec.clear(); + mWindowManager.setMagnificationSpec(mSentMagnificationSpec); + } + } } } @@ -710,28 +787,32 @@ class MagnificationController { } // If the current and sent specs don't match, update the sent spec. - final boolean changed = !mSentMagnificationSpec.equals(spec); - if (changed) { - if (animate) { - animateMagnificationSpec(spec); - } else { - setMagnificationSpec(spec); + synchronized (mLock) { + final boolean changed = !mSentMagnificationSpec.equals(spec); + if (changed) { + if (animate) { + animateMagnificationSpecLocked(spec); + } else { + setMagnificationSpecLocked(spec); + } } } } - private void animateMagnificationSpec(MagnificationSpec toSpec) { + private void animateMagnificationSpecLocked(MagnificationSpec toSpec) { mTransformationAnimator.setObjectValues(mSentMagnificationSpec, toSpec); mTransformationAnimator.start(); } - private void setMagnificationSpec(MagnificationSpec spec) { - if (DEBUG_SET_MAGNIFICATION_SPEC) { - Slog.i(LOG_TAG, "Sending: " + spec); - } + private void setMagnificationSpecLocked(MagnificationSpec spec) { + if (mEnabled) { + if (DEBUG_SET_MAGNIFICATION_SPEC) { + Slog.i(LOG_TAG, "Sending: " + spec); + } - mSentMagnificationSpec.setTo(spec); - mWindowManager.setMagnificationSpec(spec); + mSentMagnificationSpec.setTo(spec); + mWindowManager.setMagnificationSpec(spec); + } } private class UpdateHandler extends Handler { @@ -759,12 +840,16 @@ class MagnificationController { @Override public MagnificationSpec get(SpecAnimationBridge object) { - return object.mSentMagnificationSpec; + synchronized (object.mLock) { + return object.mSentMagnificationSpec; + } } @Override public void set(SpecAnimationBridge object, MagnificationSpec value) { - object.setMagnificationSpec(value); + synchronized (object.mLock) { + object.setMagnificationSpecLocked(value); + } } } @@ -862,15 +947,14 @@ class MagnificationController { } @Override - public void onMagnifiedBoundsChanged(Region magnified, Region available) { + public void onMagnificationRegionChanged(Region magnificationRegion) { final SomeArgs args = SomeArgs.obtain(); - args.arg1 = Region.obtain(magnified); - args.arg2 = Region.obtain(available); + args.arg1 = Region.obtain(magnificationRegion); mHandler.obtainMessage(MESSAGE_ON_MAGNIFIED_BOUNDS_CHANGED, args).sendToTarget(); } - private void handleOnMagnifiedBoundsChanged(Region magnified, Region available) { - mController.setMagnifiedRegion(magnified, available, mSpecIsDirty); + private void handleOnMagnifiedBoundsChanged(Region magnificationRegion) { + mController.onMagnificationRegionChanged(magnificationRegion, mSpecIsDirty); mSpecIsDirty = false; } @@ -911,8 +995,15 @@ class MagnificationController { mController.resetIfNeeded(true); } - public void getRegions(@NonNull Region outMagnified, @NonNull Region outAvailable) { - mWindowManager.getMagnificationRegions(outMagnified, outAvailable); + /** + * This method is used to get the magnification region in the tiny time slice between + * registering the callbacks and handling the message. + * TODO: Elimiante this extra path, perhaps by processing the message immediately + * + * @param outMagnificationRegion + */ + public void getMagnificationRegion(@NonNull Region outMagnificationRegion) { + mWindowManager.getMagnificationRegion(outMagnificationRegion); } private class CallbackHandler extends Handler { @@ -926,10 +1017,8 @@ class MagnificationController { case MESSAGE_ON_MAGNIFIED_BOUNDS_CHANGED: { final SomeArgs args = (SomeArgs) message.obj; final Region magnifiedBounds = (Region) args.arg1; - final Region availableBounds = (Region) args.arg2; - handleOnMagnifiedBoundsChanged(magnifiedBounds, availableBounds); + handleOnMagnifiedBoundsChanged(magnifiedBounds); magnifiedBounds.recycle(); - availableBounds.recycle(); } break; case MESSAGE_ON_RECTANGLE_ON_SCREEN_REQUESTED: { final SomeArgs args = (SomeArgs) message.obj; diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java index 818ac814eb4e3..39bc8098f6a55 100644 --- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java @@ -237,7 +237,7 @@ class MagnificationGestureHandler implements EventStreamTransformation { final float eventX = event.getX(); final float eventY = event.getY(); if (mMagnificationController.isMagnifying() - && mMagnificationController.magnifiedRegionContains(eventX, eventY)) { + && mMagnificationController.magnificationRegionContains(eventX, eventY)) { final float scale = mMagnificationController.getScale(); final float scaledOffsetX = mMagnificationController.getOffsetX(); final float scaledOffsetY = mMagnificationController.getOffsetY(); @@ -381,7 +381,8 @@ class MagnificationGestureHandler implements EventStreamTransformation { Slog.i(LOG_TAG, "Panned content by scrollX: " + distanceX + " scrollY: " + distanceY); } - mMagnificationController.offsetMagnifiedRegionCenter(distanceX, distanceY); + mMagnificationController.offsetMagnifiedRegionCenter(distanceX, distanceY, + AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); return true; } @@ -421,7 +422,8 @@ class MagnificationGestureHandler implements EventStreamTransformation { final float pivotX = detector.getFocusX(); final float pivotY = detector.getFocusY(); - mMagnificationController.setScale(scale, pivotX, pivotY, false); + mMagnificationController.setScale(scale, pivotX, pivotY, false, + AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); return true; } @@ -469,14 +471,14 @@ class MagnificationGestureHandler implements EventStreamTransformation { } final float eventX = event.getX(); final float eventY = event.getY(); - if (mMagnificationController.magnifiedRegionContains(eventX, eventY)) { + if (mMagnificationController.magnificationRegionContains(eventX, eventY)) { if (mLastMoveOutsideMagnifiedRegion) { mLastMoveOutsideMagnifiedRegion = false; - mMagnificationController.setCenter(eventX, - eventY, true); + mMagnificationController.setCenter(eventX, eventY, true, + AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); } else { - mMagnificationController.setCenter(eventX, - eventY, false); + mMagnificationController.setCenter(eventX, eventY, false, + AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); } } else { mLastMoveOutsideMagnifiedRegion = true; @@ -571,7 +573,7 @@ class MagnificationGestureHandler implements EventStreamTransformation { switch (action) { case MotionEvent.ACTION_DOWN: { mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE); - if (!mMagnificationController.magnifiedRegionContains( + if (!mMagnificationController.magnificationRegionContains( event.getX(), event.getY())) { transitionToDelegatingStateAndClear(); return; @@ -616,7 +618,7 @@ class MagnificationGestureHandler implements EventStreamTransformation { return; } mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD); - if (!mMagnificationController.magnifiedRegionContains( + if (!mMagnificationController.magnificationRegionContains( event.getX(), event.getY())) { transitionToDelegatingStateAndClear(); return; @@ -726,7 +728,8 @@ class MagnificationGestureHandler implements EventStreamTransformation { if (!mMagnificationController.isMagnifying()) { final float targetScale = mMagnificationController.getPersistedScale(); final float scale = MathUtils.constrain(targetScale, MIN_SCALE, MAX_SCALE); - mMagnificationController.setScaleAndCenter(scale, up.getX(), up.getY(), true); + mMagnificationController.setScaleAndCenter(scale, up.getX(), up.getY(), true, + AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); } else { mMagnificationController.reset(true); } @@ -742,7 +745,8 @@ class MagnificationGestureHandler implements EventStreamTransformation { final float targetScale = mMagnificationController.getPersistedScale(); final float scale = MathUtils.constrain(targetScale, MIN_SCALE, MAX_SCALE); - mMagnificationController.setScaleAndCenter(scale, down.getX(), down.getY(), true); + mMagnificationController.setScaleAndCenter(scale, down.getX(), down.getY(), true, + AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); transitionToState(STATE_VIEWPORT_DRAGGING); } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 3ef077b74b621..101f56f49241f 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -124,9 +124,9 @@ final class AccessibilityController { } } - public void getMagnificationRegionsLocked(Region outMagnified, Region outAvailable) { + public void getMagnificationRegionLocked(Region outMagnificationRegion) { if (mDisplayMagnifier != null) { - mDisplayMagnifier.getMagnificationRegionsLocked(outMagnified, outAvailable); + mDisplayMagnifier.getMagnificationRegionLocked(outMagnificationRegion); } } @@ -400,8 +400,8 @@ final class AccessibilityController { return spec; } - public void getMagnificationRegionsLocked(Region outMagnified, Region outAvailable) { - mMagnifedViewport.getBoundsLocked(outMagnified, outAvailable); + public void getMagnificationRegionLocked(Region outMagnificationRegion) { + mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion); } public void destroyLocked() { @@ -424,10 +424,8 @@ final class AccessibilityController { private final Matrix mTempMatrix = new Matrix(); - private final Region mMagnifiedBounds = new Region(); - private final Region mAvailableBounds = new Region(); - private final Region mOldMagnifiedBounds = new Region(); - private final Region mOldAvailableBounds = new Region(); + private final Region mMagnificationRegion = new Region(); + private final Region mOldMagnificationRegion = new Region(); private final Path mCircularPath; @@ -463,10 +461,8 @@ final class AccessibilityController { recomputeBoundsLocked(); } - public void getBoundsLocked(@NonNull Region outMagnified, - @NonNull Region outAvailable) { - outMagnified.set(mMagnifiedBounds); - outAvailable.set(mAvailableBounds); + public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) { + outMagnificationRegion.set(mMagnificationRegion); } public void updateMagnificationSpecLocked(MagnificationSpec spec) { @@ -488,11 +484,12 @@ final class AccessibilityController { final int screenWidth = mTempPoint.x; final int screenHeight = mTempPoint.y; - mMagnifiedBounds.set(0, 0, 0, 0); - mAvailableBounds.set(0, 0, screenWidth, screenHeight); + mMagnificationRegion.set(0, 0, 0, 0); + final Region availableBounds = mTempRegion1; + availableBounds.set(0, 0, screenWidth, screenHeight); if (mCircularPath != null) { - mAvailableBounds.setPath(mCircularPath, mAvailableBounds); + availableBounds.setPath(mCircularPath, availableBounds); } Region nonMagnifiedBounds = mTempRegion4; @@ -526,21 +523,21 @@ final class AccessibilityController { (int) windowFrame.right, (int) windowFrame.bottom); // Only update new regions Region portionOfWindowAlreadyAccountedFor = mTempRegion3; - portionOfWindowAlreadyAccountedFor.set(mMagnifiedBounds); + portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion); portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION); windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE); if (mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) { - mMagnifiedBounds.op(windowBounds, Region.Op.UNION); - mMagnifiedBounds.op(mAvailableBounds, Region.Op.INTERSECT); + mMagnificationRegion.op(windowBounds, Region.Op.UNION); + mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT); } else { nonMagnifiedBounds.op(windowBounds, Region.Op.UNION); - mAvailableBounds.op(windowBounds, Region.Op.DIFFERENCE); + availableBounds.op(windowBounds, Region.Op.DIFFERENCE); } // Update accounted bounds Region accountedBounds = mTempRegion2; - accountedBounds.set(mMagnifiedBounds); + accountedBounds.set(mMagnificationRegion); accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION); accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT); @@ -556,43 +553,36 @@ final class AccessibilityController { visibleWindows.clear(); - mMagnifiedBounds.op(mDrawBorderInset, mDrawBorderInset, + mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset, screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset, Region.Op.INTERSECT); - final boolean magnifiedChanged = !mOldMagnifiedBounds.equals(mMagnifiedBounds); - final boolean availableChanged = !mOldAvailableBounds.equals(mAvailableBounds); - if (magnifiedChanged || availableChanged) { - if (magnifiedChanged) { - mWindow.setBounds(mMagnifiedBounds); - Rect dirtyRect = mTempRect1; - if (mFullRedrawNeeded) { - mFullRedrawNeeded = false; - dirtyRect.set(mDrawBorderInset, mDrawBorderInset, - screenWidth - mDrawBorderInset, - screenHeight - mDrawBorderInset); - mWindow.invalidate(dirtyRect); - } else { - Region dirtyRegion = mTempRegion3; - dirtyRegion.set(mMagnifiedBounds); - dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION); - dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT); - dirtyRegion.getBounds(dirtyRect); - mWindow.invalidate(dirtyRect); - } - - mOldMagnifiedBounds.set(mMagnifiedBounds); - } - - if (availableChanged) { - mOldAvailableBounds.set(mAvailableBounds); + final boolean magnifiedChanged = + !mOldMagnificationRegion.equals(mMagnificationRegion); + if (magnifiedChanged) { + mWindow.setBounds(mMagnificationRegion); + final Rect dirtyRect = mTempRect1; + if (mFullRedrawNeeded) { + mFullRedrawNeeded = false; + dirtyRect.set(mDrawBorderInset, mDrawBorderInset, + screenWidth - mDrawBorderInset, + screenHeight - mDrawBorderInset); + mWindow.invalidate(dirtyRect); + } else { + final Region dirtyRegion = mTempRegion3; + dirtyRegion.set(mMagnificationRegion); + dirtyRegion.op(mOldMagnificationRegion, Region.Op.UNION); + dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT); + dirtyRegion.getBounds(dirtyRect); + mWindow.invalidate(dirtyRect); } + mOldMagnificationRegion.set(mMagnificationRegion); final SomeArgs args = SomeArgs.obtain(); - args.arg1 = Region.obtain(mMagnifiedBounds); - args.arg2 = Region.obtain(mAvailableBounds); + args.arg1 = Region.obtain(mMagnificationRegion); mHandler.obtainMessage( - MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED, args).sendToTarget(); + MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args) + .sendToTarget(); } } @@ -616,14 +606,14 @@ final class AccessibilityController { public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) { if (shown) { mFullRedrawNeeded = true; - mOldMagnifiedBounds.set(0, 0, 0, 0); + mOldMagnificationRegion.set(0, 0, 0, 0); } mWindow.setShown(shown, animate); } public void getMagnifiedFrameInContentCoordsLocked(Rect rect) { MagnificationSpec spec = mMagnificationSpec; - mMagnifiedBounds.getBounds(rect); + mMagnificationRegion.getBounds(rect); rect.offset((int) -spec.offsetX, (int) -spec.offsetY); rect.scale(1.0f / spec.scale); } @@ -886,7 +876,7 @@ final class AccessibilityController { } private class MyHandler extends Handler { - public static final int MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED = 1; + public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1; public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2; public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3; public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4; @@ -899,13 +889,11 @@ final class AccessibilityController { @Override public void handleMessage(Message message) { switch (message.what) { - case MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED: { + case MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED: { final SomeArgs args = (SomeArgs) message.obj; final Region magnifiedBounds = (Region) args.arg1; - final Region availableBounds = (Region) args.arg2; - mCallbacks.onMagnifiedBoundsChanged(magnifiedBounds, availableBounds); + mCallbacks.onMagnificationRegionChanged(magnifiedBounds); magnifiedBounds.recycle(); - availableBounds.recycle(); } break; case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 875ad9224327c..0d8f0880b6f2c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -10914,12 +10914,10 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void getMagnificationRegions(@NonNull Region outMagnified, - @NonNull Region outAvailable) { + public void getMagnificationRegion(@NonNull Region magnificationRegion) { synchronized (mWindowMap) { if (mAccessibilityController != null) { - mAccessibilityController.getMagnificationRegionsLocked( - outMagnified, outAvailable); + mAccessibilityController.getMagnificationRegionLocked(magnificationRegion); } else { throw new IllegalStateException("Magnification callbacks not set!"); }