1 /*
2  * Copyright (C) 2014 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.wm;
18 
19 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
20 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
21 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
22 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
23 
24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
25 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
26 import static com.android.server.wm.utils.RegionUtils.forEachRect;
27 
28 import android.animation.ObjectAnimator;
29 import android.animation.ValueAnimator;
30 import android.annotation.NonNull;
31 import android.content.Context;
32 import android.graphics.Canvas;
33 import android.graphics.Color;
34 import android.graphics.Matrix;
35 import android.graphics.Paint;
36 import android.graphics.Path;
37 import android.graphics.PixelFormat;
38 import android.graphics.Point;
39 import android.graphics.PorterDuff.Mode;
40 import android.graphics.Rect;
41 import android.graphics.RectF;
42 import android.graphics.Region;
43 import android.os.Handler;
44 import android.os.IBinder;
45 import android.os.Looper;
46 import android.os.Message;
47 import android.util.ArraySet;
48 import android.util.IntArray;
49 import android.util.Slog;
50 import android.util.SparseArray;
51 import android.util.TypedValue;
52 import android.view.Display;
53 import android.view.InsetsSource;
54 import android.view.MagnificationSpec;
55 import android.view.Surface;
56 import android.view.Surface.OutOfResourcesException;
57 import android.view.SurfaceControl;
58 import android.view.ViewConfiguration;
59 import android.view.WindowInfo;
60 import android.view.WindowManager;
61 import android.view.animation.DecelerateInterpolator;
62 import android.view.animation.Interpolator;
63 
64 import com.android.internal.R;
65 import com.android.internal.os.SomeArgs;
66 import com.android.server.policy.WindowManagerPolicy;
67 import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
68 import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
69 
70 import java.io.PrintWriter;
71 import java.util.ArrayList;
72 import java.util.HashSet;
73 import java.util.List;
74 import java.util.Set;
75 
76 /**
77  * This class contains the accessibility related logic of the window manager.
78  */
79 final class AccessibilityController {
80 
81     private final WindowManagerService mService;
82 
83     private static final Rect EMPTY_RECT = new Rect();
84     private static final float[] sTempFloats = new float[9];
85 
AccessibilityController(WindowManagerService service)86     public AccessibilityController(WindowManagerService service) {
87         mService = service;
88     }
89 
90     private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
91 
92     private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver =
93             new SparseArray<>();
94 
setMagnificationCallbacksLocked(int displayId, MagnificationCallbacks callbacks)95     public boolean setMagnificationCallbacksLocked(int displayId,
96             MagnificationCallbacks callbacks) {
97         boolean result = false;
98         if (callbacks != null) {
99             if (mDisplayMagnifiers.get(displayId) != null) {
100                 throw new IllegalStateException("Magnification callbacks already set!");
101             }
102             final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
103             if (dc != null) {
104                 final Display display = dc.getDisplay();
105                 if (display != null && display.getType() != Display.TYPE_OVERLAY) {
106                     mDisplayMagnifiers.put(displayId, new DisplayMagnifier(
107                             mService, dc, display, callbacks));
108                     result = true;
109                 }
110             }
111         } else {
112             final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
113             if  (displayMagnifier == null) {
114                 throw new IllegalStateException("Magnification callbacks already cleared!");
115             }
116             displayMagnifier.destroyLocked();
117             mDisplayMagnifiers.remove(displayId);
118             result = true;
119         }
120         return result;
121     }
122 
123     /**
124      * Sets a callback for observing which windows are touchable for the purposes
125      * of accessibility on specified display.
126      *
127      * @param displayId The logical display id.
128      * @param callback The callback.
129      * @return {@code false} if display id is not valid or an embedded display.
130      */
setWindowsForAccessibilityCallbackLocked(int displayId, WindowsForAccessibilityCallback callback)131     public boolean setWindowsForAccessibilityCallbackLocked(int displayId,
132             WindowsForAccessibilityCallback callback) {
133         final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
134         if (dc == null) {
135             return false;
136         }
137 
138         if (callback != null) {
139             if (isEmbeddedDisplay(dc)) {
140                 // If this display is an embedded one, its window observer should have been set from
141                 // window manager after setting its parent window. But if its window observer is
142                 // empty, that means this mapping didn't be set, and needs to do this again.
143                 // This happened when accessibility window observer is disabled and enabled again.
144                 if (mWindowsForAccessibilityObserver.get(displayId) == null) {
145                     handleWindowObserverOfEmbeddedDisplayLocked(displayId, dc.getParentWindow());
146                 }
147                 return false;
148             } else if (mWindowsForAccessibilityObserver.get(displayId) != null) {
149                 throw new IllegalStateException(
150                         "Windows for accessibility callback of display "
151                                 + displayId + " already set!");
152             }
153             mWindowsForAccessibilityObserver.put(displayId,
154                     new WindowsForAccessibilityObserver(mService, displayId, callback));
155         } else {
156             if (isEmbeddedDisplay(dc)) {
157                 // If this display is an embedded one, its window observer should be removed along
158                 // with the window observer of its parent display removed because the window
159                 // observer of the embedded display and its parent display is the same, and would
160                 // be removed together when stopping the window tracking of its parent display. So
161                 // here don't need to do removing window observer of the embedded display again.
162                 return true;
163             }
164             final WindowsForAccessibilityObserver windowsForA11yObserver =
165                     mWindowsForAccessibilityObserver.get(displayId);
166             if (windowsForA11yObserver == null) {
167                 throw new IllegalStateException(
168                         "Windows for accessibility callback of display " + displayId
169                                 + " already cleared!");
170             }
171             removeObserverOfEmbeddedDisplay(windowsForA11yObserver);
172             mWindowsForAccessibilityObserver.remove(displayId);
173         }
174         return true;
175     }
176 
performComputeChangedWindowsNotLocked(int displayId, boolean forceSend)177     public void performComputeChangedWindowsNotLocked(int displayId, boolean forceSend) {
178         WindowsForAccessibilityObserver observer = null;
179         synchronized (mService) {
180             final WindowsForAccessibilityObserver windowsForA11yObserver =
181                     mWindowsForAccessibilityObserver.get(displayId);
182             if (windowsForA11yObserver != null) {
183                 observer = windowsForA11yObserver;
184             }
185         }
186         if (observer != null) {
187             observer.performComputeChangedWindowsNotLocked(forceSend);
188         }
189     }
190 
setMagnificationSpecLocked(int displayId, MagnificationSpec spec)191     public void setMagnificationSpecLocked(int displayId, MagnificationSpec spec) {
192         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
193         if (displayMagnifier != null) {
194             displayMagnifier.setMagnificationSpecLocked(spec);
195         }
196         final WindowsForAccessibilityObserver windowsForA11yObserver =
197                 mWindowsForAccessibilityObserver.get(displayId);
198         if (windowsForA11yObserver != null) {
199             windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
200         }
201     }
202 
getMagnificationRegionLocked(int displayId, Region outMagnificationRegion)203     public void getMagnificationRegionLocked(int displayId, Region outMagnificationRegion) {
204         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
205         if (displayMagnifier != null) {
206             displayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
207         }
208     }
209 
onRectangleOnScreenRequestedLocked(int displayId, Rect rectangle)210     public void onRectangleOnScreenRequestedLocked(int displayId, Rect rectangle) {
211         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
212         if (displayMagnifier != null) {
213             displayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
214         }
215         // Not relevant for the window observer.
216     }
217 
onWindowLayersChangedLocked(int displayId)218     public void onWindowLayersChangedLocked(int displayId) {
219         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
220         if (displayMagnifier != null) {
221             displayMagnifier.onWindowLayersChangedLocked();
222         }
223         final WindowsForAccessibilityObserver windowsForA11yObserver =
224                 mWindowsForAccessibilityObserver.get(displayId);
225         if (windowsForA11yObserver != null) {
226             windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
227         }
228     }
229 
onRotationChangedLocked(DisplayContent displayContent)230     public void onRotationChangedLocked(DisplayContent displayContent) {
231         final int displayId = displayContent.getDisplayId();
232         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
233         if (displayMagnifier != null) {
234             displayMagnifier.onRotationChangedLocked(displayContent);
235         }
236         final WindowsForAccessibilityObserver windowsForA11yObserver =
237                 mWindowsForAccessibilityObserver.get(displayId);
238         if (windowsForA11yObserver != null) {
239             windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
240         }
241     }
242 
onAppWindowTransitionLocked(int displayId, int transition)243     public void onAppWindowTransitionLocked(int displayId, int transition) {
244         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
245         if (displayMagnifier != null) {
246             displayMagnifier.onAppWindowTransitionLocked(displayId, transition);
247         }
248         // Not relevant for the window observer.
249     }
250 
onWindowTransitionLocked(WindowState windowState, int transition)251     public void onWindowTransitionLocked(WindowState windowState, int transition) {
252         final int displayId = windowState.getDisplayId();
253         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
254         if (displayMagnifier != null) {
255             displayMagnifier.onWindowTransitionLocked(windowState, transition);
256         }
257         final WindowsForAccessibilityObserver windowsForA11yObserver =
258                 mWindowsForAccessibilityObserver.get(displayId);
259         if (windowsForA11yObserver != null) {
260             windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
261         }
262     }
263 
onWindowFocusChangedNotLocked(int displayId)264     public void onWindowFocusChangedNotLocked(int displayId) {
265         // Not relevant for the display magnifier.
266 
267         WindowsForAccessibilityObserver observer = null;
268         synchronized (mService) {
269             final WindowsForAccessibilityObserver windowsForA11yObserver =
270                     mWindowsForAccessibilityObserver.get(displayId);
271             if (windowsForA11yObserver != null) {
272                 observer = windowsForA11yObserver;
273             }
274         }
275         if (observer != null) {
276             observer.performComputeChangedWindowsNotLocked(false);
277         }
278     }
279 
280     /**
281      * Called when the location or the size of the window is changed. Moving the window to
282      * another display is also taken into consideration.
283      * @param displayIds the display ids of displays when the situation happens.
284      */
onSomeWindowResizedOrMovedLocked(int... displayIds)285     public void onSomeWindowResizedOrMovedLocked(int... displayIds) {
286         // Not relevant for the display magnifier.
287         for (int i = 0; i < displayIds.length; i++) {
288             final WindowsForAccessibilityObserver windowsForA11yObserver =
289                     mWindowsForAccessibilityObserver.get(displayIds[i]);
290             if (windowsForA11yObserver != null) {
291                 windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
292             }
293         }
294     }
295 
drawMagnifiedRegionBorderIfNeededLocked(int displayId, SurfaceControl.Transaction t)296     public void drawMagnifiedRegionBorderIfNeededLocked(int displayId,
297             SurfaceControl.Transaction t) {
298         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
299         if (displayMagnifier != null) {
300             displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(t);
301         }
302         // Not relevant for the window observer.
303     }
304 
getMagnificationSpecForWindowLocked(WindowState windowState)305     public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
306         final int displayId = windowState.getDisplayId();
307         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
308         if (displayMagnifier != null) {
309             return displayMagnifier.getMagnificationSpecForWindowLocked(windowState);
310         }
311         return null;
312     }
313 
hasCallbacksLocked()314     public boolean hasCallbacksLocked() {
315         return (mDisplayMagnifiers.size() > 0
316                 || mWindowsForAccessibilityObserver.size() > 0);
317     }
318 
setForceShowMagnifiableBoundsLocked(int displayId, boolean show)319     public void setForceShowMagnifiableBoundsLocked(int displayId, boolean show) {
320         final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
321         if (displayMagnifier != null) {
322             displayMagnifier.setForceShowMagnifiableBoundsLocked(show);
323             displayMagnifier.showMagnificationBoundsIfNeeded();
324         }
325     }
326 
handleWindowObserverOfEmbeddedDisplayLocked(int embeddedDisplayId, WindowState parentWindow)327     public void handleWindowObserverOfEmbeddedDisplayLocked(int embeddedDisplayId,
328             WindowState parentWindow) {
329         if (embeddedDisplayId == Display.DEFAULT_DISPLAY || parentWindow == null) {
330             return;
331         }
332         // Finds the parent display of this embedded display
333         final int parentDisplayId;
334         WindowState candidate = parentWindow;
335         while (candidate != null) {
336             parentWindow = candidate;
337             candidate = parentWindow.getDisplayContent().getParentWindow();
338         }
339         parentDisplayId = parentWindow.getDisplayId();
340         // Uses the observer of parent display
341         final WindowsForAccessibilityObserver windowsForA11yObserver =
342                 mWindowsForAccessibilityObserver.get(parentDisplayId);
343 
344         if (windowsForA11yObserver != null) {
345             windowsForA11yObserver.addEmbeddedDisplay(embeddedDisplayId);
346             // Replaces the observer of embedded display to the one of parent display
347             mWindowsForAccessibilityObserver.put(embeddedDisplayId, windowsForA11yObserver);
348         }
349     }
350 
populateTransformationMatrixLocked(WindowState windowState, Matrix outMatrix)351     private static void populateTransformationMatrixLocked(WindowState windowState,
352             Matrix outMatrix) {
353         windowState.getTransformationMatrix(sTempFloats, outMatrix);
354     }
355 
dump(PrintWriter pw, String prefix)356     void dump(PrintWriter pw, String prefix) {
357         for (int i = 0; i < mDisplayMagnifiers.size(); i++) {
358             final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.valueAt(i);
359             if (displayMagnifier != null) {
360                 displayMagnifier.dump(pw, prefix
361                         + "Magnification display# " + mDisplayMagnifiers.keyAt(i));
362             }
363         }
364     }
365 
removeObserverOfEmbeddedDisplay(WindowsForAccessibilityObserver observerOfParentDisplay)366     private void removeObserverOfEmbeddedDisplay(WindowsForAccessibilityObserver
367             observerOfParentDisplay) {
368         final IntArray embeddedDisplayIdList =
369                 observerOfParentDisplay.getAndClearEmbeddedDisplayIdList();
370 
371         for (int index = 0; index < embeddedDisplayIdList.size(); index++) {
372             final int embeddedDisplayId = embeddedDisplayIdList.get(index);
373             mWindowsForAccessibilityObserver.remove(embeddedDisplayId);
374         }
375     }
376 
isEmbeddedDisplay(DisplayContent dc)377     private static boolean isEmbeddedDisplay(DisplayContent dc) {
378         final Display display = dc.getDisplay();
379 
380         return display.getType() == Display.TYPE_VIRTUAL && dc.getParentWindow() != null;
381     }
382 
383     /**
384      * This class encapsulates the functionality related to display magnification.
385      */
386     private static final class DisplayMagnifier {
387 
388         private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM;
389 
390         private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
391         private static final boolean DEBUG_ROTATION = false;
392         private static final boolean DEBUG_LAYERS = false;
393         private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
394         private static final boolean DEBUG_VIEWPORT_WINDOW = false;
395 
396         private final Rect mTempRect1 = new Rect();
397         private final Rect mTempRect2 = new Rect();
398 
399         private final Region mTempRegion1 = new Region();
400         private final Region mTempRegion2 = new Region();
401         private final Region mTempRegion3 = new Region();
402         private final Region mTempRegion4 = new Region();
403 
404         private final Context mDisplayContext;
405         private final WindowManagerService mService;
406         private final MagnifiedViewport mMagnifedViewport;
407         private final Handler mHandler;
408         private final DisplayContent mDisplayContent;
409         private final Display mDisplay;
410 
411         private final MagnificationCallbacks mCallbacks;
412 
413         private final long mLongAnimationDuration;
414 
415         private boolean mForceShowMagnifiableBounds = false;
416 
DisplayMagnifier(WindowManagerService windowManagerService, DisplayContent displayContent, Display display, MagnificationCallbacks callbacks)417         public DisplayMagnifier(WindowManagerService windowManagerService,
418                 DisplayContent displayContent,
419                 Display display,
420                 MagnificationCallbacks callbacks) {
421             mDisplayContext = windowManagerService.mContext.createDisplayContext(display);
422             mService = windowManagerService;
423             mCallbacks = callbacks;
424             mDisplayContent = displayContent;
425             mDisplay = display;
426             mHandler = new MyHandler(mService.mH.getLooper());
427             mMagnifedViewport = new MagnifiedViewport();
428             mLongAnimationDuration = mDisplayContext.getResources().getInteger(
429                     com.android.internal.R.integer.config_longAnimTime);
430         }
431 
setMagnificationSpecLocked(MagnificationSpec spec)432         public void setMagnificationSpecLocked(MagnificationSpec spec) {
433             mMagnifedViewport.updateMagnificationSpecLocked(spec);
434             mMagnifedViewport.recomputeBoundsLocked();
435 
436             mService.applyMagnificationSpecLocked(mDisplay.getDisplayId(), spec);
437             mService.scheduleAnimationLocked();
438         }
439 
setForceShowMagnifiableBoundsLocked(boolean show)440         public void setForceShowMagnifiableBoundsLocked(boolean show) {
441             mForceShowMagnifiableBounds = show;
442             mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true);
443         }
444 
isForceShowingMagnifiableBoundsLocked()445         public boolean isForceShowingMagnifiableBoundsLocked() {
446             return mForceShowMagnifiableBounds;
447         }
448 
onRectangleOnScreenRequestedLocked(Rect rectangle)449         public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
450             if (DEBUG_RECTANGLE_REQUESTED) {
451                 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
452             }
453             if (!mMagnifedViewport.isMagnifyingLocked()) {
454                 return;
455             }
456             Rect magnifiedRegionBounds = mTempRect2;
457             mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
458             if (magnifiedRegionBounds.contains(rectangle)) {
459                 return;
460             }
461             SomeArgs args = SomeArgs.obtain();
462             args.argi1 = rectangle.left;
463             args.argi2 = rectangle.top;
464             args.argi3 = rectangle.right;
465             args.argi4 = rectangle.bottom;
466             mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
467                     args).sendToTarget();
468         }
469 
onWindowLayersChangedLocked()470         public void onWindowLayersChangedLocked() {
471             if (DEBUG_LAYERS) {
472                 Slog.i(LOG_TAG, "Layers changed.");
473             }
474             mMagnifedViewport.recomputeBoundsLocked();
475             mService.scheduleAnimationLocked();
476         }
477 
onRotationChangedLocked(DisplayContent displayContent)478         public void onRotationChangedLocked(DisplayContent displayContent) {
479             if (DEBUG_ROTATION) {
480                 final int rotation = displayContent.getRotation();
481                 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
482                         + " displayId: " + displayContent.getDisplayId());
483             }
484             mMagnifedViewport.onRotationChangedLocked(displayContent.getPendingTransaction());
485             mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
486         }
487 
onAppWindowTransitionLocked(int displayId, int transition)488         public void onAppWindowTransitionLocked(int displayId, int transition) {
489             if (DEBUG_WINDOW_TRANSITIONS) {
490                 Slog.i(LOG_TAG, "Window transition: "
491                         + AppTransition.appTransitionToString(transition)
492                         + " displayId: " + displayId);
493             }
494             final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
495             if (magnifying) {
496                 switch (transition) {
497                     case WindowManager.TRANSIT_ACTIVITY_OPEN:
498                     case WindowManager.TRANSIT_TASK_OPEN:
499                     case WindowManager.TRANSIT_TASK_TO_FRONT:
500                     case WindowManager.TRANSIT_WALLPAPER_OPEN:
501                     case WindowManager.TRANSIT_WALLPAPER_CLOSE:
502                     case WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN: {
503                         mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
504                     }
505                 }
506             }
507         }
508 
onWindowTransitionLocked(WindowState windowState, int transition)509         public void onWindowTransitionLocked(WindowState windowState, int transition) {
510             if (DEBUG_WINDOW_TRANSITIONS) {
511                 Slog.i(LOG_TAG, "Window transition: "
512                         + AppTransition.appTransitionToString(transition)
513                         + " displayId: " + windowState.getDisplayId());
514             }
515             final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
516             final int type = windowState.mAttrs.type;
517             switch (transition) {
518                 case WindowManagerPolicy.TRANSIT_ENTER:
519                 case WindowManagerPolicy.TRANSIT_SHOW: {
520                     if (!magnifying) {
521                         break;
522                     }
523                     switch (type) {
524                         case WindowManager.LayoutParams.TYPE_APPLICATION:
525                         case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
526                         case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
527                         case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
528                         case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
529                         case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
530                         case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
531                         case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
532                         case WindowManager.LayoutParams.TYPE_PHONE:
533                         case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
534                         case WindowManager.LayoutParams.TYPE_TOAST:
535                         case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
536                         case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
537                         case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
538                         case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
539                         case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
540                         case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
541                         case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
542                         case WindowManager.LayoutParams.TYPE_QS_DIALOG:
543                         case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
544                             Rect magnifiedRegionBounds = mTempRect2;
545                             mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
546                                     magnifiedRegionBounds);
547                             Rect touchableRegionBounds = mTempRect1;
548                             windowState.getTouchableRegion(mTempRegion1);
549                             mTempRegion1.getBounds(touchableRegionBounds);
550                             if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
551                                 mCallbacks.onRectangleOnScreenRequested(
552                                         touchableRegionBounds.left,
553                                         touchableRegionBounds.top,
554                                         touchableRegionBounds.right,
555                                         touchableRegionBounds.bottom);
556                             }
557                         } break;
558                     } break;
559                 }
560             }
561         }
562 
getMagnificationSpecForWindowLocked(WindowState windowState)563         public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
564             MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
565             if (spec != null && !spec.isNop()) {
566                 if (!windowState.shouldMagnify()) {
567                     return null;
568                 }
569             }
570             return spec;
571         }
572 
getMagnificationRegionLocked(Region outMagnificationRegion)573         public void getMagnificationRegionLocked(Region outMagnificationRegion) {
574             // Make sure we're working with the most current bounds
575             mMagnifedViewport.recomputeBoundsLocked();
576             mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
577         }
578 
destroyLocked()579         public void destroyLocked() {
580             mMagnifedViewport.destroyWindow();
581         }
582 
583         // Can be called outside of a surface transaction
showMagnificationBoundsIfNeeded()584         public void showMagnificationBoundsIfNeeded() {
585             mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
586                     .sendToTarget();
587         }
588 
drawMagnifiedRegionBorderIfNeededLocked(SurfaceControl.Transaction t)589         public void drawMagnifiedRegionBorderIfNeededLocked(SurfaceControl.Transaction t) {
590             mMagnifedViewport.drawWindowIfNeededLocked(t);
591         }
592 
dump(PrintWriter pw, String prefix)593         void dump(PrintWriter pw, String prefix) {
594             mMagnifedViewport.dump(pw, prefix);
595         }
596 
597         private final class MagnifiedViewport {
598 
599             private final SparseArray<WindowState> mTempWindowStates =
600                     new SparseArray<WindowState>();
601 
602             private final RectF mTempRectF = new RectF();
603 
604             private final Point mTempPoint = new Point();
605 
606             private final Matrix mTempMatrix = new Matrix();
607 
608             private final Region mMagnificationRegion = new Region();
609             private final Region mOldMagnificationRegion = new Region();
610 
611             private final Path mCircularPath;
612 
613             private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
614 
615             private final float mBorderWidth;
616             private final int mHalfBorderWidth;
617             private final int mDrawBorderInset;
618 
619             private final ViewportWindow mWindow;
620 
621             private boolean mFullRedrawNeeded;
622             private int mTempLayer = 0;
623 
MagnifiedViewport()624             public MagnifiedViewport() {
625                 mBorderWidth = mDisplayContext.getResources().getDimension(
626                         com.android.internal.R.dimen.accessibility_magnification_indicator_width);
627                 mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
628                 mDrawBorderInset = (int) mBorderWidth / 2;
629                 mWindow = new ViewportWindow(mDisplayContext);
630 
631                 if (mDisplayContext.getResources().getConfiguration().isScreenRound()) {
632                     mCircularPath = new Path();
633                     mDisplay.getRealSize(mTempPoint);
634                     final int centerXY = mTempPoint.x / 2;
635                     mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
636                 } else {
637                     mCircularPath = null;
638                 }
639 
640                 recomputeBoundsLocked();
641             }
642 
getMagnificationRegionLocked(@onNull Region outMagnificationRegion)643             public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) {
644                 outMagnificationRegion.set(mMagnificationRegion);
645             }
646 
updateMagnificationSpecLocked(MagnificationSpec spec)647             public void updateMagnificationSpecLocked(MagnificationSpec spec) {
648                 if (spec != null) {
649                     mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
650                 } else {
651                     mMagnificationSpec.clear();
652                 }
653                 // If this message is pending we are in a rotation animation and do not want
654                 // to show the border. We will do so when the pending message is handled.
655                 if (!mHandler.hasMessages(
656                         MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
657                     setMagnifiedRegionBorderShownLocked(
658                             isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true);
659                 }
660             }
661 
recomputeBoundsLocked()662             public void recomputeBoundsLocked() {
663                 mDisplay.getRealSize(mTempPoint);
664                 final int screenWidth = mTempPoint.x;
665                 final int screenHeight = mTempPoint.y;
666 
667                 mMagnificationRegion.set(0, 0, 0, 0);
668                 final Region availableBounds = mTempRegion1;
669                 availableBounds.set(0, 0, screenWidth, screenHeight);
670 
671                 if (mCircularPath != null) {
672                     availableBounds.setPath(mCircularPath, availableBounds);
673                 }
674 
675                 Region nonMagnifiedBounds = mTempRegion4;
676                 nonMagnifiedBounds.set(0, 0, 0, 0);
677 
678                 SparseArray<WindowState> visibleWindows = mTempWindowStates;
679                 visibleWindows.clear();
680                 populateWindowsOnScreenLocked(visibleWindows);
681 
682                 final int visibleWindowCount = visibleWindows.size();
683                 for (int i = visibleWindowCount - 1; i >= 0; i--) {
684                     WindowState windowState = visibleWindows.valueAt(i);
685                     if ((windowState.mAttrs.type == TYPE_MAGNIFICATION_OVERLAY)
686                             || ((windowState.mAttrs.privateFlags
687                             & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) {
688                         continue;
689                     }
690 
691                     // Consider the touchable portion of the window
692                     Matrix matrix = mTempMatrix;
693                     populateTransformationMatrixLocked(windowState, matrix);
694                     Region touchableRegion = mTempRegion3;
695                     windowState.getTouchableRegion(touchableRegion);
696                     Rect touchableFrame = mTempRect1;
697                     touchableRegion.getBounds(touchableFrame);
698                     RectF windowFrame = mTempRectF;
699                     windowFrame.set(touchableFrame);
700                     windowFrame.offset(-windowState.getFrameLw().left,
701                             -windowState.getFrameLw().top);
702                     matrix.mapRect(windowFrame);
703                     Region windowBounds = mTempRegion2;
704                     windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
705                             (int) windowFrame.right, (int) windowFrame.bottom);
706                     // Only update new regions
707                     Region portionOfWindowAlreadyAccountedFor = mTempRegion3;
708                     portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion);
709                     portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
710                     windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
711 
712                     if (windowState.shouldMagnify()) {
713                         mMagnificationRegion.op(windowBounds, Region.Op.UNION);
714                         mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
715                     } else {
716                         nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
717                         availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
718                     }
719 
720                     // If the navigation bar window doesn't have touchable region, count
721                     // navigation bar insets into nonMagnifiedBounds. It happens when
722                     // navigation mode is gestural.
723                     if (isUntouchableNavigationBar(windowState, mTempRegion3)) {
724                         final Rect navBarInsets = getNavBarInsets(mDisplayContent);
725                         nonMagnifiedBounds.op(navBarInsets, Region.Op.UNION);
726                         availableBounds.op(navBarInsets, Region.Op.DIFFERENCE);
727                     }
728 
729                     // Count letterbox into nonMagnifiedBounds
730                     if (windowState.isLetterboxedForDisplayCutoutLw()) {
731                         Region letterboxBounds = getLetterboxBounds(windowState);
732                         nonMagnifiedBounds.op(letterboxBounds, Region.Op.UNION);
733                         availableBounds.op(letterboxBounds, Region.Op.DIFFERENCE);
734                     }
735 
736                     // Update accounted bounds
737                     Region accountedBounds = mTempRegion2;
738                     accountedBounds.set(mMagnificationRegion);
739                     accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
740                     accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
741 
742                     if (accountedBounds.isRect()) {
743                         Rect accountedFrame = mTempRect1;
744                         accountedBounds.getBounds(accountedFrame);
745                         if (accountedFrame.width() == screenWidth
746                                 && accountedFrame.height() == screenHeight) {
747                             break;
748                         }
749                     }
750                 }
751 
752                 visibleWindows.clear();
753 
754                 mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
755                         screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
756                         Region.Op.INTERSECT);
757 
758                 final boolean magnifiedChanged =
759                         !mOldMagnificationRegion.equals(mMagnificationRegion);
760                 if (magnifiedChanged) {
761                     mWindow.setBounds(mMagnificationRegion);
762                     final Rect dirtyRect = mTempRect1;
763                     if (mFullRedrawNeeded) {
764                         mFullRedrawNeeded = false;
765                         dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
766                                 screenWidth - mDrawBorderInset,
767                                 screenHeight - mDrawBorderInset);
768                         mWindow.invalidate(dirtyRect);
769                     } else {
770                         final Region dirtyRegion = mTempRegion3;
771                         dirtyRegion.set(mMagnificationRegion);
772                         dirtyRegion.op(mOldMagnificationRegion, Region.Op.XOR);
773                         dirtyRegion.getBounds(dirtyRect);
774                         mWindow.invalidate(dirtyRect);
775                     }
776 
777                     mOldMagnificationRegion.set(mMagnificationRegion);
778                     final SomeArgs args = SomeArgs.obtain();
779                     args.arg1 = Region.obtain(mMagnificationRegion);
780                     mHandler.obtainMessage(
781                             MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args)
782                             .sendToTarget();
783                 }
784             }
785 
getLetterboxBounds(WindowState windowState)786             private Region getLetterboxBounds(WindowState windowState) {
787                 final ActivityRecord appToken = windowState.mActivityRecord;
788                 if (appToken == null) {
789                     return new Region();
790                 }
791 
792                 mDisplay.getRealSize(mTempPoint);
793                 final Rect letterboxInsets = appToken.getLetterboxInsets();
794                 final int screenWidth = mTempPoint.x;
795                 final int screenHeight = mTempPoint.y;
796                 final Rect nonLetterboxRect = mTempRect1;
797                 final Region letterboxBounds = mTempRegion3;
798                 nonLetterboxRect.set(0, 0, screenWidth, screenHeight);
799                 nonLetterboxRect.inset(letterboxInsets);
800                 letterboxBounds.set(0, 0, screenWidth, screenHeight);
801                 letterboxBounds.op(nonLetterboxRect, Region.Op.DIFFERENCE);
802                 return letterboxBounds;
803             }
804 
onRotationChangedLocked(SurfaceControl.Transaction t)805             public void onRotationChangedLocked(SurfaceControl.Transaction t) {
806                 // If we are showing the magnification border, hide it immediately so
807                 // the user does not see strange artifacts during rotation. The screenshot
808                 // used for rotation already has the border. After the rotation is complete
809                 // we will show the border.
810                 if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) {
811                     setMagnifiedRegionBorderShownLocked(false, false);
812                     final long delay = (long) (mLongAnimationDuration
813                             * mService.getWindowAnimationScaleLocked());
814                     Message message = mHandler.obtainMessage(
815                             MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
816                     mHandler.sendMessageDelayed(message, delay);
817                 }
818                 recomputeBoundsLocked();
819                 mWindow.updateSize(t);
820             }
821 
setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate)822             public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
823                 if (shown) {
824                     mFullRedrawNeeded = true;
825                     mOldMagnificationRegion.set(0, 0, 0, 0);
826                 }
827                 mWindow.setShown(shown, animate);
828             }
829 
getMagnifiedFrameInContentCoordsLocked(Rect rect)830             public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
831                 MagnificationSpec spec = mMagnificationSpec;
832                 mMagnificationRegion.getBounds(rect);
833                 rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
834                 rect.scale(1.0f / spec.scale);
835             }
836 
isMagnifyingLocked()837             public boolean isMagnifyingLocked() {
838                 return mMagnificationSpec.scale > 1.0f;
839             }
840 
getMagnificationSpecLocked()841             public MagnificationSpec getMagnificationSpecLocked() {
842                 return mMagnificationSpec;
843             }
844 
drawWindowIfNeededLocked(SurfaceControl.Transaction t)845             public void drawWindowIfNeededLocked(SurfaceControl.Transaction t) {
846                 recomputeBoundsLocked();
847                 mWindow.drawIfNeeded(t);
848             }
849 
destroyWindow()850             public void destroyWindow() {
851                 mWindow.releaseSurface();
852             }
853 
populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows)854             private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
855                 mTempLayer = 0;
856                 mDisplayContent.forAllWindows((w) -> {
857                     if (w.isOnScreen() && w.isVisibleLw()
858                             && (w.mAttrs.alpha != 0)) {
859                         mTempLayer++;
860                         outWindows.put(mTempLayer, w);
861                     }
862                 }, false /* traverseTopToBottom */ );
863             }
864 
dump(PrintWriter pw, String prefix)865             void dump(PrintWriter pw, String prefix) {
866                 mWindow.dump(pw, prefix);
867             }
868 
869             private final class ViewportWindow {
870                 private static final String SURFACE_TITLE = "Magnification Overlay";
871 
872                 private final Region mBounds = new Region();
873                 private final Rect mDirtyRect = new Rect();
874                 private final Paint mPaint = new Paint();
875 
876                 private final SurfaceControl mSurfaceControl;
877                 private final Surface mSurface = mService.mSurfaceFactory.get();
878 
879                 private final AnimationController mAnimationController;
880 
881                 private boolean mShown;
882                 private int mAlpha;
883 
884                 private boolean mInvalidated;
885 
ViewportWindow(Context context)886                 public ViewportWindow(Context context) {
887                     SurfaceControl surfaceControl = null;
888                     try {
889                         mDisplay.getRealSize(mTempPoint);
890                         surfaceControl = mDisplayContent
891                                 .makeOverlay()
892                                 .setName(SURFACE_TITLE)
893                                 .setBufferSize(mTempPoint.x, mTempPoint.y) // not a typo
894                                 .setFormat(PixelFormat.TRANSLUCENT)
895                                 .setCallsite("ViewportWindow")
896                                 .build();
897                     } catch (OutOfResourcesException oore) {
898                         /* ignore */
899                     }
900                     mSurfaceControl = surfaceControl;
901 
902                     final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
903                     final int layer =
904                             mService.mPolicy.getWindowLayerFromTypeLw(TYPE_MAGNIFICATION_OVERLAY) *
905                                     WindowManagerService.TYPE_LAYER_MULTIPLIER;
906                     t.setLayer(mSurfaceControl, layer).setPosition(mSurfaceControl, 0, 0);
907                     InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t,
908                             mDisplayContent.getDisplayId(), "Magnification Overlay");
909                     t.apply();
910 
911                     mSurface.copyFrom(mSurfaceControl);
912 
913                     mAnimationController = new AnimationController(context,
914                             mService.mH.getLooper());
915 
916                     TypedValue typedValue = new TypedValue();
917                     context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
918                             typedValue, true);
919                     final int borderColor = context.getColor(typedValue.resourceId);
920 
921                     mPaint.setStyle(Paint.Style.STROKE);
922                     mPaint.setStrokeWidth(mBorderWidth);
923                     mPaint.setColor(borderColor);
924 
925                     mInvalidated = true;
926                 }
927 
setShown(boolean shown, boolean animate)928                 public void setShown(boolean shown, boolean animate) {
929                     synchronized (mService.mGlobalLock) {
930                         if (mShown == shown) {
931                             return;
932                         }
933                         mShown = shown;
934                         mAnimationController.onFrameShownStateChanged(shown, animate);
935                         if (DEBUG_VIEWPORT_WINDOW) {
936                             Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
937                         }
938                     }
939                 }
940 
941                 @SuppressWarnings("unused")
942                 // Called reflectively from an animator.
getAlpha()943                 public int getAlpha() {
944                     synchronized (mService.mGlobalLock) {
945                         return mAlpha;
946                     }
947                 }
948 
setAlpha(int alpha)949                 public void setAlpha(int alpha) {
950                     synchronized (mService.mGlobalLock) {
951                         if (mAlpha == alpha) {
952                             return;
953                         }
954                         mAlpha = alpha;
955                         invalidate(null);
956                         if (DEBUG_VIEWPORT_WINDOW) {
957                             Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
958                         }
959                     }
960                 }
961 
setBounds(Region bounds)962                 public void setBounds(Region bounds) {
963                     synchronized (mService.mGlobalLock) {
964                         if (mBounds.equals(bounds)) {
965                             return;
966                         }
967                         mBounds.set(bounds);
968                         invalidate(mDirtyRect);
969                         if (DEBUG_VIEWPORT_WINDOW) {
970                             Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
971                         }
972                     }
973                 }
974 
updateSize(SurfaceControl.Transaction t)975                 public void updateSize(SurfaceControl.Transaction t) {
976                     synchronized (mService.mGlobalLock) {
977                         mDisplay.getRealSize(mTempPoint);
978                         t.setBufferSize(mSurfaceControl, mTempPoint.x, mTempPoint.y);
979                         invalidate(mDirtyRect);
980                     }
981                 }
982 
invalidate(Rect dirtyRect)983                 public void invalidate(Rect dirtyRect) {
984                     if (dirtyRect != null) {
985                         mDirtyRect.set(dirtyRect);
986                     } else {
987                         mDirtyRect.setEmpty();
988                     }
989                     mInvalidated = true;
990                     mService.scheduleAnimationLocked();
991                 }
992 
drawIfNeeded(SurfaceControl.Transaction t)993                 public void drawIfNeeded(SurfaceControl.Transaction t) {
994                     synchronized (mService.mGlobalLock) {
995                         if (!mInvalidated) {
996                             return;
997                         }
998                         mInvalidated = false;
999                         if (mAlpha > 0) {
1000                             Canvas canvas = null;
1001                             try {
1002                                 // Empty dirty rectangle means unspecified.
1003                                 if (mDirtyRect.isEmpty()) {
1004                                     mBounds.getBounds(mDirtyRect);
1005                                 }
1006                                 mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth);
1007                                 canvas = mSurface.lockCanvas(mDirtyRect);
1008                                 if (DEBUG_VIEWPORT_WINDOW) {
1009                                     Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
1010                                 }
1011                             } catch (IllegalArgumentException iae) {
1012                                 /* ignore */
1013                             } catch (Surface.OutOfResourcesException oore) {
1014                                 /* ignore */
1015                             }
1016                             if (canvas == null) {
1017                                 return;
1018                             }
1019                             if (DEBUG_VIEWPORT_WINDOW) {
1020                                 Slog.i(LOG_TAG, "Bounds: " + mBounds);
1021                             }
1022                             canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
1023                             mPaint.setAlpha(mAlpha);
1024                             Path path = mBounds.getBoundaryPath();
1025                             canvas.drawPath(path, mPaint);
1026 
1027                             mSurface.unlockCanvasAndPost(canvas);
1028                             t.show(mSurfaceControl);
1029                         } else {
1030                             t.hide(mSurfaceControl);
1031                         }
1032                     }
1033                 }
1034 
releaseSurface()1035                 public void releaseSurface() {
1036                     mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
1037                     mSurface.release();
1038                 }
1039 
dump(PrintWriter pw, String prefix)1040                 void dump(PrintWriter pw, String prefix) {
1041                     pw.println(prefix
1042                             + " mBounds= " + mBounds
1043                             + " mDirtyRect= " + mDirtyRect
1044                             + " mWidth= " + mSurfaceControl.getWidth()
1045                             + " mHeight= " + mSurfaceControl.getHeight());
1046                 }
1047 
1048                 private final class AnimationController extends Handler {
1049                     private static final String PROPERTY_NAME_ALPHA = "alpha";
1050 
1051                     private static final int MIN_ALPHA = 0;
1052                     private static final int MAX_ALPHA = 255;
1053 
1054                     private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1;
1055 
1056                     private final ValueAnimator mShowHideFrameAnimator;
1057 
AnimationController(Context context, Looper looper)1058                     public AnimationController(Context context, Looper looper) {
1059                         super(looper);
1060                         mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
1061                                 PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);
1062 
1063                         Interpolator interpolator = new DecelerateInterpolator(2.5f);
1064                         final long longAnimationDuration = context.getResources().getInteger(
1065                                 com.android.internal.R.integer.config_longAnimTime);
1066 
1067                         mShowHideFrameAnimator.setInterpolator(interpolator);
1068                         mShowHideFrameAnimator.setDuration(longAnimationDuration);
1069                     }
1070 
onFrameShownStateChanged(boolean shown, boolean animate)1071                     public void onFrameShownStateChanged(boolean shown, boolean animate) {
1072                         obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
1073                                 shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
1074                     }
1075 
1076                     @Override
handleMessage(Message message)1077                     public void handleMessage(Message message) {
1078                         switch (message.what) {
1079                             case MSG_FRAME_SHOWN_STATE_CHANGED: {
1080                                 final boolean shown = message.arg1 == 1;
1081                                 final boolean animate = message.arg2 == 1;
1082 
1083                                 if (animate) {
1084                                     if (mShowHideFrameAnimator.isRunning()) {
1085                                         mShowHideFrameAnimator.reverse();
1086                                     } else {
1087                                         if (shown) {
1088                                             mShowHideFrameAnimator.start();
1089                                         } else {
1090                                             mShowHideFrameAnimator.reverse();
1091                                         }
1092                                     }
1093                                 } else {
1094                                     mShowHideFrameAnimator.cancel();
1095                                     if (shown) {
1096                                         setAlpha(MAX_ALPHA);
1097                                     } else {
1098                                         setAlpha(MIN_ALPHA);
1099                                     }
1100                                 }
1101                             } break;
1102                         }
1103                     }
1104                 }
1105             }
1106         }
1107 
1108         private class MyHandler extends Handler {
1109             public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
1110             public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
1111             public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
1112             public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
1113             public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
1114 
MyHandler(Looper looper)1115             public MyHandler(Looper looper) {
1116                 super(looper);
1117             }
1118 
1119             @Override
handleMessage(Message message)1120             public void handleMessage(Message message) {
1121                 switch (message.what) {
1122                     case MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED: {
1123                         final SomeArgs args = (SomeArgs) message.obj;
1124                         final Region magnifiedBounds = (Region) args.arg1;
1125                         mCallbacks.onMagnificationRegionChanged(magnifiedBounds);
1126                         magnifiedBounds.recycle();
1127                     } break;
1128 
1129                     case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
1130                         SomeArgs args = (SomeArgs) message.obj;
1131                         final int left = args.argi1;
1132                         final int top = args.argi2;
1133                         final int right = args.argi3;
1134                         final int bottom = args.argi4;
1135                         mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
1136                         args.recycle();
1137                     } break;
1138 
1139                     case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
1140                         mCallbacks.onUserContextChanged();
1141                     } break;
1142 
1143                     case MESSAGE_NOTIFY_ROTATION_CHANGED: {
1144                         final int rotation = message.arg1;
1145                         mCallbacks.onRotationChanged(rotation);
1146                     } break;
1147 
1148                     case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
1149                         synchronized (mService.mGlobalLock) {
1150                             if (mMagnifedViewport.isMagnifyingLocked()
1151                                     || isForceShowingMagnifiableBoundsLocked()) {
1152                                 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
1153                                 mService.scheduleAnimationLocked();
1154                             }
1155                         }
1156                     } break;
1157                 }
1158             }
1159         }
1160     }
1161 
isUntouchableNavigationBar(WindowState windowState, Region touchableRegion)1162     static boolean isUntouchableNavigationBar(WindowState windowState,
1163             Region touchableRegion) {
1164         if (windowState.mAttrs.type != WindowManager.LayoutParams.TYPE_NAVIGATION_BAR) {
1165             return false;
1166         }
1167 
1168         // Gets the touchable region.
1169         windowState.getTouchableRegion(touchableRegion);
1170 
1171         return touchableRegion.isEmpty();
1172     }
1173 
getNavBarInsets(DisplayContent displayContent)1174     static Rect getNavBarInsets(DisplayContent displayContent) {
1175         final InsetsSource source = displayContent.getInsetsStateController().getRawInsetsState()
1176                 .peekSource(ITYPE_NAVIGATION_BAR);
1177         return source != null ? source.getFrame() : EMPTY_RECT;
1178     }
1179 
1180     /**
1181      * This class encapsulates the functionality related to computing the windows
1182      * reported for accessibility purposes. These windows are all windows a sighted
1183      * user can see on the screen.
1184      */
1185     private static final class WindowsForAccessibilityObserver {
1186         private static final String LOG_TAG = TAG_WITH_CLASS_NAME ?
1187                 "WindowsForAccessibilityObserver" : TAG_WM;
1188 
1189         private static final boolean DEBUG = false;
1190 
1191         private final SparseArray<WindowState> mTempWindowStates = new SparseArray<>();
1192 
1193         private final Set<IBinder> mTempBinderSet = new ArraySet<>();
1194 
1195         private final RectF mTempRectF = new RectF();
1196 
1197         private final Matrix mTempMatrix = new Matrix();
1198 
1199         private final Point mTempPoint = new Point();
1200 
1201         private final Region mTempRegion = new Region();
1202 
1203         private final Region mTempRegion1 = new Region();
1204 
1205         private final WindowManagerService mService;
1206 
1207         private final Handler mHandler;
1208 
1209         private final WindowsForAccessibilityCallback mCallback;
1210 
1211         private final int mDisplayId;
1212 
1213         private final long mRecurringAccessibilityEventsIntervalMillis;
1214 
1215         private final IntArray mEmbeddedDisplayIdList = new IntArray(0);
1216 
WindowsForAccessibilityObserver(WindowManagerService windowManagerService, int displayId, WindowsForAccessibilityCallback callback)1217         public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
1218                 int displayId,
1219                 WindowsForAccessibilityCallback callback) {
1220             mService = windowManagerService;
1221             mCallback = callback;
1222             mDisplayId = displayId;
1223             mHandler = new MyHandler(mService.mH.getLooper());
1224             mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
1225                     .getSendRecurringAccessibilityEventsInterval();
1226             computeChangedWindows(true);
1227         }
1228 
performComputeChangedWindowsNotLocked(boolean forceSend)1229         public void performComputeChangedWindowsNotLocked(boolean forceSend) {
1230             mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
1231             computeChangedWindows(forceSend);
1232         }
1233 
scheduleComputeChangedWindowsLocked()1234         public void scheduleComputeChangedWindowsLocked() {
1235             if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
1236                 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
1237                         mRecurringAccessibilityEventsIntervalMillis);
1238             }
1239         }
1240 
getAndClearEmbeddedDisplayIdList()1241         IntArray getAndClearEmbeddedDisplayIdList() {
1242             final IntArray returnedArray = new IntArray(mEmbeddedDisplayIdList.size());
1243             returnedArray.addAll(mEmbeddedDisplayIdList);
1244             mEmbeddedDisplayIdList.clear();
1245 
1246             return returnedArray;
1247         }
1248 
addEmbeddedDisplay(int displayId)1249         void addEmbeddedDisplay(int displayId) {
1250             if (displayId == mDisplayId) {
1251                 return;
1252             }
1253             mEmbeddedDisplayIdList.add(displayId);
1254         }
1255 
1256         /**
1257          * Check if windows have changed, and send them to the accessibility subsystem if they have.
1258          *
1259          * @param forceSend Send the windows the accessibility even if they haven't changed.
1260          */
computeChangedWindows(boolean forceSend)1261         public void computeChangedWindows(boolean forceSend) {
1262             if (DEBUG) {
1263                 Slog.i(LOG_TAG, "computeChangedWindows()");
1264             }
1265 
1266             List<WindowInfo> windows = new ArrayList<>();
1267             final int topFocusedDisplayId;
1268             IBinder topFocusedWindowToken = null;
1269 
1270             synchronized (mService.mGlobalLock) {
1271                 // Do not send the windows if there is no top focus as
1272                 // the window manager is still looking for where to put it.
1273                 // We will do the work when we get a focus change callback.
1274                 final WindowState topFocusedWindowState = getTopFocusWindow();
1275                 if (topFocusedWindowState == null) return;
1276 
1277                 final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
1278                 if (dc == null) {
1279                     return;
1280                 }
1281                 final Display display = dc.getDisplay();
1282                 display.getRealSize(mTempPoint);
1283                 final int screenWidth = mTempPoint.x;
1284                 final int screenHeight = mTempPoint.y;
1285 
1286                 Region unaccountedSpace = mTempRegion;
1287                 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
1288 
1289                 final SparseArray<WindowState> visibleWindows = mTempWindowStates;
1290                 populateVisibleWindowsOnScreenLocked(visibleWindows);
1291                 Set<IBinder> addedWindows = mTempBinderSet;
1292                 addedWindows.clear();
1293 
1294                 boolean focusedWindowAdded = false;
1295 
1296                 final int visibleWindowCount = visibleWindows.size();
1297                 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
1298 
1299                 // Iterate until we figure out what is touchable for the entire screen.
1300                 for (int i = visibleWindowCount - 1; i >= 0; i--) {
1301                     final WindowState windowState = visibleWindows.valueAt(i);
1302                     final Region regionInScreen = new Region();
1303                     computeWindowRegionInScreen(windowState, regionInScreen);
1304 
1305                     if (windowMattersToAccessibility(windowState, regionInScreen, unaccountedSpace,
1306                             skipRemainingWindowsForTasks)) {
1307                         addPopulatedWindowInfo(windowState, regionInScreen, windows, addedWindows);
1308                         updateUnaccountedSpace(windowState, regionInScreen, unaccountedSpace,
1309                                 skipRemainingWindowsForTasks);
1310                         focusedWindowAdded |= windowState.isFocused();
1311                     } else if (isUntouchableNavigationBar(windowState, mTempRegion1)) {
1312                         // If this widow is navigation bar without touchable region, accounting the
1313                         // region of navigation bar inset because all touch events from this region
1314                         // would be received by launcher, i.e. this region is a un-touchable one
1315                         // for the application.
1316                         unaccountedSpace.op(getNavBarInsets(dc), unaccountedSpace,
1317                                 Region.Op.REVERSE_DIFFERENCE);
1318                     }
1319 
1320                     if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
1321                         break;
1322                     }
1323                 }
1324 
1325                 for (int i = dc.mShellRoots.size() - 1; i >= 0; --i) {
1326                     final WindowInfo info = dc.mShellRoots.valueAt(i).getWindowInfo();
1327                     if (info == null) {
1328                         continue;
1329                     }
1330                     info.layer = addedWindows.size();
1331                     windows.add(info);
1332                     addedWindows.add(info.token);
1333                 }
1334 
1335                 // Remove child/parent references to windows that were not added.
1336                 final int windowCount = windows.size();
1337                 for (int i = 0; i < windowCount; i++) {
1338                     WindowInfo window = windows.get(i);
1339                     if (!addedWindows.contains(window.parentToken)) {
1340                         window.parentToken = null;
1341                     }
1342                     if (window.childTokens != null) {
1343                         final int childTokenCount = window.childTokens.size();
1344                         for (int j = childTokenCount - 1; j >= 0; j--) {
1345                             if (!addedWindows.contains(window.childTokens.get(j))) {
1346                                 window.childTokens.remove(j);
1347                             }
1348                         }
1349                         // Leave the child token list if empty.
1350                     }
1351                 }
1352 
1353                 visibleWindows.clear();
1354                 addedWindows.clear();
1355 
1356                 // Gets the top focused display Id and window token for supporting multi-display.
1357                 topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId();
1358                 topFocusedWindowToken = topFocusedWindowState.mClient.asBinder();
1359             }
1360             mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId,
1361                     topFocusedWindowToken, windows);
1362 
1363             // Recycle the windows as we do not need them.
1364             clearAndRecycleWindows(windows);
1365         }
1366 
windowMattersToAccessibility(WindowState windowState, Region regionInScreen, Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks)1367         private boolean windowMattersToAccessibility(WindowState windowState,
1368                 Region regionInScreen, Region unaccountedSpace,
1369                 HashSet<Integer> skipRemainingWindowsForTasks) {
1370             final RecentsAnimationController controller = mService.getRecentsAnimationController();
1371             if (controller != null && controller.shouldIgnoreForAccessibility(windowState)) {
1372                 return false;
1373             }
1374 
1375             if (windowState.isFocused()) {
1376                 return true;
1377             }
1378 
1379             // If the window is part of a task that we're finished with - ignore.
1380             final Task task = windowState.getTask();
1381             if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
1382                 return false;
1383             }
1384 
1385             // Ignore non-touchable windows, except the split-screen divider, which is
1386             // occasionally non-touchable but still useful for identifying split-screen
1387             // mode.
1388             if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
1389                     && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) {
1390                 return false;
1391             }
1392 
1393             // If the window is completely covered by other windows - ignore.
1394             if (unaccountedSpace.quickReject(regionInScreen)) {
1395                 return false;
1396             }
1397 
1398             // Add windows of certain types not covered by modal windows.
1399             if (isReportedWindowType(windowState.mAttrs.type)) {
1400                 return true;
1401             }
1402 
1403             return false;
1404         }
1405 
updateUnaccountedSpace(WindowState windowState, Region regionInScreen, Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks)1406         private void updateUnaccountedSpace(WindowState windowState, Region regionInScreen,
1407                 Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) {
1408             if (windowState.mAttrs.type
1409                     != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
1410 
1411                 // Account for the space this window takes if the window
1412                 // is not an accessibility overlay which does not change
1413                 // the reported windows.
1414                 unaccountedSpace.op(regionInScreen, unaccountedSpace,
1415                         Region.Op.REVERSE_DIFFERENCE);
1416 
1417                 // If a window is modal it prevents other windows from being touched
1418                 if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1419                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
1420                     if (!windowState.hasTapExcludeRegion()) {
1421                         // Account for all space in the task, whether the windows in it are
1422                         // touchable or not. The modal window blocks all touches from the task's
1423                         // area.
1424                         unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
1425                                 Region.Op.REVERSE_DIFFERENCE);
1426                     } else {
1427                         // If a window has tap exclude region, we need to account it.
1428                         final Region displayRegion = new Region(windowState.getDisplayFrameLw());
1429                         final Region tapExcludeRegion = new Region();
1430                         windowState.getTapExcludeRegion(tapExcludeRegion);
1431                         displayRegion.op(tapExcludeRegion, displayRegion,
1432                                 Region.Op.REVERSE_DIFFERENCE);
1433                         unaccountedSpace.op(displayRegion, unaccountedSpace,
1434                                 Region.Op.REVERSE_DIFFERENCE);
1435                     }
1436 
1437                     final Task task = windowState.getTask();
1438                     if (task != null) {
1439                         // If the window is associated with a particular task, we can skip the
1440                         // rest of the windows for that task.
1441                         skipRemainingWindowsForTasks.add(task.mTaskId);
1442                     } else if (!windowState.hasTapExcludeRegion()) {
1443                         // If the window is not associated with a particular task, then it is
1444                         // globally modal. In this case we can skip all remaining windows when
1445                         // it doesn't has tap exclude region.
1446                         unaccountedSpace.setEmpty();
1447                     }
1448                 }
1449             }
1450         }
1451 
computeWindowRegionInScreen(WindowState windowState, Region outRegion)1452         private void computeWindowRegionInScreen(WindowState windowState, Region outRegion) {
1453             // Get the touchable frame.
1454             Region touchableRegion = mTempRegion1;
1455             windowState.getTouchableRegion(touchableRegion);
1456 
1457             // Map the frame to get what appears on the screen.
1458             Matrix matrix = mTempMatrix;
1459             populateTransformationMatrixLocked(windowState, matrix);
1460 
1461             forEachRect(touchableRegion, rect -> {
1462                 // Move to origin as all transforms are captured by the matrix.
1463                 RectF windowFrame = mTempRectF;
1464                 windowFrame.set(rect);
1465                 windowFrame.offset(-windowState.getFrameLw().left, -windowState.getFrameLw().top);
1466 
1467                 matrix.mapRect(windowFrame);
1468 
1469                 // Union all rects.
1470                 outRegion.union(new Rect((int) windowFrame.left, (int) windowFrame.top,
1471                         (int) windowFrame.right, (int) windowFrame.bottom));
1472             });
1473         }
1474 
addPopulatedWindowInfo(WindowState windowState, Region regionInScreen, List<WindowInfo> out, Set<IBinder> tokenOut)1475         private static void addPopulatedWindowInfo(WindowState windowState, Region regionInScreen,
1476                 List<WindowInfo> out, Set<IBinder> tokenOut) {
1477             final WindowInfo window = windowState.getWindowInfo();
1478             window.regionInScreen.set(regionInScreen);
1479             window.layer = tokenOut.size();
1480             out.add(window);
1481             tokenOut.add(window.token);
1482         }
1483 
clearAndRecycleWindows(List<WindowInfo> windows)1484         private static void clearAndRecycleWindows(List<WindowInfo> windows) {
1485             final int windowCount = windows.size();
1486             for (int i = windowCount - 1; i >= 0; i--) {
1487                 windows.remove(i).recycle();
1488             }
1489         }
1490 
isReportedWindowType(int windowType)1491         private static boolean isReportedWindowType(int windowType) {
1492             return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
1493                     && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
1494                     && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
1495                     && windowType != WindowManager.LayoutParams.TYPE_DRAG
1496                     && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
1497                     && windowType != WindowManager.LayoutParams.TYPE_POINTER
1498                     && windowType != TYPE_MAGNIFICATION_OVERLAY
1499                     && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
1500                     && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
1501                     && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
1502         }
1503 
populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows)1504         private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
1505             final List<WindowState> tempWindowStatesList = new ArrayList<>();
1506             final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
1507             if (dc == null) {
1508                 return;
1509             }
1510 
1511             dc.forAllWindows(w -> {
1512                 if (w.isVisibleLw()) {
1513                     tempWindowStatesList.add(w);
1514                 }
1515             }, false /* traverseTopToBottom */);
1516             // Insert the re-parented windows in another display below their parents in
1517             // default display.
1518             mService.mRoot.forAllWindows(w -> {
1519                 final WindowState parentWindow = findRootDisplayParentWindow(w);
1520                 if (parentWindow == null) {
1521                     return;
1522                 }
1523 
1524                 if (w.isVisibleLw() && tempWindowStatesList.contains(parentWindow)) {
1525                     tempWindowStatesList.add(tempWindowStatesList.lastIndexOf(parentWindow), w);
1526                 }
1527             }, false /* traverseTopToBottom */);
1528             for (int i = 0; i < tempWindowStatesList.size(); i++) {
1529                 outWindows.put(i, tempWindowStatesList.get(i));
1530             }
1531         }
1532 
findRootDisplayParentWindow(WindowState win)1533         private WindowState findRootDisplayParentWindow(WindowState win) {
1534             WindowState displayParentWindow = win.getDisplayContent().getParentWindow();
1535             if (displayParentWindow == null) {
1536                 return null;
1537             }
1538             WindowState candidate = displayParentWindow;
1539             while (candidate != null) {
1540                 displayParentWindow = candidate;
1541                 candidate = displayParentWindow.getDisplayContent().getParentWindow();
1542             }
1543             return displayParentWindow;
1544         }
1545 
getTopFocusWindow()1546         private WindowState getTopFocusWindow() {
1547             return mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus;
1548         }
1549 
1550         private class MyHandler extends Handler {
1551             public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1;
1552 
MyHandler(Looper looper)1553             public MyHandler(Looper looper) {
1554                 super(looper, null, false);
1555             }
1556 
1557             @Override
1558             @SuppressWarnings("unchecked")
handleMessage(Message message)1559             public void handleMessage(Message message) {
1560                 switch (message.what) {
1561                     case MESSAGE_COMPUTE_CHANGED_WINDOWS: {
1562                         computeChangedWindows(false);
1563                     } break;
1564                 }
1565             }
1566         }
1567     }
1568 }
1569