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