1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.accessibility;
18 
19 import android.animation.ValueAnimator;
20 import android.annotation.NonNull;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.graphics.Rect;
26 import android.graphics.Region;
27 import android.os.AsyncTask;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.provider.Settings;
31 import android.text.TextUtils;
32 import android.util.MathUtils;
33 import android.util.Slog;
34 import android.util.SparseArray;
35 import android.view.Display;
36 import android.view.MagnificationSpec;
37 import android.view.View;
38 import android.view.animation.DecelerateInterpolator;
39 
40 import com.android.internal.R;
41 import com.android.internal.annotations.GuardedBy;
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.internal.util.function.pooled.PooledLambda;
44 import com.android.server.LocalServices;
45 import com.android.server.wm.WindowManagerInternal;
46 
47 import java.util.Locale;
48 
49 /**
50  * This class is used to control and query the state of display magnification
51  * from the accessibility manager and related classes. It is responsible for
52  * holding the current state of magnification and animation, and it handles
53  * communication between the accessibility manager and window manager.
54  *
55  * Magnification is limited to the range [MIN_SCALE, MAX_SCALE], and can only occur inside the
56  * magnification region. If a value is out of bounds, it will be adjusted to guarantee these
57  * constraints.
58  */
59 public class MagnificationController {
60     private static final boolean DEBUG = false;
61     private static final String LOG_TAG = "MagnificationController";
62 
63     public static final float MIN_SCALE = 1.0f;
64     public static final float MAX_SCALE = 8.0f;
65 
66     private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false;
67 
68     private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f;
69 
70     private final Object mLock;
71 
72     private final ControllerContext mControllerCtx;
73 
74     private final ScreenStateObserver mScreenStateObserver;
75 
76     private int mUserId;
77 
78     private final long mMainThreadId;
79 
80     /** List of display Magnification, mapping from displayId -> DisplayMagnification. */
81     @GuardedBy("mLock")
82     private final SparseArray<DisplayMagnification> mDisplays = new SparseArray<>(0);
83 
84     /**
85      * This class implements {@link WindowManagerInternal.MagnificationCallbacks} and holds
86      * magnification information per display.
87      */
88     private final class DisplayMagnification implements
89             WindowManagerInternal.MagnificationCallbacks {
90         /**
91          * The current magnification spec. If an animation is running, this
92          * reflects the end state.
93          */
94         private final MagnificationSpec mCurrentMagnificationSpec = MagnificationSpec.obtain();
95 
96         private final Region mMagnificationRegion = Region.obtain();
97         private final Rect mMagnificationBounds = new Rect();
98 
99         private final Rect mTempRect = new Rect();
100         private final Rect mTempRect1 = new Rect();
101 
102         private final SpecAnimationBridge mSpecAnimationBridge;
103 
104         // Flag indicating that we are registered with window manager.
105         private boolean mRegistered;
106         private boolean mUnregisterPending;
107         private boolean mDeleteAfterUnregister;
108 
109         private final int mDisplayId;
110 
111         private static final int INVALID_ID = -1;
112         private int mIdOfLastServiceToMagnify = INVALID_ID;
113 
DisplayMagnification(int displayId)114         DisplayMagnification(int displayId) {
115             mDisplayId = displayId;
116             mSpecAnimationBridge = new SpecAnimationBridge(mControllerCtx, mLock, mDisplayId);
117         }
118 
119         /**
120          * Registers magnification callback and get current magnification region from
121          * window manager.
122          *
123          * @return true if callback registers successful.
124          */
125         @GuardedBy("mLock")
register()126         boolean register() {
127             mRegistered = mControllerCtx.getWindowManager().setMagnificationCallbacks(
128                     mDisplayId, this);
129             if (!mRegistered) {
130                 Slog.w(LOG_TAG, "set magnification callbacks fail, displayId:" + mDisplayId);
131                 return false;
132             }
133             mSpecAnimationBridge.setEnabled(true);
134             // Obtain initial state.
135             mControllerCtx.getWindowManager().getMagnificationRegion(
136                     mDisplayId, mMagnificationRegion);
137             mMagnificationRegion.getBounds(mMagnificationBounds);
138             return true;
139         }
140 
141         /**
142          * Unregisters magnification callback from window manager. Callbacks to
143          * {@link MagnificationController#unregisterCallbackLocked(int, boolean)} after
144          * unregistered.
145          *
146          * @param delete true if this instance should be removed from the SparseArray in
147          *               MagnificationController after unregistered, for example, display removed.
148          */
149         @GuardedBy("mLock")
unregister(boolean delete)150         void unregister(boolean delete) {
151             if (mRegistered) {
152                 mSpecAnimationBridge.setEnabled(false);
153                 mControllerCtx.getWindowManager().setMagnificationCallbacks(
154                         mDisplayId, null);
155                 mMagnificationRegion.setEmpty();
156                 mRegistered = false;
157                 unregisterCallbackLocked(mDisplayId, delete);
158             }
159             mUnregisterPending = false;
160         }
161 
162         /**
163          * Reset magnification status with animation enabled. {@link #unregister(boolean)} will be
164          * called after animation finished.
165          *
166          * @param delete true if this instance should be removed from the SparseArray in
167          *               MagnificationController after unregistered, for example, display removed.
168          */
169         @GuardedBy("mLock")
unregisterPending(boolean delete)170         void unregisterPending(boolean delete) {
171             mDeleteAfterUnregister = delete;
172             mUnregisterPending = true;
173             reset(true);
174         }
175 
isRegistered()176         boolean isRegistered() {
177             return mRegistered;
178         }
179 
isMagnifying()180         boolean isMagnifying() {
181             return mCurrentMagnificationSpec.scale > 1.0f;
182         }
183 
getScale()184         float getScale() {
185             return mCurrentMagnificationSpec.scale;
186         }
187 
getOffsetX()188         float getOffsetX() {
189             return mCurrentMagnificationSpec.offsetX;
190         }
191 
getOffsetY()192         float getOffsetY() {
193             return mCurrentMagnificationSpec.offsetY;
194         }
195 
196         @GuardedBy("mLock")
getCenterX()197         float getCenterX() {
198             return (mMagnificationBounds.width() / 2.0f
199                     + mMagnificationBounds.left - getOffsetX()) / getScale();
200         }
201 
202         @GuardedBy("mLock")
getCenterY()203         float getCenterY() {
204             return (mMagnificationBounds.height() / 2.0f
205                     + mMagnificationBounds.top - getOffsetY()) / getScale();
206         }
207 
208         /**
209          * Returns the scale currently used by the window manager. If an
210          * animation is in progress, this reflects the current state of the
211          * animation.
212          *
213          * @return the scale currently used by the window manager
214          */
getSentScale()215         float getSentScale() {
216             return mSpecAnimationBridge.mSentMagnificationSpec.scale;
217         }
218 
219         /**
220          * Returns the X offset currently used by the window manager. If an
221          * animation is in progress, this reflects the current state of the
222          * animation.
223          *
224          * @return the X offset currently used by the window manager
225          */
getSentOffsetX()226         float getSentOffsetX() {
227             return mSpecAnimationBridge.mSentMagnificationSpec.offsetX;
228         }
229 
230         /**
231          * Returns the Y offset currently used by the window manager. If an
232          * animation is in progress, this reflects the current state of the
233          * animation.
234          *
235          * @return the Y offset currently used by the window manager
236          */
getSentOffsetY()237         float getSentOffsetY() {
238             return mSpecAnimationBridge.mSentMagnificationSpec.offsetY;
239         }
240 
241         @Override
onMagnificationRegionChanged(Region magnificationRegion)242         public void onMagnificationRegionChanged(Region magnificationRegion) {
243             final Message m = PooledLambda.obtainMessage(
244                     DisplayMagnification::updateMagnificationRegion, this,
245                     Region.obtain(magnificationRegion));
246             mControllerCtx.getHandler().sendMessage(m);
247         }
248 
249         @Override
onRectangleOnScreenRequested(int left, int top, int right, int bottom)250         public void onRectangleOnScreenRequested(int left, int top, int right, int bottom) {
251             final Message m = PooledLambda.obtainMessage(
252                     DisplayMagnification::requestRectangleOnScreen, this,
253                     left, top, right, bottom);
254             mControllerCtx.getHandler().sendMessage(m);
255         }
256 
257         @Override
onRotationChanged(int rotation)258         public void onRotationChanged(int rotation) {
259             // Treat as context change and reset
260             final Message m = PooledLambda.obtainMessage(MagnificationController::resetIfNeeded,
261                     MagnificationController.this, mDisplayId, true);
262             mControllerCtx.getHandler().sendMessage(m);
263         }
264 
265         @Override
onUserContextChanged()266         public void onUserContextChanged() {
267             final Message m = PooledLambda.obtainMessage(MagnificationController::resetIfNeeded,
268                     MagnificationController.this, mDisplayId, true);
269             mControllerCtx.getHandler().sendMessage(m);
270         }
271 
272         /**
273          * Update our copy of the current magnification region
274          *
275          * @param magnified the magnified region
276          */
updateMagnificationRegion(Region magnified)277         void updateMagnificationRegion(Region magnified) {
278             synchronized (mLock) {
279                 if (!mRegistered) {
280                     // Don't update if we've unregistered
281                     return;
282                 }
283                 if (!mMagnificationRegion.equals(magnified)) {
284                     mMagnificationRegion.set(magnified);
285                     mMagnificationRegion.getBounds(mMagnificationBounds);
286                     // It's possible that our magnification spec is invalid with the new bounds.
287                     // Adjust the current spec's offsets if necessary.
288                     if (updateCurrentSpecWithOffsetsLocked(
289                             mCurrentMagnificationSpec.offsetX, mCurrentMagnificationSpec.offsetY)) {
290                         sendSpecToAnimation(mCurrentMagnificationSpec, false);
291                     }
292                     onMagnificationChangedLocked();
293                 }
294                 magnified.recycle();
295             }
296         }
297 
sendSpecToAnimation(MagnificationSpec spec, boolean animate)298         void sendSpecToAnimation(MagnificationSpec spec, boolean animate) {
299             if (DEBUG) {
300                 Slog.i(LOG_TAG,
301                         "sendSpecToAnimation(spec = " + spec + ", animate = " + animate + ")");
302             }
303             if (Thread.currentThread().getId() == mMainThreadId) {
304                 mSpecAnimationBridge.updateSentSpecMainThread(spec, animate);
305             } else {
306                 final Message m = PooledLambda.obtainMessage(
307                         SpecAnimationBridge::updateSentSpecMainThread,
308                         mSpecAnimationBridge, spec, animate);
309                 mControllerCtx.getHandler().sendMessage(m);
310             }
311         }
312 
313         /**
314          * Get the ID of the last service that changed the magnification spec.
315          *
316          * @return The id
317          */
getIdOfLastServiceToMagnify()318         int getIdOfLastServiceToMagnify() {
319             return mIdOfLastServiceToMagnify;
320         }
321 
onMagnificationChangedLocked()322         void onMagnificationChangedLocked() {
323             mControllerCtx.getAms().notifyMagnificationChanged(mDisplayId, mMagnificationRegion,
324                     getScale(), getCenterX(), getCenterY());
325             if (mUnregisterPending && !isMagnifying()) {
326                 unregister(mDeleteAfterUnregister);
327             }
328         }
329 
330         @GuardedBy("mLock")
magnificationRegionContains(float x, float y)331         boolean magnificationRegionContains(float x, float y) {
332             return mMagnificationRegion.contains((int) x, (int) y);
333         }
334 
335         @GuardedBy("mLock")
getMagnificationBounds(@onNull Rect outBounds)336         void getMagnificationBounds(@NonNull Rect outBounds) {
337             outBounds.set(mMagnificationBounds);
338         }
339 
340         @GuardedBy("mLock")
getMagnificationRegion(@onNull Region outRegion)341         void getMagnificationRegion(@NonNull Region outRegion) {
342             outRegion.set(mMagnificationRegion);
343         }
344 
requestRectangleOnScreen(int left, int top, int right, int bottom)345         void requestRectangleOnScreen(int left, int top, int right, int bottom) {
346             synchronized (mLock) {
347                 final Rect magnifiedFrame = mTempRect;
348                 getMagnificationBounds(magnifiedFrame);
349                 if (!magnifiedFrame.intersects(left, top, right, bottom)) {
350                     return;
351                 }
352 
353                 final Rect magnifFrameInScreenCoords = mTempRect1;
354                 getMagnifiedFrameInContentCoordsLocked(magnifFrameInScreenCoords);
355 
356                 final float scrollX;
357                 final float scrollY;
358                 if (right - left > magnifFrameInScreenCoords.width()) {
359                     final int direction = TextUtils
360                             .getLayoutDirectionFromLocale(Locale.getDefault());
361                     if (direction == View.LAYOUT_DIRECTION_LTR) {
362                         scrollX = left - magnifFrameInScreenCoords.left;
363                     } else {
364                         scrollX = right - magnifFrameInScreenCoords.right;
365                     }
366                 } else if (left < magnifFrameInScreenCoords.left) {
367                     scrollX = left - magnifFrameInScreenCoords.left;
368                 } else if (right > magnifFrameInScreenCoords.right) {
369                     scrollX = right - magnifFrameInScreenCoords.right;
370                 } else {
371                     scrollX = 0;
372                 }
373 
374                 if (bottom - top > magnifFrameInScreenCoords.height()) {
375                     scrollY = top - magnifFrameInScreenCoords.top;
376                 } else if (top < magnifFrameInScreenCoords.top) {
377                     scrollY = top - magnifFrameInScreenCoords.top;
378                 } else if (bottom > magnifFrameInScreenCoords.bottom) {
379                     scrollY = bottom - magnifFrameInScreenCoords.bottom;
380                 } else {
381                     scrollY = 0;
382                 }
383 
384                 final float scale = getScale();
385                 offsetMagnifiedRegion(scrollX * scale, scrollY * scale, INVALID_ID);
386             }
387         }
388 
getMagnifiedFrameInContentCoordsLocked(Rect outFrame)389         void getMagnifiedFrameInContentCoordsLocked(Rect outFrame) {
390             final float scale = getSentScale();
391             final float offsetX = getSentOffsetX();
392             final float offsetY = getSentOffsetY();
393             getMagnificationBounds(outFrame);
394             outFrame.offset((int) -offsetX, (int) -offsetY);
395             outFrame.scale(1.0f / scale);
396         }
397 
398         @GuardedBy("mLock")
setForceShowMagnifiableBounds(boolean show)399         void setForceShowMagnifiableBounds(boolean show) {
400             if (mRegistered) {
401                 mControllerCtx.getWindowManager().setForceShowMagnifiableBounds(
402                         mDisplayId, show);
403             }
404         }
405 
406         @GuardedBy("mLock")
reset(boolean animate)407         boolean reset(boolean animate) {
408             if (!mRegistered) {
409                 return false;
410             }
411             final MagnificationSpec spec = mCurrentMagnificationSpec;
412             final boolean changed = !spec.isNop();
413             if (changed) {
414                 spec.clear();
415                 onMagnificationChangedLocked();
416             }
417             mIdOfLastServiceToMagnify = INVALID_ID;
418             sendSpecToAnimation(spec, animate);
419             return changed;
420         }
421 
422         @GuardedBy("mLock")
setScale(float scale, float pivotX, float pivotY, boolean animate, int id)423         boolean setScale(float scale, float pivotX, float pivotY,
424                 boolean animate, int id) {
425             if (!mRegistered) {
426                 return false;
427             }
428             // Constrain scale immediately for use in the pivot calculations.
429             scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
430 
431             final Rect viewport = mTempRect;
432             mMagnificationRegion.getBounds(viewport);
433             final MagnificationSpec spec = mCurrentMagnificationSpec;
434             final float oldScale = spec.scale;
435             final float oldCenterX =
436                     (viewport.width() / 2.0f - spec.offsetX + viewport.left) / oldScale;
437             final float oldCenterY =
438                     (viewport.height() / 2.0f - spec.offsetY + viewport.top) / oldScale;
439             final float normPivotX = (pivotX - spec.offsetX) / oldScale;
440             final float normPivotY = (pivotY - spec.offsetY) / oldScale;
441             final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
442             final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
443             final float centerX = normPivotX + offsetX;
444             final float centerY = normPivotY + offsetY;
445             mIdOfLastServiceToMagnify = id;
446             return setScaleAndCenter(scale, centerX, centerY, animate, id);
447         }
448 
449         @GuardedBy("mLock")
setScaleAndCenter(float scale, float centerX, float centerY, boolean animate, int id)450         boolean setScaleAndCenter(float scale, float centerX, float centerY,
451                 boolean animate, int id) {
452             if (!mRegistered) {
453                 return false;
454             }
455             if (DEBUG) {
456                 Slog.i(LOG_TAG,
457                         "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX
458                                 + ", centerY = " + centerY + ", animate = " + animate
459                                 + ", id = " + id
460                                 + ")");
461             }
462             final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
463             sendSpecToAnimation(mCurrentMagnificationSpec, animate);
464             if (isMagnifying() && (id != INVALID_ID)) {
465                 mIdOfLastServiceToMagnify = id;
466             }
467             return changed;
468         }
469 
470         /**
471          * Updates the current magnification spec.
472          *
473          * @param scale the magnification scale
474          * @param centerX the unscaled, screen-relative X coordinate of the center
475          *                of the viewport, or {@link Float#NaN} to leave unchanged
476          * @param centerY the unscaled, screen-relative Y coordinate of the center
477          *                of the viewport, or {@link Float#NaN} to leave unchanged
478          * @return {@code true} if the magnification spec changed or {@code false}
479          *         otherwise
480          */
updateMagnificationSpecLocked(float scale, float centerX, float centerY)481         boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY) {
482             // Handle defaults.
483             if (Float.isNaN(centerX)) {
484                 centerX = getCenterX();
485             }
486             if (Float.isNaN(centerY)) {
487                 centerY = getCenterY();
488             }
489             if (Float.isNaN(scale)) {
490                 scale = getScale();
491             }
492 
493             // Compute changes.
494             boolean changed = false;
495 
496             final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
497             if (Float.compare(mCurrentMagnificationSpec.scale, normScale) != 0) {
498                 mCurrentMagnificationSpec.scale = normScale;
499                 changed = true;
500             }
501 
502             final float nonNormOffsetX = mMagnificationBounds.width() / 2.0f
503                     + mMagnificationBounds.left - centerX * normScale;
504             final float nonNormOffsetY = mMagnificationBounds.height() / 2.0f
505                     + mMagnificationBounds.top - centerY * normScale;
506             changed |= updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY);
507 
508             if (changed) {
509                 onMagnificationChangedLocked();
510             }
511 
512             return changed;
513         }
514 
515         @GuardedBy("mLock")
offsetMagnifiedRegion(float offsetX, float offsetY, int id)516         void offsetMagnifiedRegion(float offsetX, float offsetY, int id) {
517             if (!mRegistered) {
518                 return;
519             }
520 
521             final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
522             final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
523             if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) {
524                 onMagnificationChangedLocked();
525             }
526             if (id != INVALID_ID) {
527                 mIdOfLastServiceToMagnify = id;
528             }
529             sendSpecToAnimation(mCurrentMagnificationSpec, false);
530         }
531 
updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY)532         boolean updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY) {
533             if (DEBUG) {
534                 Slog.i(LOG_TAG,
535                         "updateCurrentSpecWithOffsetsLocked(nonNormOffsetX = " + nonNormOffsetX
536                                 + ", nonNormOffsetY = " + nonNormOffsetY + ")");
537             }
538             boolean changed = false;
539             final float offsetX = MathUtils.constrain(
540                     nonNormOffsetX, getMinOffsetXLocked(), getMaxOffsetXLocked());
541             if (Float.compare(mCurrentMagnificationSpec.offsetX, offsetX) != 0) {
542                 mCurrentMagnificationSpec.offsetX = offsetX;
543                 changed = true;
544             }
545             final float offsetY = MathUtils.constrain(
546                     nonNormOffsetY, getMinOffsetYLocked(), getMaxOffsetYLocked());
547             if (Float.compare(mCurrentMagnificationSpec.offsetY, offsetY) != 0) {
548                 mCurrentMagnificationSpec.offsetY = offsetY;
549                 changed = true;
550             }
551             return changed;
552         }
553 
getMinOffsetXLocked()554         float getMinOffsetXLocked() {
555             final float viewportWidth = mMagnificationBounds.width();
556             final float viewportLeft = mMagnificationBounds.left;
557             return (viewportLeft + viewportWidth) -
558                     (viewportLeft + viewportWidth) * mCurrentMagnificationSpec.scale;
559         }
560 
getMaxOffsetXLocked()561         float getMaxOffsetXLocked() {
562             return mMagnificationBounds.left -
563                     mMagnificationBounds.left * mCurrentMagnificationSpec.scale;
564         }
565 
getMinOffsetYLocked()566         float getMinOffsetYLocked() {
567             final float viewportHeight = mMagnificationBounds.height();
568             final float viewportTop = mMagnificationBounds.top;
569             return (viewportTop + viewportHeight) -
570                     (viewportTop + viewportHeight) * mCurrentMagnificationSpec.scale;
571         }
572 
getMaxOffsetYLocked()573         float getMaxOffsetYLocked() {
574             return mMagnificationBounds.top -
575                     mMagnificationBounds.top * mCurrentMagnificationSpec.scale;
576         }
577 
578         @Override
toString()579         public String toString() {
580             return "DisplayMagnification["
581                     + "mCurrentMagnificationSpec=" + mCurrentMagnificationSpec
582                     + ", mMagnificationRegion=" + mMagnificationRegion
583                     + ", mMagnificationBounds=" + mMagnificationBounds
584                     + ", mDisplayId=" + mDisplayId
585                     + ", mIdOfLastServiceToMagnify=" + mIdOfLastServiceToMagnify
586                     + ", mRegistered=" + mRegistered
587                     + ", mUnregisterPending=" + mUnregisterPending
588                     + ']';
589         }
590     }
591 
592     /**
593      * MagnificationController Constructor
594      */
MagnificationController(@onNull Context context, @NonNull AccessibilityManagerService ams, @NonNull Object lock)595     public MagnificationController(@NonNull Context context,
596             @NonNull AccessibilityManagerService ams, @NonNull Object lock) {
597         this(new ControllerContext(context, ams,
598                 LocalServices.getService(WindowManagerInternal.class),
599                 new Handler(context.getMainLooper()),
600                 context.getResources().getInteger(R.integer.config_longAnimTime)), lock);
601     }
602 
603     /**
604      * Constructor for tests
605      */
606     @VisibleForTesting
MagnificationController(@onNull ControllerContext ctx, @NonNull Object lock)607     public MagnificationController(@NonNull ControllerContext ctx, @NonNull Object lock) {
608         mControllerCtx = ctx;
609         mLock = lock;
610         mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId();
611         mScreenStateObserver = new ScreenStateObserver(mControllerCtx.getContext(), this);
612     }
613 
614     /**
615      * Start tracking the magnification region for services that control magnification and the
616      * magnification gesture handler.
617      *
618      * This tracking imposes a cost on the system, so we avoid tracking this data unless it's
619      * required.
620      *
621      * @param displayId The logical display id.
622      */
register(int displayId)623     public void register(int displayId) {
624         synchronized (mLock) {
625             DisplayMagnification display = mDisplays.get(displayId);
626             if (display == null) {
627                 display = new DisplayMagnification(displayId);
628             }
629             if (display.isRegistered()) {
630                 return;
631             }
632             if (display.register()) {
633                 mDisplays.put(displayId, display);
634                 mScreenStateObserver.registerIfNecessary();
635             }
636         }
637     }
638 
639     /**
640      * Stop requiring tracking the magnification region. We may remain registered while we
641      * reset magnification.
642      *
643      * @param displayId The logical display id.
644      */
unregister(int displayId)645     public void unregister(int displayId) {
646         synchronized (mLock) {
647             unregisterLocked(displayId, false);
648         }
649     }
650 
651     /**
652      * Stop tracking all displays' magnification region.
653      */
unregisterAll()654     public void unregisterAll() {
655         synchronized (mLock) {
656             // display will be removed from array after unregister, we need to clone it to
657             // prevent error.
658             final SparseArray<DisplayMagnification> displays = mDisplays.clone();
659             for (int i = 0; i < displays.size(); i++) {
660                 unregisterLocked(displays.keyAt(i), false);
661             }
662         }
663     }
664 
665     /**
666      * Remove the display magnification with given id.
667      *
668      * @param displayId The logical display id.
669      */
onDisplayRemoved(int displayId)670     public void onDisplayRemoved(int displayId) {
671         synchronized (mLock) {
672             unregisterLocked(displayId, true);
673         }
674     }
675 
676     /**
677      * Check if we are registered on specified display. Note that we may be planning to unregister
678      * at any moment.
679      *
680      * @return {@code true} if the controller is registered on specified display.
681      * {@code false} otherwise.
682      *
683      * @param displayId The logical display id.
684      */
isRegistered(int displayId)685     public boolean isRegistered(int displayId) {
686         synchronized (mLock) {
687             final DisplayMagnification display = mDisplays.get(displayId);
688             if (display == null) {
689                 return false;
690             }
691             return display.isRegistered();
692         }
693     }
694 
695     /**
696      * @param displayId The logical display id.
697      * @return {@code true} if magnification is active, e.g. the scale
698      *         is > 1, {@code false} otherwise
699      */
isMagnifying(int displayId)700     public boolean isMagnifying(int displayId) {
701         synchronized (mLock) {
702             final DisplayMagnification display = mDisplays.get(displayId);
703             if (display == null) {
704                 return false;
705             }
706             return display.isMagnifying();
707         }
708     }
709 
710     /**
711      * Returns whether the magnification region contains the specified
712      * screen-relative coordinates.
713      *
714      * @param displayId The logical display id.
715      * @param x the screen-relative X coordinate to check
716      * @param y the screen-relative Y coordinate to check
717      * @return {@code true} if the coordinate is contained within the
718      *         magnified region, or {@code false} otherwise
719      */
magnificationRegionContains(int displayId, float x, float y)720     public boolean magnificationRegionContains(int displayId, float x, float y) {
721         synchronized (mLock) {
722             final DisplayMagnification display = mDisplays.get(displayId);
723             if (display == null) {
724                 return false;
725             }
726             return display.magnificationRegionContains(x, y);
727         }
728     }
729 
730     /**
731      * Populates the specified rect with the screen-relative bounds of the
732      * magnification region. If magnification is not enabled, the returned
733      * bounds will be empty.
734      *
735      * @param displayId The logical display id.
736      * @param outBounds rect to populate with the bounds of the magnified
737      *                  region
738      */
getMagnificationBounds(int displayId, @NonNull Rect outBounds)739     public void getMagnificationBounds(int displayId, @NonNull Rect outBounds) {
740         synchronized (mLock) {
741             final DisplayMagnification display = mDisplays.get(displayId);
742             if (display == null) {
743                 return;
744             }
745             display.getMagnificationBounds(outBounds);
746         }
747     }
748 
749     /**
750      * Populates the specified region with the screen-relative magnification
751      * region. If magnification is not enabled, then the returned region
752      * will be empty.
753      *
754      * @param displayId The logical display id.
755      * @param outRegion the region to populate
756      */
getMagnificationRegion(int displayId, @NonNull Region outRegion)757     public void getMagnificationRegion(int displayId, @NonNull Region outRegion) {
758         synchronized (mLock) {
759             final DisplayMagnification display = mDisplays.get(displayId);
760             if (display == null) {
761                 return;
762             }
763             display.getMagnificationRegion(outRegion);
764         }
765     }
766 
767     /**
768      * Returns the magnification scale. If an animation is in progress,
769      * this reflects the end state of the animation.
770      *
771      * @param displayId The logical display id.
772      * @return the scale
773      */
getScale(int displayId)774     public float getScale(int displayId) {
775         synchronized (mLock) {
776             final DisplayMagnification display = mDisplays.get(displayId);
777             if (display == null) {
778                 return 1.0f;
779             }
780             return display.getScale();
781         }
782     }
783 
784     /**
785      * Returns the X offset of the magnification viewport. If an animation
786      * is in progress, this reflects the end state of the animation.
787      *
788      * @param displayId The logical display id.
789      * @return the X offset
790      */
getOffsetX(int displayId)791     public float getOffsetX(int displayId) {
792         synchronized (mLock) {
793             final DisplayMagnification display = mDisplays.get(displayId);
794             if (display == null) {
795                 return 0.0f;
796             }
797             return display.getOffsetX();
798         }
799     }
800 
801     /**
802      * Returns the screen-relative X coordinate of the center of the
803      * magnification viewport.
804      *
805      * @param displayId The logical display id.
806      * @return the X coordinate
807      */
getCenterX(int displayId)808     public float getCenterX(int displayId) {
809         synchronized (mLock) {
810             final DisplayMagnification display = mDisplays.get(displayId);
811             if (display == null) {
812                 return 0.0f;
813             }
814             return display.getCenterX();
815         }
816     }
817 
818     /**
819      * Returns the Y offset of the magnification viewport. If an animation
820      * is in progress, this reflects the end state of the animation.
821      *
822      * @param displayId The logical display id.
823      * @return the Y offset
824      */
getOffsetY(int displayId)825     public float getOffsetY(int displayId) {
826         synchronized (mLock) {
827             final DisplayMagnification display = mDisplays.get(displayId);
828             if (display == null) {
829                 return 0.0f;
830             }
831             return display.getOffsetY();
832         }
833     }
834 
835     /**
836      * Returns the screen-relative Y coordinate of the center of the
837      * magnification viewport.
838      *
839      * @param displayId The logical display id.
840      * @return the Y coordinate
841      */
getCenterY(int displayId)842     public float getCenterY(int displayId) {
843         synchronized (mLock) {
844             final DisplayMagnification display = mDisplays.get(displayId);
845             if (display == null) {
846                 return 0.0f;
847             }
848             return display.getCenterY();
849         }
850     }
851 
852     /**
853      * Resets the magnification scale and center, optionally animating the
854      * transition.
855      *
856      * @param displayId The logical display id.
857      * @param animate {@code true} to animate the transition, {@code false}
858      *                to transition immediately
859      * @return {@code true} if the magnification spec changed, {@code false} if
860      *         the spec did not change
861      */
reset(int displayId, boolean animate)862     public boolean reset(int displayId, boolean animate) {
863         synchronized (mLock) {
864             final DisplayMagnification display = mDisplays.get(displayId);
865             if (display == null) {
866                 return false;
867             }
868             return display.reset(animate);
869         }
870     }
871 
872     /**
873      * Scales the magnified region around the specified pivot point,
874      * optionally animating the transition. If animation is disabled, the
875      * transition is immediate.
876      *
877      * @param displayId The logical display id.
878      * @param scale the target scale, must be >= 1
879      * @param pivotX the screen-relative X coordinate around which to scale
880      * @param pivotY the screen-relative Y coordinate around which to scale
881      * @param animate {@code true} to animate the transition, {@code false}
882      *                to transition immediately
883      * @param id the ID of the service requesting the change
884      * @return {@code true} if the magnification spec changed, {@code false} if
885      *         the spec did not change
886      */
setScale(int displayId, float scale, float pivotX, float pivotY, boolean animate, int id)887     public boolean setScale(int displayId, float scale, float pivotX, float pivotY,
888             boolean animate, int id) {
889         synchronized (mLock) {
890             final DisplayMagnification display = mDisplays.get(displayId);
891             if (display == null) {
892                 return false;
893             }
894             return display.setScale(scale, pivotX, pivotY, animate, id);
895         }
896     }
897 
898     /**
899      * Sets the center of the magnified region, optionally animating the
900      * transition. If animation is disabled, the transition is immediate.
901      *
902      * @param displayId The logical display id.
903      * @param centerX the screen-relative X coordinate around which to
904      *                center
905      * @param centerY the screen-relative Y coordinate around which to
906      *                center
907      * @param animate {@code true} to animate the transition, {@code false}
908      *                to transition immediately
909      * @param id      the ID of the service requesting the change
910      * @return {@code true} if the magnification spec changed, {@code false} if
911      * the spec did not change
912      */
setCenter(int displayId, float centerX, float centerY, boolean animate, int id)913     public boolean setCenter(int displayId, float centerX, float centerY, boolean animate, int id) {
914         synchronized (mLock) {
915             final DisplayMagnification display = mDisplays.get(displayId);
916             if (display == null) {
917                 return false;
918             }
919             return display.setScaleAndCenter(Float.NaN, centerX, centerY, animate, id);
920         }
921     }
922 
923     /**
924      * Sets the scale and center of the magnified region, optionally
925      * animating the transition. If animation is disabled, the transition
926      * is immediate.
927      *
928      * @param displayId The logical display id.
929      * @param scale the target scale, or {@link Float#NaN} to leave unchanged
930      * @param centerX the screen-relative X coordinate around which to
931      *                center and scale, or {@link Float#NaN} to leave unchanged
932      * @param centerY the screen-relative Y coordinate around which to
933      *                center and scale, or {@link Float#NaN} to leave unchanged
934      * @param animate {@code true} to animate the transition, {@code false}
935      *                to transition immediately
936      * @param id the ID of the service requesting the change
937      * @return {@code true} if the magnification spec changed, {@code false} if
938      *         the spec did not change
939      */
setScaleAndCenter(int displayId, float scale, float centerX, float centerY, boolean animate, int id)940     public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY,
941             boolean animate, int id) {
942         synchronized (mLock) {
943             final DisplayMagnification display = mDisplays.get(displayId);
944             if (display == null) {
945                 return false;
946             }
947             return display.setScaleAndCenter(scale, centerX, centerY, animate, id);
948         }
949     }
950 
951     /**
952      * Offsets the magnified region. Note that the offsetX and offsetY values actually move in the
953      * opposite direction as the offsets passed in here.
954      *
955      * @param displayId The logical display id.
956      * @param offsetX the amount in pixels to offset the region in the X direction, in current
957      *                screen pixels.
958      * @param offsetY the amount in pixels to offset the region in the Y direction, in current
959      *                screen pixels.
960      * @param id      the ID of the service requesting the change
961      */
offsetMagnifiedRegion(int displayId, float offsetX, float offsetY, int id)962     public void offsetMagnifiedRegion(int displayId, float offsetX, float offsetY, int id) {
963         synchronized (mLock) {
964             final DisplayMagnification display = mDisplays.get(displayId);
965             if (display == null) {
966                 return;
967             }
968             display.offsetMagnifiedRegion(offsetX, offsetY, id);
969         }
970     }
971 
972     /**
973      * Get the ID of the last service that changed the magnification spec.
974      *
975      * @param displayId The logical display id.
976      * @return The id
977      */
getIdOfLastServiceToMagnify(int displayId)978     public int getIdOfLastServiceToMagnify(int displayId) {
979         synchronized (mLock) {
980             final DisplayMagnification display = mDisplays.get(displayId);
981             if (display == null) {
982                 return -1;
983             }
984             return display.getIdOfLastServiceToMagnify();
985         }
986     }
987 
988     /**
989      * Persists the default display magnification scale to the current user's settings.
990      */
persistScale()991     public void persistScale() {
992         // TODO: b/123047354, Need support multi-display?
993         final float scale = getScale(Display.DEFAULT_DISPLAY);
994         final int userId = mUserId;
995 
996         new AsyncTask<Void, Void, Void>() {
997             @Override
998             protected Void doInBackground(Void... params) {
999                 mControllerCtx.putMagnificationScale(scale, userId);
1000                 return null;
1001             }
1002         }.execute();
1003     }
1004 
1005     /**
1006      * Retrieves a previously persisted magnification scale from the current
1007      * user's settings.
1008      *
1009      * @return the previously persisted magnification scale, or the default
1010      *         scale if none is available
1011      */
getPersistedScale()1012     public float getPersistedScale() {
1013         return mControllerCtx.getMagnificationScale(mUserId);
1014     }
1015 
1016     /**
1017      * Sets the currently active user ID.
1018      *
1019      * @param userId the currently active user ID
1020      */
setUserId(int userId)1021     public void setUserId(int userId) {
1022         if (mUserId == userId) {
1023             return;
1024         }
1025         mUserId = userId;
1026         resetAllIfNeeded(false);
1027     }
1028 
1029     /**
1030      * Resets all displays' magnification if last magnifying service is disabled.
1031      *
1032      * @param connectionId
1033      */
resetAllIfNeeded(int connectionId)1034     public void resetAllIfNeeded(int connectionId) {
1035         synchronized (mLock) {
1036             for (int i = 0; i < mDisplays.size(); i++) {
1037                 resetIfNeeded(mDisplays.keyAt(i), connectionId);
1038             }
1039         }
1040     }
1041 
1042     /**
1043      * Resets magnification if magnification and auto-update are both enabled.
1044      *
1045      * @param displayId The logical display id.
1046      * @param animate whether the animate the transition
1047      * @return whether was {@link #isMagnifying(int) magnifying}
1048      */
resetIfNeeded(int displayId, boolean animate)1049     boolean resetIfNeeded(int displayId, boolean animate) {
1050         synchronized (mLock) {
1051             final DisplayMagnification display = mDisplays.get(displayId);
1052             if (display == null || !display.isMagnifying()) {
1053                 return false;
1054             }
1055             display.reset(animate);
1056             return true;
1057         }
1058     }
1059 
1060     /**
1061      * Resets magnification if last magnifying service is disabled.
1062      *
1063      * @param displayId The logical display id.
1064      * @param connectionId the connection ID be disabled.
1065      * @return {@code true} on success, {@code false} on failure
1066      */
resetIfNeeded(int displayId, int connectionId)1067     boolean resetIfNeeded(int displayId, int connectionId) {
1068         synchronized (mLock) {
1069             final DisplayMagnification display = mDisplays.get(displayId);
1070             if (display == null || !display.isMagnifying()
1071                     || connectionId != display.getIdOfLastServiceToMagnify()) {
1072                 return false;
1073             }
1074             display.reset(true);
1075             return true;
1076         }
1077     }
1078 
setForceShowMagnifiableBounds(int displayId, boolean show)1079     void setForceShowMagnifiableBounds(int displayId, boolean show) {
1080         synchronized (mLock) {
1081             final DisplayMagnification display = mDisplays.get(displayId);
1082             if (display == null) {
1083                 return;
1084             }
1085             display.setForceShowMagnifiableBounds(show);
1086         }
1087     }
1088 
onScreenTurnedOff()1089     private void onScreenTurnedOff() {
1090         final Message m = PooledLambda.obtainMessage(
1091                 MagnificationController::resetAllIfNeeded, this, false);
1092         mControllerCtx.getHandler().sendMessage(m);
1093     }
1094 
resetAllIfNeeded(boolean animate)1095     private void resetAllIfNeeded(boolean animate) {
1096         synchronized (mLock) {
1097             for (int i = 0; i < mDisplays.size(); i++) {
1098                 resetIfNeeded(mDisplays.keyAt(i), animate);
1099             }
1100         }
1101     }
1102 
unregisterLocked(int displayId, boolean delete)1103     private void unregisterLocked(int displayId, boolean delete) {
1104         final DisplayMagnification display = mDisplays.get(displayId);
1105         if (display == null) {
1106             return;
1107         }
1108         if (!display.isRegistered()) {
1109             if (delete) {
1110                 mDisplays.remove(displayId);
1111             }
1112             return;
1113         }
1114         if (!display.isMagnifying()) {
1115             display.unregister(delete);
1116         } else {
1117             display.unregisterPending(delete);
1118         }
1119     }
1120 
1121     /**
1122      * Callbacks from DisplayMagnification after display magnification unregistered. It will remove
1123      * DisplayMagnification instance if delete is true, and unregister screen state if
1124      * there is no registered display magnification.
1125      */
unregisterCallbackLocked(int displayId, boolean delete)1126     private void unregisterCallbackLocked(int displayId, boolean delete) {
1127         if (delete) {
1128             mDisplays.remove(displayId);
1129         }
1130         // unregister screen state if necessary
1131         boolean hasRegister = false;
1132         for (int i = 0; i < mDisplays.size(); i++) {
1133             final DisplayMagnification display = mDisplays.valueAt(i);
1134             hasRegister = display.isRegistered();
1135             if (hasRegister) {
1136                 break;
1137             }
1138         }
1139         if (!hasRegister) {
1140             mScreenStateObserver.unregister();
1141         }
1142     }
1143 
1144     @Override
toString()1145     public String toString() {
1146         StringBuilder builder = new StringBuilder();
1147         builder.append("MagnificationController[");
1148         builder.append("mUserId=").append(mUserId);
1149         builder.append(", mDisplays=").append(mDisplays);
1150         builder.append("]");
1151         return builder.toString();
1152     }
1153 
1154     /**
1155      * Class responsible for animating spec on the main thread and sending spec
1156      * updates to the window manager.
1157      */
1158     private static class SpecAnimationBridge implements ValueAnimator.AnimatorUpdateListener {
1159         private final ControllerContext mControllerCtx;
1160 
1161         /**
1162          * The magnification spec that was sent to the window manager. This should
1163          * only be accessed with the lock held.
1164          */
1165         private final MagnificationSpec mSentMagnificationSpec = MagnificationSpec.obtain();
1166 
1167         private final MagnificationSpec mStartMagnificationSpec = MagnificationSpec.obtain();
1168 
1169         private final MagnificationSpec mEndMagnificationSpec = MagnificationSpec.obtain();
1170 
1171         private final MagnificationSpec mTmpMagnificationSpec = MagnificationSpec.obtain();
1172 
1173         /**
1174          * The animator should only be accessed and modified on the main (e.g. animation) thread.
1175          */
1176         private final ValueAnimator mValueAnimator;
1177 
1178         private final Object mLock;
1179 
1180         private final int mDisplayId;
1181 
1182         @GuardedBy("mLock")
1183         private boolean mEnabled = false;
1184 
SpecAnimationBridge(ControllerContext ctx, Object lock, int displayId)1185         private SpecAnimationBridge(ControllerContext ctx, Object lock, int displayId) {
1186             mControllerCtx = ctx;
1187             mLock = lock;
1188             mDisplayId = displayId;
1189             final long animationDuration = mControllerCtx.getAnimationDuration();
1190             mValueAnimator = mControllerCtx.newValueAnimator();
1191             mValueAnimator.setDuration(animationDuration);
1192             mValueAnimator.setInterpolator(new DecelerateInterpolator(2.5f));
1193             mValueAnimator.setFloatValues(0.0f, 1.0f);
1194             mValueAnimator.addUpdateListener(this);
1195         }
1196 
1197         /**
1198          * Enabled means the bridge will accept input. When not enabled, the output of the animator
1199          * will be ignored
1200          */
setEnabled(boolean enabled)1201         public void setEnabled(boolean enabled) {
1202             synchronized (mLock) {
1203                 if (enabled != mEnabled) {
1204                     mEnabled = enabled;
1205                     if (!mEnabled) {
1206                         mSentMagnificationSpec.clear();
1207                         mControllerCtx.getWindowManager().setMagnificationSpec(
1208                                 mDisplayId, mSentMagnificationSpec);
1209                     }
1210                 }
1211             }
1212         }
1213 
updateSentSpecMainThread(MagnificationSpec spec, boolean animate)1214         public void updateSentSpecMainThread(MagnificationSpec spec, boolean animate) {
1215             if (mValueAnimator.isRunning()) {
1216                 mValueAnimator.cancel();
1217             }
1218 
1219             // If the current and sent specs don't match, update the sent spec.
1220             synchronized (mLock) {
1221                 final boolean changed = !mSentMagnificationSpec.equals(spec);
1222                 if (changed) {
1223                     if (animate) {
1224                         animateMagnificationSpecLocked(spec);
1225                     } else {
1226                         setMagnificationSpecLocked(spec);
1227                     }
1228                 }
1229             }
1230         }
1231 
1232         @GuardedBy("mLock")
setMagnificationSpecLocked(MagnificationSpec spec)1233         private void setMagnificationSpecLocked(MagnificationSpec spec) {
1234             if (mEnabled) {
1235                 if (DEBUG_SET_MAGNIFICATION_SPEC) {
1236                     Slog.i(LOG_TAG, "Sending: " + spec);
1237                 }
1238 
1239                 mSentMagnificationSpec.setTo(spec);
1240                 mControllerCtx.getWindowManager().setMagnificationSpec(
1241                         mDisplayId, mSentMagnificationSpec);
1242             }
1243         }
1244 
animateMagnificationSpecLocked(MagnificationSpec toSpec)1245         private void animateMagnificationSpecLocked(MagnificationSpec toSpec) {
1246             mEndMagnificationSpec.setTo(toSpec);
1247             mStartMagnificationSpec.setTo(mSentMagnificationSpec);
1248             mValueAnimator.start();
1249         }
1250 
1251         @Override
onAnimationUpdate(ValueAnimator animation)1252         public void onAnimationUpdate(ValueAnimator animation) {
1253             synchronized (mLock) {
1254                 if (mEnabled) {
1255                     float fract = animation.getAnimatedFraction();
1256                     mTmpMagnificationSpec.scale = mStartMagnificationSpec.scale +
1257                             (mEndMagnificationSpec.scale - mStartMagnificationSpec.scale) * fract;
1258                     mTmpMagnificationSpec.offsetX = mStartMagnificationSpec.offsetX +
1259                             (mEndMagnificationSpec.offsetX - mStartMagnificationSpec.offsetX)
1260                                     * fract;
1261                     mTmpMagnificationSpec.offsetY = mStartMagnificationSpec.offsetY +
1262                             (mEndMagnificationSpec.offsetY - mStartMagnificationSpec.offsetY)
1263                                     * fract;
1264                     setMagnificationSpecLocked(mTmpMagnificationSpec);
1265                 }
1266             }
1267         }
1268     }
1269 
1270     private static class ScreenStateObserver extends BroadcastReceiver {
1271         private final Context mContext;
1272         private final MagnificationController mController;
1273         private boolean mRegistered = false;
1274 
ScreenStateObserver(Context context, MagnificationController controller)1275         public ScreenStateObserver(Context context, MagnificationController controller) {
1276             mContext = context;
1277             mController = controller;
1278         }
1279 
registerIfNecessary()1280         public void registerIfNecessary() {
1281             if (!mRegistered) {
1282                 mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF));
1283                 mRegistered = true;
1284             }
1285         }
1286 
unregister()1287         public void unregister() {
1288             if (mRegistered) {
1289                 mContext.unregisterReceiver(this);
1290                 mRegistered = false;
1291             }
1292         }
1293 
1294         @Override
onReceive(Context context, Intent intent)1295         public void onReceive(Context context, Intent intent) {
1296             mController.onScreenTurnedOff();
1297         }
1298     }
1299 
1300     /**
1301      * This class holds resources used between the classes in MagnificationController, and
1302      * functions for tests to mock it.
1303      */
1304     @VisibleForTesting
1305     public static class ControllerContext {
1306         private final Context mContext;
1307         private final AccessibilityManagerService mAms;
1308         private final WindowManagerInternal mWindowManager;
1309         private final Handler mHandler;
1310         private final Long mAnimationDuration;
1311 
1312         /**
1313          * Constructor for ControllerContext.
1314          */
ControllerContext(@onNull Context context, @NonNull AccessibilityManagerService ams, @NonNull WindowManagerInternal windowManager, @NonNull Handler handler, long animationDuration)1315         public ControllerContext(@NonNull Context context,
1316                 @NonNull AccessibilityManagerService ams,
1317                 @NonNull WindowManagerInternal windowManager,
1318                 @NonNull Handler handler,
1319                 long animationDuration) {
1320             mContext = context;
1321             mAms = ams;
1322             mWindowManager = windowManager;
1323             mHandler = handler;
1324             mAnimationDuration = animationDuration;
1325         }
1326 
1327         /**
1328          * @return A context.
1329          */
1330         @NonNull
getContext()1331         public Context getContext() {
1332             return mContext;
1333         }
1334 
1335         /**
1336          * @return AccessibilityManagerService
1337          */
1338         @NonNull
getAms()1339         public AccessibilityManagerService getAms() {
1340             return mAms;
1341         }
1342 
1343         /**
1344          * @return WindowManagerInternal
1345          */
1346         @NonNull
getWindowManager()1347         public WindowManagerInternal getWindowManager() {
1348             return mWindowManager;
1349         }
1350 
1351         /**
1352          * @return Handler for main looper
1353          */
1354         @NonNull
getHandler()1355         public Handler getHandler() {
1356             return mHandler;
1357         }
1358 
1359         /**
1360          * Create a new ValueAnimator.
1361          *
1362          * @return ValueAnimator
1363          */
1364         @NonNull
newValueAnimator()1365         public ValueAnimator newValueAnimator() {
1366             return new ValueAnimator();
1367         }
1368 
1369         /**
1370          * Write Settings of magnification scale.
1371          */
putMagnificationScale(float value, int userId)1372         public void putMagnificationScale(float value, int userId) {
1373             Settings.Secure.putFloatForUser(mContext.getContentResolver(),
1374                     Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, value, userId);
1375         }
1376 
1377         /**
1378          * Get Settings of magnification scale.
1379          */
getMagnificationScale(int userId)1380         public float getMagnificationScale(int userId) {
1381             return Settings.Secure.getFloatForUser(mContext.getContentResolver(),
1382                     Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
1383                     DEFAULT_MAGNIFICATION_SCALE, userId);
1384         }
1385 
1386         /**
1387          * @return Configuration of animation duration.
1388          */
getAnimationDuration()1389         public long getAnimationDuration() {
1390             return mAnimationDuration;
1391         }
1392     }
1393 }
1394