• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.magnification;
18 
19 import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
20 import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
21 import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
22 
23 import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID;
24 import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID;
25 
26 import android.annotation.IntDef;
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.content.BroadcastReceiver;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.IntentFilter;
33 import android.graphics.PointF;
34 import android.graphics.Rect;
35 import android.graphics.Region;
36 import android.os.Binder;
37 import android.os.IBinder;
38 import android.os.RemoteException;
39 import android.os.SystemClock;
40 import android.util.Slog;
41 import android.util.SparseArray;
42 import android.util.SparseBooleanArray;
43 import android.view.MotionEvent;
44 import android.view.accessibility.IWindowMagnificationConnection;
45 import android.view.accessibility.IWindowMagnificationConnectionCallback;
46 import android.view.accessibility.MagnificationAnimationCallback;
47 
48 import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
49 import com.android.internal.annotations.GuardedBy;
50 import com.android.internal.annotations.VisibleForTesting;
51 import com.android.server.LocalServices;
52 import com.android.server.accessibility.AccessibilityTraceManager;
53 import com.android.server.statusbar.StatusBarManagerInternal;
54 import com.android.server.wm.WindowManagerInternal;
55 
56 import java.lang.annotation.Retention;
57 import java.lang.annotation.RetentionPolicy;
58 import java.util.concurrent.atomic.AtomicLongFieldUpdater;
59 
60 /**
61  * A class to manipulate window magnification through {@link WindowMagnificationConnectionWrapper}
62  * create by {@link #setConnection(IWindowMagnificationConnection)}. To set the connection with
63  * SysUI, call {@code StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)}.
64  * The applied magnification scale is constrained by
65  * {@link MagnificationScaleProvider#constrainScale(float)}
66  */
67 public class WindowMagnificationManager implements
68         PanningScalingHandler.MagnificationDelegate,
69         WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks {
70 
71     private static final boolean DBG = false;
72 
73     private static final String TAG = "WindowMagnificationMgr";
74 
75     /**
76      * Indicate that the magnification window is at the magnification center.
77      */
78     public static final int WINDOW_POSITION_AT_CENTER = 0;
79 
80     /**
81      * Indicate that the magnification window is at the top-left side of the magnification
82      * center. The offset is equal to a half of MirrorSurfaceView. So, the bottom-right corner
83      * of the window is at the magnification center.
84      */
85     public static final int WINDOW_POSITION_AT_TOP_LEFT = 1;
86 
87     @Retention(RetentionPolicy.SOURCE)
88     @IntDef(prefix = { "WINDOW_POSITION_AT_" }, value = {
89             WINDOW_POSITION_AT_CENTER,
90             WINDOW_POSITION_AT_TOP_LEFT
91     })
92     public @interface WindowPosition {}
93 
94     /** Window magnification connection is connecting. */
95     private static final int CONNECTING = 0;
96     /** Window magnification connection is connected. */
97     private static final int CONNECTED = 1;
98     /** Window magnification connection is disconnecting. */
99     private static final int DISCONNECTING = 2;
100     /** Window magnification connection is disconnected. */
101     private static final int DISCONNECTED = 3;
102 
103     @Retention(RetentionPolicy.SOURCE)
104     @IntDef(prefix = {"CONNECTION_STATE"}, value = {
105             CONNECTING,
106             CONNECTED,
107             DISCONNECTING,
108             DISCONNECTED
109     })
110     private @interface ConnectionState {
111     }
112 
connectionStateToString(@onnectionState int state)113     private static String connectionStateToString(@ConnectionState int state) {
114         switch (state) {
115             case CONNECTING: return "CONNECTING";
116             case CONNECTED: return "CONNECTED";
117             case DISCONNECTING: return "DISCONNECTING";
118             case DISCONNECTED: return "DISCONNECTED";
119             default:
120                 return "UNKNOWN:" + state;
121         }
122     }
123 
124     @ConnectionState
125     private int mConnectionState = DISCONNECTED;
126 
127     private static final int WAIT_CONNECTION_TIMEOUT_MILLIS = 100;
128 
129     private final Object mLock;
130     private final Context mContext;
131     @VisibleForTesting
132     @GuardedBy("mLock")
133     @Nullable
134     WindowMagnificationConnectionWrapper mConnectionWrapper;
135     @GuardedBy("mLock")
136     private ConnectionCallback mConnectionCallback;
137     @GuardedBy("mLock")
138     private SparseArray<WindowMagnifier> mWindowMagnifiers = new SparseArray<>();
139     // Whether the following typing focus feature for magnification is enabled.
140     private boolean mMagnificationFollowTypingEnabled = true;
141     @GuardedBy("mLock")
142     private final SparseBooleanArray mIsImeVisibleArray = new SparseBooleanArray();
143 
144     private boolean mReceiverRegistered = false;
145     @VisibleForTesting
146     protected final BroadcastReceiver mScreenStateReceiver = new BroadcastReceiver() {
147         @Override
148         public void onReceive(Context context, Intent intent) {
149             final int displayId = context.getDisplayId();
150             removeMagnificationButton(displayId);
151             disableWindowMagnification(displayId, false, null);
152         }
153     };
154 
155     /**
156      * Callback to handle magnification actions from system UI.
157      */
158     public interface Callback {
159 
160         /**
161          * Called when the accessibility action of scale requests to be performed.
162          * It is invoked from System UI. And the action is provided by the mirror window.
163          *
164          * @param displayId The logical display id.
165          * @param scale the target scale, or {@link Float#NaN} to leave unchanged
166          */
onPerformScaleAction(int displayId, float scale)167         void onPerformScaleAction(int displayId, float scale);
168 
169         /**
170          * Called when the accessibility action is performed.
171          *
172          * @param displayId The logical display id.
173          */
onAccessibilityActionPerformed(int displayId)174         void onAccessibilityActionPerformed(int displayId);
175 
176         /**
177          * Called when the state of the magnification activation is changed.
178          *
179          * @param displayId The logical display id.
180          * @param activated {@code true} if the magnification is activated, otherwise {@code false}.
181          */
onWindowMagnificationActivationState(int displayId, boolean activated)182         void onWindowMagnificationActivationState(int displayId, boolean activated);
183 
184         /**
185          * Called when the magnification source bounds are changed.
186          *
187          * @param displayId The logical display id.
188          * @param bounds    The magnified source bounds on the display.
189          */
onSourceBoundsChanged(int displayId, Rect bounds)190         void onSourceBoundsChanged(int displayId, Rect bounds);
191 
192         /**
193          * Called from {@link IWindowMagnificationConnection} to request changing the magnification
194          * mode on the given display.
195          *
196          * @param displayId the logical display id
197          * @param magnificationMode the target magnification mode
198          */
onChangeMagnificationMode(int displayId, int magnificationMode)199         void onChangeMagnificationMode(int displayId, int magnificationMode);
200     }
201 
202     private final Callback mCallback;
203     private final AccessibilityTraceManager mTrace;
204     private final MagnificationScaleProvider mScaleProvider;
205 
WindowMagnificationManager(Context context, Object lock, @NonNull Callback callback, AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider)206     public WindowMagnificationManager(Context context, Object lock, @NonNull Callback callback,
207             AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider) {
208         mContext = context;
209         mLock = lock;
210         mCallback = callback;
211         mTrace = trace;
212         mScaleProvider = scaleProvider;
213     }
214 
215     /**
216      * Sets {@link IWindowMagnificationConnection}.
217      *
218      * @param connection {@link IWindowMagnificationConnection}
219      */
setConnection(@ullable IWindowMagnificationConnection connection)220     public void setConnection(@Nullable IWindowMagnificationConnection connection) {
221         if (DBG) {
222             Slog.d(TAG, "setConnection :" + connection + ", mConnectionState="
223                     + connectionStateToString(mConnectionState));
224         }
225         synchronized (mLock) {
226             // Reset connectionWrapper.
227             if (mConnectionWrapper != null) {
228                 mConnectionWrapper.setConnectionCallback(null);
229                 if (mConnectionCallback != null) {
230                     mConnectionCallback.mExpiredDeathRecipient = true;
231                 }
232                 mConnectionWrapper.unlinkToDeath(mConnectionCallback);
233                 mConnectionWrapper = null;
234                 // The connection is still connecting so it is no need to reset the
235                 // connection state to disconnected.
236                 // TODO b/220086369 will reset the connection immediately when requestConnection
237                 //  is called
238                 if (mConnectionState != CONNECTING) {
239                     setConnectionState(DISCONNECTED);
240                 }
241             }
242             if (connection != null) {
243                 mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection, mTrace);
244             }
245 
246             if (mConnectionWrapper != null) {
247                 try {
248                     mConnectionCallback = new ConnectionCallback();
249                     mConnectionWrapper.linkToDeath(mConnectionCallback);
250                     mConnectionWrapper.setConnectionCallback(mConnectionCallback);
251                     setConnectionState(CONNECTED);
252                 } catch (RemoteException e) {
253                     Slog.e(TAG, "setConnection failed", e);
254                     mConnectionWrapper = null;
255                     setConnectionState(DISCONNECTED);
256                 } finally {
257                     mLock.notify();
258                 }
259             }
260         }
261     }
262 
263     /**
264      * @return {@code true} if {@link IWindowMagnificationConnection} is available
265      */
isConnected()266     public boolean isConnected() {
267         synchronized (mLock) {
268             return mConnectionWrapper != null;
269         }
270     }
271 
272     /**
273      * Requests {@link IWindowMagnificationConnection} through
274      * {@link StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)} and
275      * destroys all window magnifications if necessary.
276      *
277      * @param connect {@code true} if needs connection, otherwise set the connection to null and
278      *                destroy all window magnifications.
279      * @return {@code true} if {@link IWindowMagnificationConnection} state is going to change.
280      */
requestConnection(boolean connect)281     public boolean requestConnection(boolean connect) {
282         if (DBG) {
283             Slog.d(TAG, "requestConnection :" + connect);
284         }
285         if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
286             mTrace.logTrace(TAG + ".requestWindowMagnificationConnection",
287                     FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "connect=" + connect);
288         }
289         synchronized (mLock) {
290             if ((connect && (mConnectionState == CONNECTED || mConnectionState == CONNECTING))
291                     || (!connect && (mConnectionState == DISCONNECTED
292                     || mConnectionState == DISCONNECTING))) {
293                 Slog.w(TAG, "requestConnection duplicated request: connect=" + connect
294                         + ", mConnectionState=" + connectionStateToString(mConnectionState));
295                 return false;
296             }
297 
298             if (connect) {
299                 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
300                 if (!mReceiverRegistered) {
301                     mContext.registerReceiver(mScreenStateReceiver, intentFilter);
302                     mReceiverRegistered = true;
303                 }
304             } else {
305                 disableAllWindowMagnifiers();
306                 if (mReceiverRegistered) {
307                     mContext.unregisterReceiver(mScreenStateReceiver);
308                     mReceiverRegistered = false;
309                 }
310             }
311         }
312         if (requestConnectionInternal(connect)) {
313             setConnectionState(connect ? CONNECTING : DISCONNECTING);
314             return true;
315         } else {
316             setConnectionState(DISCONNECTED);
317             return false;
318         }
319     }
320 
requestConnectionInternal(boolean connect)321     private boolean requestConnectionInternal(boolean connect) {
322         final long identity = Binder.clearCallingIdentity();
323         try {
324             final StatusBarManagerInternal service = LocalServices.getService(
325                     StatusBarManagerInternal.class);
326             if (service != null) {
327                 return service.requestWindowMagnificationConnection(connect);
328             }
329         } finally {
330             Binder.restoreCallingIdentity(identity);
331         }
332         return false;
333     }
334 
335     /**
336      * Returns window magnification connection state.
337      */
getConnectionState()338     public String getConnectionState() {
339         return connectionStateToString(mConnectionState);
340     }
341 
setConnectionState(@onnectionState int state)342     private void setConnectionState(@ConnectionState int state) {
343         if (DBG) {
344             Slog.d(TAG, "setConnectionState : state=" + state + ", mConnectionState="
345                     + connectionStateToString(mConnectionState));
346         }
347         mConnectionState = state;
348     }
349 
350     /**
351      * Disables window magnifier on all displays without animation.
352      */
disableAllWindowMagnifiers()353     void disableAllWindowMagnifiers() {
354         synchronized (mLock) {
355             for (int i = 0; i < mWindowMagnifiers.size(); i++) {
356                 final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i);
357                 magnifier.disableWindowMagnificationInternal(null);
358             }
359             mWindowMagnifiers.clear();
360         }
361     }
362 
363     /**
364      * Resets the window magnifier on all displays that had been controlled by the
365      * specified service connection. Called when the service connection is unbound
366      * or binder died.
367      *
368      * @param connectionId The connection id
369      */
resetAllIfNeeded(int connectionId)370     public void resetAllIfNeeded(int connectionId) {
371         synchronized (mLock) {
372             for (int i = 0; i < mWindowMagnifiers.size(); i++) {
373                 final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i);
374                 if (magnifier != null
375                         && magnifier.mEnabled
376                         && connectionId == magnifier.getIdOfLastServiceToControl()) {
377                     magnifier.disableWindowMagnificationInternal(null);
378                 }
379             }
380         }
381     }
382 
resetWindowMagnifiers()383     private void resetWindowMagnifiers() {
384         synchronized (mLock) {
385             for (int i = 0; i < mWindowMagnifiers.size(); i++) {
386                 WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i);
387                 magnifier.reset();
388             }
389         }
390     }
391 
392     @Override
onRectangleOnScreenRequested(int displayId, int left, int top, int right, int bottom)393     public void onRectangleOnScreenRequested(int displayId, int left, int top, int right,
394             int bottom) {
395         if (!mMagnificationFollowTypingEnabled) {
396             return;
397         }
398 
399         float toCenterX = (float) (left + right) / 2;
400         float toCenterY = (float) (top + bottom) / 2;
401 
402         synchronized (mLock) {
403             if (mIsImeVisibleArray.get(displayId, false)
404                     && !isPositionInSourceBounds(displayId, toCenterX, toCenterY)
405                     && isTrackingTypingFocusEnabled(displayId)) {
406                 moveWindowMagnifierToPositionInternal(displayId, toCenterX, toCenterY,
407                         STUB_ANIMATION_CALLBACK);
408             }
409         }
410     }
411 
setMagnificationFollowTypingEnabled(boolean enabled)412     void setMagnificationFollowTypingEnabled(boolean enabled) {
413         mMagnificationFollowTypingEnabled = enabled;
414     }
415 
isMagnificationFollowTypingEnabled()416     boolean isMagnificationFollowTypingEnabled() {
417         return mMagnificationFollowTypingEnabled;
418     }
419 
420     /**
421      * Get the ID of the last service that changed the magnification config.
422      *
423      * @param displayId The logical display id.
424      * @return The id
425      */
getIdOfLastServiceToMagnify(int displayId)426     public int getIdOfLastServiceToMagnify(int displayId) {
427         synchronized (mLock) {
428             final WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
429             if (magnifier != null) {
430                 return magnifier.mIdOfLastServiceToControl;
431             }
432         }
433         return INVALID_SERVICE_ID;
434     }
435 
436     /**
437      * Enable or disable tracking typing focus for the specific magnification window.
438      *
439      * The tracking typing focus should be set to enabled with the following conditions:
440      * 1. IME is shown.
441      *
442      * The tracking typing focus should be set to disabled with the following conditions:
443      * 1. A user drags the magnification window by 1 finger.
444      * 2. A user scroll the magnification window by 2 fingers.
445      *
446      * @param displayId The logical display id.
447      * @param trackingTypingFocusEnabled Enabled or disable the function of tracking typing focus.
448      */
setTrackingTypingFocusEnabled(int displayId, boolean trackingTypingFocusEnabled)449     void setTrackingTypingFocusEnabled(int displayId, boolean trackingTypingFocusEnabled) {
450         synchronized (mLock) {
451             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
452             if (magnifier == null) {
453                 return;
454             }
455             magnifier.setTrackingTypingFocusEnabled(trackingTypingFocusEnabled);
456         }
457     }
458 
459     /**
460      * Enable tracking typing focus function for all magnifications.
461      */
enableAllTrackingTypingFocus()462     private void enableAllTrackingTypingFocus() {
463         synchronized (mLock) {
464             for (int i = 0; i < mWindowMagnifiers.size(); i++) {
465                 WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i);
466                 magnifier.setTrackingTypingFocusEnabled(true);
467             }
468         }
469     }
470 
pauseTrackingTypingFocusRecord(int displayId)471     private void pauseTrackingTypingFocusRecord(int displayId) {
472         WindowMagnifier magnifier;
473         synchronized (mLock) {
474             magnifier = mWindowMagnifiers.get(displayId);
475             if (magnifier == null) {
476                 return;
477             }
478         }
479         magnifier.pauseTrackingTypingFocusRecord();
480     }
481 
482     /**
483      * Called when the IME window visibility changed.
484      *
485      * @param shown {@code true} means the IME window shows on the screen. Otherwise, it's hidden.
486      */
onImeWindowVisibilityChanged(int displayId, boolean shown)487     void onImeWindowVisibilityChanged(int displayId, boolean shown) {
488         synchronized (mLock) {
489             mIsImeVisibleArray.put(displayId, shown);
490         }
491         if (shown) {
492             enableAllTrackingTypingFocus();
493         } else {
494             pauseTrackingTypingFocusRecord(displayId);
495         }
496     }
497 
isImeVisible(int displayId)498     boolean isImeVisible(int displayId) {
499         synchronized (mLock) {
500             return mIsImeVisibleArray.get(displayId);
501         }
502     }
503 
logTrackingTypingFocus(long duration)504     void logTrackingTypingFocus(long duration) {
505         AccessibilityStatsLogUtils.logMagnificationFollowTypingFocusSession(duration);
506     }
507 
508     @Override
processScroll(int displayId, float distanceX, float distanceY)509     public boolean processScroll(int displayId, float distanceX, float distanceY) {
510         moveWindowMagnification(displayId, -distanceX, -distanceY);
511         setTrackingTypingFocusEnabled(displayId, false);
512         return /* event consumed: */ true;
513     }
514 
515     /**
516      * Scales the magnified region on the specified display if window magnification is initiated.
517      *
518      * @param displayId The logical display id.
519      * @param scale The target scale, must be >= 1
520      */
521     @Override
setScale(int displayId, float scale)522     public void setScale(int displayId, float scale) {
523         synchronized (mLock) {
524             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
525             if (magnifier == null) {
526                 return;
527             }
528             magnifier.setScale(scale);
529         }
530     }
531 
532     /**
533      * Enables window magnification with specified center and scale on the given display and
534      * animating the transition.
535      *
536      * @param displayId The logical display id.
537      * @param scale The target scale, must be >= 1.
538      * @param centerX The screen-relative X coordinate around which to center,
539      *                or {@link Float#NaN} to leave unchanged.
540      * @param centerY The screen-relative Y coordinate around which to center,
541      *                or {@link Float#NaN} to leave unchanged.
542      * @return {@code true} if the magnification is enabled successfully.
543      */
enableWindowMagnification(int displayId, float scale, float centerX, float centerY)544     public boolean enableWindowMagnification(int displayId, float scale, float centerX,
545             float centerY) {
546         return enableWindowMagnification(displayId, scale, centerX, centerY,
547                 STUB_ANIMATION_CALLBACK, MAGNIFICATION_GESTURE_HANDLER_ID);
548     }
549 
550     /**
551      * Enables window magnification with specified center and scale on the given display and
552      * animating the transition.
553      *
554      * @param displayId The logical display id.
555      * @param scale The target scale, must be >= 1.
556      * @param centerX The screen-relative X coordinate around which to center for magnification,
557      *                or {@link Float#NaN} to leave unchanged.
558      * @param centerY The screen-relative Y coordinate around which to center for magnification,
559      *                or {@link Float#NaN} to leave unchanged.
560      * @param animationCallback Called when the animation result is valid.
561      * @param id The connection ID
562      * @return {@code true} if the magnification is enabled successfully.
563      */
enableWindowMagnification(int displayId, float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback animationCallback, int id)564     public boolean enableWindowMagnification(int displayId, float scale, float centerX,
565             float centerY, @Nullable MagnificationAnimationCallback animationCallback, int id) {
566         return enableWindowMagnification(displayId, scale, centerX, centerY, animationCallback,
567                 WINDOW_POSITION_AT_CENTER, id);
568     }
569 
570     /**
571      * Enables window magnification with specified center and scale on the given display and
572      * animating the transition.
573      *
574      * @param displayId The logical display id.
575      * @param scale The target scale, must be >= 1.
576      * @param centerX The screen-relative X coordinate around which to center for magnification,
577      *                or {@link Float#NaN} to leave unchanged.
578      * @param centerY The screen-relative Y coordinate around which to center for magnification,
579      *                or {@link Float#NaN} to leave unchanged.
580      * @param windowPosition Indicate the offset between window position and (centerX, centerY).
581      * @return {@code true} if the magnification is enabled successfully.
582      */
enableWindowMagnification(int displayId, float scale, float centerX, float centerY, @WindowPosition int windowPosition)583     public boolean enableWindowMagnification(int displayId, float scale, float centerX,
584             float centerY, @WindowPosition int windowPosition) {
585         return enableWindowMagnification(displayId, scale, centerX, centerY,
586                 STUB_ANIMATION_CALLBACK, windowPosition, MAGNIFICATION_GESTURE_HANDLER_ID);
587     }
588 
589     /**
590      * Enables window magnification with specified center and scale on the given display and
591      * animating the transition.
592      *
593      * @param displayId         The logical display id.
594      * @param scale             The target scale, must be >= 1.
595      * @param centerX           The screen-relative X coordinate around which to center for
596      *                          magnification, or {@link Float#NaN} to leave unchanged.
597      * @param centerY           The screen-relative Y coordinate around which to center for
598      *                          magnification, or {@link Float#NaN} to leave unchanged.
599      * @param animationCallback Called when the animation result is valid.
600      * @param windowPosition    Indicate the offset between window position and (centerX, centerY).
601      * @return {@code true} if the magnification is enabled successfully.
602      */
enableWindowMagnification(int displayId, float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback animationCallback, @WindowPosition int windowPosition, int id)603     public boolean enableWindowMagnification(int displayId, float scale, float centerX,
604             float centerY, @Nullable MagnificationAnimationCallback animationCallback,
605             @WindowPosition int windowPosition, int id) {
606         final boolean enabled;
607         boolean previousEnabled;
608         synchronized (mLock) {
609             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
610             if (magnifier == null) {
611                 magnifier = createWindowMagnifier(displayId);
612             }
613             previousEnabled = magnifier.mEnabled;
614             enabled = magnifier.enableWindowMagnificationInternal(scale, centerX, centerY,
615                     animationCallback, windowPosition, id);
616         }
617 
618         if (enabled) {
619             setTrackingTypingFocusEnabled(displayId, true);
620             if (!previousEnabled) {
621                 mCallback.onWindowMagnificationActivationState(displayId, true);
622             }
623         }
624         return enabled;
625     }
626 
627     /**
628      * Disables window magnification on the given display.
629      *
630      * @param displayId The logical display id.
631      * @param clear {@true} Clears the state of window magnification.
632      * @return {@code true} if the magnification is turned to be disabled successfully
633      */
disableWindowMagnification(int displayId, boolean clear)634     boolean disableWindowMagnification(int displayId, boolean clear) {
635         return disableWindowMagnification(displayId, clear, STUB_ANIMATION_CALLBACK);
636     }
637 
638     /**
639      * Disables window magnification on the specified display and animating the transition.
640      *
641      * @param displayId The logical display id.
642      * @param clear {@true} Clears the state of window magnification.
643      * @param animationCallback Called when the animation result is valid.
644      * @return {@code true} if the magnification is turned to be disabled successfully
645      */
disableWindowMagnification(int displayId, boolean clear, MagnificationAnimationCallback animationCallback)646     public boolean disableWindowMagnification(int displayId, boolean clear,
647             MagnificationAnimationCallback animationCallback) {
648         final boolean disabled;
649         synchronized (mLock) {
650             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
651             if (magnifier == null) {
652                 return false;
653             }
654 
655             disabled = magnifier.disableWindowMagnificationInternal(animationCallback);
656             if (clear) {
657                 mWindowMagnifiers.delete(displayId);
658             }
659         }
660 
661         if (disabled) {
662             mCallback.onWindowMagnificationActivationState(displayId, false);
663         }
664         return disabled;
665     }
666 
667     /**
668      * Calculates the number of fingers in the window.
669      *
670      * @param displayId The logical display id.
671      * @param motionEvent The motion event
672      * @return the number of fingers in the window.
673      */
pointersInWindow(int displayId, MotionEvent motionEvent)674     int pointersInWindow(int displayId, MotionEvent motionEvent) {
675         synchronized (mLock) {
676             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
677             if (magnifier == null) {
678                 return 0;
679             }
680             return magnifier.pointersInWindow(motionEvent);
681         }
682     }
683 
684     @GuardedBy("mLock")
isPositionInSourceBounds(int displayId, float x, float y)685     boolean isPositionInSourceBounds(int displayId, float x, float y) {
686         WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
687         if (magnifier == null) {
688             return false;
689         }
690         return magnifier.isPositionInSourceBounds(x, y);
691     }
692 
693     /**
694      * Indicates whether window magnification is enabled on specified display.
695      *
696      * @param displayId The logical display id.
697      * @return {@code true} if the window magnification is enabled.
698      */
699     @VisibleForTesting
isWindowMagnifierEnabled(int displayId)700     public boolean isWindowMagnifierEnabled(int displayId) {
701         synchronized (mLock) {
702             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
703             if (magnifier == null) {
704                 return false;
705             }
706             return magnifier.isEnabled();
707         }
708     }
709 
710     /**
711      * Retrieves a previously magnification scale from the current
712      * user's settings. Only the value of the default display is persisted.
713      *
714      * @return the previously magnification scale, or the default
715      *         scale if none is available
716      */
getPersistedScale(int displayId)717     float getPersistedScale(int displayId) {
718         return mScaleProvider.getScale(displayId);
719     }
720 
721     /**
722      * Persists the default display magnification scale to the current user's settings. Only the
723      * value of the default display is persisted in user's settings.
724      */
persistScale(int displayId)725     void persistScale(int displayId) {
726         float scale = getScale(displayId);
727         if (scale != 1.0f) {
728             mScaleProvider.putScale(scale, displayId);
729         }
730     }
731 
732     /**
733      * Returns the magnification scale.
734      *
735      * @param displayId The logical display id.
736      * @return the scale
737      */
getScale(int displayId)738     public float getScale(int displayId) {
739         synchronized (mLock) {
740             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
741             if (magnifier == null || !magnifier.mEnabled) {
742                 return 1.0f;
743             }
744             return magnifier.getScale();
745         }
746     }
747 
748     /**
749      * Moves window magnification on the specified display with the specified offset.
750      *
751      * @param displayId The logical display id.
752      * @param offsetX the amount in pixels to offset the region in the X direction, in current
753      *                screen pixels.
754      * @param offsetY the amount in pixels to offset the region in the Y direction, in current
755      *                screen pixels.
756      */
moveWindowMagnification(int displayId, float offsetX, float offsetY)757     void moveWindowMagnification(int displayId, float offsetX, float offsetY) {
758         synchronized (mLock) {
759             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
760             if (magnifier == null) {
761                 return;
762             }
763             magnifier.move(offsetX, offsetY);
764         }
765     }
766 
767     /**
768      * Requests System UI show magnification mode button UI on the specified display.
769      *
770      * @param displayId The logical display id.
771      * @param magnificationMode the current magnification mode.
772      * @return {@code true} if the event was handled, {@code false} otherwise
773      */
showMagnificationButton(int displayId, int magnificationMode)774     public boolean showMagnificationButton(int displayId, int magnificationMode) {
775         return mConnectionWrapper != null && mConnectionWrapper.showMagnificationButton(
776                 displayId, magnificationMode);
777     }
778 
779     /**
780      * Requests System UI remove magnification mode button UI on the specified display.
781      *
782      * @param displayId The logical display id.
783      * @return {@code true} if the event was handled, {@code false} otherwise
784      */
removeMagnificationButton(int displayId)785     public boolean removeMagnificationButton(int displayId) {
786         return mConnectionWrapper != null && mConnectionWrapper.removeMagnificationButton(
787                 displayId);
788     }
789 
790     /**
791      * Returns the screen-relative X coordinate of the center of the magnified bounds.
792      *
793      * @param displayId The logical display id
794      * @return the X coordinate. {@link Float#NaN} if the window magnification is not enabled.
795      */
getCenterX(int displayId)796     public float getCenterX(int displayId) {
797         synchronized (mLock) {
798             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
799             if (magnifier == null || !magnifier.mEnabled) {
800                 return Float.NaN;
801             }
802             return magnifier.getCenterX();
803         }
804     }
805 
806     /**
807      * Returns the screen-relative Y coordinate of the center of the magnified bounds.
808      *
809      * @param displayId The logical display id
810      * @return the Y coordinate. {@link Float#NaN} if the window magnification is not enabled.
811      */
getCenterY(int displayId)812     public float getCenterY(int displayId) {
813         synchronized (mLock) {
814             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
815             if (magnifier == null || !magnifier.mEnabled) {
816                 return Float.NaN;
817             }
818             return magnifier.getCenterY();
819         }
820     }
821 
isTrackingTypingFocusEnabled(int displayId)822     boolean isTrackingTypingFocusEnabled(int displayId) {
823         synchronized (mLock) {
824             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
825             if (magnifier == null) {
826                 return false;
827             }
828             return magnifier.isTrackingTypingFocusEnabled();
829         }
830     }
831 
832     /**
833      * Populates magnified bounds on the screen. And the populated magnified bounds would be
834      * empty If window magnifier is not activated.
835      *
836      * @param displayId The logical display id.
837      * @param outRegion the region to populate
838      */
getMagnificationSourceBounds(int displayId, @NonNull Region outRegion)839     public void getMagnificationSourceBounds(int displayId, @NonNull Region outRegion) {
840         synchronized (mLock) {
841             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
842             if (magnifier == null || !magnifier.mEnabled) {
843                 outRegion.setEmpty();
844             } else {
845                 outRegion.set(magnifier.mSourceBounds);
846             }
847         }
848     }
849 
850     /**
851      * Creates the windowMagnifier based on the specified display and stores it.
852      *
853      * @param displayId logical display id.
854      */
855     @GuardedBy("mLock")
createWindowMagnifier(int displayId)856     private WindowMagnifier createWindowMagnifier(int displayId) {
857         final WindowMagnifier magnifier = new WindowMagnifier(displayId, this);
858         mWindowMagnifiers.put(displayId, magnifier);
859         return magnifier;
860     }
861 
862     /**
863      * Removes the window magnifier with given id.
864      *
865      * @param displayId The logical display id.
866      */
onDisplayRemoved(int displayId)867     public void onDisplayRemoved(int displayId) {
868         disableWindowMagnification(displayId, true);
869     }
870 
871     private class ConnectionCallback extends IWindowMagnificationConnectionCallback.Stub implements
872             IBinder.DeathRecipient {
873         private boolean mExpiredDeathRecipient = false;
874 
875         @Override
onWindowMagnifierBoundsChanged(int displayId, Rect bounds)876         public void onWindowMagnifierBoundsChanged(int displayId, Rect bounds) {
877             if (mTrace.isA11yTracingEnabledForTypes(
878                     FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
879                 mTrace.logTrace(TAG + "ConnectionCallback.onWindowMagnifierBoundsChanged",
880                         FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
881                         "displayId=" + displayId + ";bounds=" + bounds);
882             }
883             synchronized (mLock) {
884                 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
885                 if (magnifier == null) {
886                     magnifier = createWindowMagnifier(displayId);
887                 }
888                 if (DBG) {
889                     Slog.i(TAG,
890                             "onWindowMagnifierBoundsChanged -" + displayId + " bounds = " + bounds);
891                 }
892                 magnifier.setMagnifierLocation(bounds);
893             }
894         }
895 
896         @Override
onChangeMagnificationMode(int displayId, int magnificationMode)897         public void onChangeMagnificationMode(int displayId, int magnificationMode)
898                 throws RemoteException {
899             if (mTrace.isA11yTracingEnabledForTypes(
900                     FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
901                 mTrace.logTrace(TAG + "ConnectionCallback.onChangeMagnificationMode",
902                         FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
903                         "displayId=" + displayId + ";mode=" + magnificationMode);
904             }
905             mCallback.onChangeMagnificationMode(displayId, magnificationMode);
906         }
907 
908         @Override
onSourceBoundsChanged(int displayId, Rect sourceBounds)909         public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
910             if (mTrace.isA11yTracingEnabledForTypes(
911                     FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
912                 mTrace.logTrace(TAG + "ConnectionCallback.onSourceBoundsChanged",
913                         FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
914                         "displayId=" + displayId + ";source=" + sourceBounds);
915             }
916             synchronized (mLock) {
917                 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
918                 if (magnifier == null) {
919                     magnifier = createWindowMagnifier(displayId);
920                 }
921                 magnifier.onSourceBoundsChanged(sourceBounds);
922             }
923             mCallback.onSourceBoundsChanged(displayId, sourceBounds);
924         }
925 
926         @Override
onPerformScaleAction(int displayId, float scale)927         public void onPerformScaleAction(int displayId, float scale) {
928             if (mTrace.isA11yTracingEnabledForTypes(
929                     FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
930                 mTrace.logTrace(TAG + "ConnectionCallback.onPerformScaleAction",
931                         FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
932                         "displayId=" + displayId + ";scale=" + scale);
933             }
934             mCallback.onPerformScaleAction(displayId, scale);
935         }
936 
937         @Override
onAccessibilityActionPerformed(int displayId)938         public void onAccessibilityActionPerformed(int displayId) {
939             if (mTrace.isA11yTracingEnabledForTypes(
940                     FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
941                 mTrace.logTrace(TAG + "ConnectionCallback.onAccessibilityActionPerformed",
942                         FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
943                         "displayId=" + displayId);
944             }
945             mCallback.onAccessibilityActionPerformed(displayId);
946         }
947 
948         @Override
onMove(int displayId)949         public void onMove(int displayId) {
950             if (mTrace.isA11yTracingEnabledForTypes(
951                     FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
952                 mTrace.logTrace(TAG + "ConnectionCallback.onMove",
953                         FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
954                         "displayId=" + displayId);
955             }
956             setTrackingTypingFocusEnabled(displayId, false);
957         }
958 
959         @Override
binderDied()960         public void binderDied() {
961             synchronized (mLock) {
962                 Slog.w(TAG, "binderDied DeathRecipient :" + mExpiredDeathRecipient);
963                 if (mExpiredDeathRecipient) {
964                     return;
965                 }
966                 mConnectionWrapper.unlinkToDeath(this);
967                 mConnectionWrapper = null;
968                 mConnectionCallback = null;
969                 setConnectionState(DISCONNECTED);
970                 resetWindowMagnifiers();
971             }
972         }
973     }
974 
975     /**
976      * A class manipulates window magnification per display and contains the magnification
977      * information.
978      * <p>
979      * This class requires to hold the lock when controlling the magnifier.
980      * </p>
981      */
982     private static class WindowMagnifier {
983 
984         private final int mDisplayId;
985         private float mScale = MagnificationScaleProvider.MIN_SCALE;
986         private boolean mEnabled;
987 
988         private final WindowMagnificationManager mWindowMagnificationManager;
989         // Records the bounds of window magnification.
990         private final Rect mBounds = new Rect();
991         // The magnified bounds on the screen.
992         private final Rect mSourceBounds = new Rect();
993 
994         private int mIdOfLastServiceToControl = INVALID_SERVICE_ID;
995 
996         private final PointF mMagnificationFrameOffsetRatio = new PointF(0f, 0f);
997 
998         private boolean mTrackingTypingFocusEnabled = true;
999 
1000         private volatile long mTrackingTypingFocusStartTime = 0;
1001         private static final AtomicLongFieldUpdater<WindowMagnifier> SUM_TIME_UPDATER =
1002                 AtomicLongFieldUpdater.newUpdater(WindowMagnifier.class,
1003                         "mTrackingTypingFocusSumTime");
1004         private volatile long mTrackingTypingFocusSumTime = 0;
1005 
WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager)1006         WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
1007             mDisplayId = displayId;
1008             mWindowMagnificationManager = windowMagnificationManager;
1009         }
1010 
enableWindowMagnificationInternal(float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback animationCallback, @WindowPosition int windowPosition, int id)1011         boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY,
1012                 @Nullable MagnificationAnimationCallback animationCallback,
1013                 @WindowPosition int windowPosition, int id) {
1014             // Handle defaults. The scale may be NAN when just updating magnification center.
1015             if (Float.isNaN(scale)) {
1016                 scale = getScale();
1017             }
1018             final float normScale = MagnificationScaleProvider.constrainScale(scale);
1019             setMagnificationFrameOffsetRatioByWindowPosition(windowPosition);
1020             if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
1021                     centerX, centerY, mMagnificationFrameOffsetRatio.x,
1022                     mMagnificationFrameOffsetRatio.y, animationCallback)) {
1023                 mScale = normScale;
1024                 mEnabled = true;
1025                 mIdOfLastServiceToControl = id;
1026                 return true;
1027             }
1028             return false;
1029         }
1030 
setMagnificationFrameOffsetRatioByWindowPosition(@indowPosition int windowPosition)1031         void setMagnificationFrameOffsetRatioByWindowPosition(@WindowPosition int windowPosition) {
1032             switch (windowPosition) {
1033                 case WINDOW_POSITION_AT_CENTER: {
1034                     mMagnificationFrameOffsetRatio.set(0f, 0f);
1035                 }
1036                 break;
1037                 case WINDOW_POSITION_AT_TOP_LEFT: {
1038                     mMagnificationFrameOffsetRatio.set(-1f, -1f);
1039                 }
1040                 break;
1041             }
1042         }
1043 
disableWindowMagnificationInternal( @ullable MagnificationAnimationCallback animationResultCallback)1044         boolean disableWindowMagnificationInternal(
1045                 @Nullable MagnificationAnimationCallback animationResultCallback) {
1046             if (!mEnabled) {
1047                 return false;
1048             }
1049             if (mWindowMagnificationManager.disableWindowMagnificationInternal(
1050                     mDisplayId, animationResultCallback)) {
1051                 mEnabled = false;
1052                 mIdOfLastServiceToControl = INVALID_SERVICE_ID;
1053                 mTrackingTypingFocusEnabled = false;
1054                 pauseTrackingTypingFocusRecord();
1055                 return true;
1056             }
1057             return false;
1058         }
1059 
1060         @GuardedBy("mLock")
setScale(float scale)1061         void setScale(float scale) {
1062             if (!mEnabled) {
1063                 return;
1064             }
1065             final float normScale = MagnificationScaleProvider.constrainScale(scale);
1066             if (Float.compare(mScale, normScale) != 0
1067                     && mWindowMagnificationManager.setScaleInternal(mDisplayId, scale)) {
1068                 mScale = normScale;
1069             }
1070         }
1071 
1072         @GuardedBy("mLock")
getScale()1073         float getScale() {
1074             return mScale;
1075         }
1076 
1077         @GuardedBy("mLock")
setMagnifierLocation(Rect rect)1078         void setMagnifierLocation(Rect rect) {
1079             mBounds.set(rect);
1080         }
1081 
1082         /**
1083          * Returns the ID of the last service that changed the magnification config.
1084          */
getIdOfLastServiceToControl()1085         int getIdOfLastServiceToControl() {
1086             return mIdOfLastServiceToControl;
1087         }
1088 
pointersInWindow(MotionEvent motionEvent)1089         int pointersInWindow(MotionEvent motionEvent) {
1090             int count = 0;
1091             final int pointerCount = motionEvent.getPointerCount();
1092             for (int i = 0; i < pointerCount; i++) {
1093                 final float x = motionEvent.getX(i);
1094                 final float y = motionEvent.getY(i);
1095                 if (mBounds.contains((int) x, (int) y)) {
1096                     count++;
1097                 }
1098             }
1099             return count;
1100         }
1101 
isPositionInSourceBounds(float x, float y)1102         boolean isPositionInSourceBounds(float x, float y) {
1103             return mSourceBounds.contains((int) x, (int) y);
1104         }
1105 
setTrackingTypingFocusEnabled(boolean trackingTypingFocusEnabled)1106         void setTrackingTypingFocusEnabled(boolean trackingTypingFocusEnabled) {
1107             if (mWindowMagnificationManager.isWindowMagnifierEnabled(mDisplayId)
1108                     && mWindowMagnificationManager.isImeVisible(mDisplayId)
1109                     && trackingTypingFocusEnabled) {
1110                 startTrackingTypingFocusRecord();
1111             }
1112             if (mTrackingTypingFocusEnabled && !trackingTypingFocusEnabled) {
1113                 stopAndLogTrackingTypingFocusRecordIfNeeded();
1114             }
1115             mTrackingTypingFocusEnabled = trackingTypingFocusEnabled;
1116         }
1117 
isTrackingTypingFocusEnabled()1118         boolean isTrackingTypingFocusEnabled() {
1119             return mTrackingTypingFocusEnabled;
1120         }
1121 
startTrackingTypingFocusRecord()1122         void startTrackingTypingFocusRecord() {
1123             if (mTrackingTypingFocusStartTime == 0) {
1124                 mTrackingTypingFocusStartTime = SystemClock.uptimeMillis();
1125                 if (DBG) {
1126                     Slog.d(TAG, "start: mTrackingTypingFocusStartTime = "
1127                             + mTrackingTypingFocusStartTime);
1128                 }
1129             }
1130         }
1131 
pauseTrackingTypingFocusRecord()1132         void pauseTrackingTypingFocusRecord() {
1133             if (mTrackingTypingFocusStartTime != 0) {
1134                 final long elapsed = (SystemClock.uptimeMillis() - mTrackingTypingFocusStartTime);
1135                 // update mTrackingTypingFocusSumTime value in an atomic operation
1136                 SUM_TIME_UPDATER.addAndGet(this, elapsed);
1137                 mTrackingTypingFocusStartTime = 0;
1138                 if (DBG) {
1139                     Slog.d(TAG, "pause: mTrackingTypingFocusSumTime = "
1140                             + mTrackingTypingFocusSumTime + ", elapsed = " + elapsed);
1141                 }
1142             }
1143         }
1144 
stopAndLogTrackingTypingFocusRecordIfNeeded()1145         void stopAndLogTrackingTypingFocusRecordIfNeeded() {
1146             if (mTrackingTypingFocusStartTime != 0 || mTrackingTypingFocusSumTime != 0) {
1147                 final long elapsed = mTrackingTypingFocusStartTime != 0
1148                         ? (SystemClock.uptimeMillis() - mTrackingTypingFocusStartTime) : 0;
1149                 final long duration = mTrackingTypingFocusSumTime + elapsed;
1150                 if (DBG) {
1151                     Slog.d(TAG, "stop and log: session duration = " + duration
1152                             + ", elapsed = " + elapsed);
1153                 }
1154                 mWindowMagnificationManager.logTrackingTypingFocus(duration);
1155                 mTrackingTypingFocusStartTime = 0;
1156                 mTrackingTypingFocusSumTime = 0;
1157             }
1158         }
1159 
isEnabled()1160         boolean isEnabled() {
1161             return mEnabled;
1162         }
1163 
1164         @GuardedBy("mLock")
move(float offsetX, float offsetY)1165         void move(float offsetX, float offsetY) {
1166             mWindowMagnificationManager.moveWindowMagnifierInternal(mDisplayId, offsetX, offsetY);
1167         }
1168 
1169         @GuardedBy("mLock")
reset()1170         void reset() {
1171             mEnabled = false;
1172             mIdOfLastServiceToControl = INVALID_SERVICE_ID;
1173             mSourceBounds.setEmpty();
1174         }
1175 
1176         @GuardedBy("mLock")
onSourceBoundsChanged(Rect sourceBounds)1177         public void onSourceBoundsChanged(Rect sourceBounds) {
1178             mSourceBounds.set(sourceBounds);
1179         }
1180 
1181         @GuardedBy("mLock")
getCenterX()1182         float getCenterX() {
1183             return mSourceBounds.exactCenterX();
1184         }
1185 
1186         @GuardedBy("mLock")
getCenterY()1187         float getCenterY() {
1188             return mSourceBounds.exactCenterY();
1189         }
1190     }
1191 
1192     @GuardedBy("mLock")
enableWindowMagnificationInternal(int displayId, float scale, float centerX, float centerY, float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY, MagnificationAnimationCallback animationCallback)1193     private boolean enableWindowMagnificationInternal(int displayId, float scale, float centerX,
1194             float centerY, float magnificationFrameOffsetRatioX,
1195             float magnificationFrameOffsetRatioY,
1196             MagnificationAnimationCallback animationCallback) {
1197         // Wait for the connection with a timeout.
1198         final long endMillis = SystemClock.uptimeMillis() + WAIT_CONNECTION_TIMEOUT_MILLIS;
1199         while (mConnectionState == CONNECTING && (SystemClock.uptimeMillis() < endMillis)) {
1200             try {
1201                 mLock.wait(endMillis - SystemClock.uptimeMillis());
1202             } catch (InterruptedException ie) {
1203                 /* ignore */
1204             }
1205         }
1206         if (mConnectionWrapper == null) {
1207             Slog.w(TAG,
1208                     "enableWindowMagnificationInternal mConnectionWrapper is null. "
1209                             + "mConnectionState=" + connectionStateToString(mConnectionState));
1210             return false;
1211         }
1212         return mConnectionWrapper.enableWindowMagnification(
1213                 displayId, scale, centerX, centerY,
1214                 magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY,
1215                 animationCallback);
1216     }
1217 
setScaleInternal(int displayId, float scale)1218     private boolean setScaleInternal(int displayId, float scale) {
1219         return mConnectionWrapper != null && mConnectionWrapper.setScale(displayId, scale);
1220     }
1221 
1222     @GuardedBy("mLock")
disableWindowMagnificationInternal(int displayId, MagnificationAnimationCallback animationCallback)1223     private boolean disableWindowMagnificationInternal(int displayId,
1224             MagnificationAnimationCallback animationCallback) {
1225         if (mConnectionWrapper == null) {
1226             Slog.w(TAG, "mConnectionWrapper is null");
1227             return false;
1228         }
1229         return mConnectionWrapper.disableWindowMagnification(
1230                 displayId, animationCallback);
1231     }
1232 
1233     @GuardedBy("mLock")
moveWindowMagnifierInternal(int displayId, float offsetX, float offsetY)1234     private boolean moveWindowMagnifierInternal(int displayId, float offsetX, float offsetY) {
1235         return mConnectionWrapper != null && mConnectionWrapper.moveWindowMagnifier(
1236                 displayId, offsetX, offsetY);
1237     }
1238 
1239     @GuardedBy("mLock")
moveWindowMagnifierToPositionInternal(int displayId, float positionX, float positionY, MagnificationAnimationCallback animationCallback)1240     private boolean moveWindowMagnifierToPositionInternal(int displayId, float positionX,
1241             float positionY, MagnificationAnimationCallback animationCallback) {
1242         return mConnectionWrapper != null && mConnectionWrapper.moveWindowMagnifierToPosition(
1243                 displayId, positionX, positionY, animationCallback);
1244     }
1245 }
1246