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 com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
20 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
21 
22 import android.animation.ObjectAnimator;
23 import android.animation.ValueAnimator;
24 import android.annotation.NonNull;
25 import android.app.Service;
26 import android.content.Context;
27 import android.graphics.Canvas;
28 import android.graphics.Color;
29 import android.graphics.Matrix;
30 import android.graphics.Paint;
31 import android.graphics.Path;
32 import android.graphics.PixelFormat;
33 import android.graphics.Point;
34 import android.graphics.PorterDuff.Mode;
35 import android.graphics.Rect;
36 import android.graphics.RectF;
37 import android.graphics.Region;
38 import android.os.Handler;
39 import android.os.IBinder;
40 import android.os.Looper;
41 import android.os.Message;
42 import android.text.TextUtils;
43 import android.util.ArraySet;
44 import android.util.Log;
45 import android.util.Slog;
46 import android.util.SparseArray;
47 import android.util.TypedValue;
48 import android.view.MagnificationSpec;
49 import android.view.Surface;
50 import android.view.Surface.OutOfResourcesException;
51 import android.view.SurfaceControl;
52 import android.view.ViewConfiguration;
53 import android.view.WindowInfo;
54 import android.view.WindowManager;
55 import android.view.WindowManagerInternal.MagnificationCallbacks;
56 import android.view.WindowManagerInternal.WindowsForAccessibilityCallback;
57 import android.view.WindowManagerPolicy;
58 import android.view.animation.DecelerateInterpolator;
59 import android.view.animation.Interpolator;
60 
61 import com.android.internal.R;
62 import com.android.internal.os.SomeArgs;
63 
64 import java.util.ArrayList;
65 import java.util.HashSet;
66 import java.util.List;
67 import java.util.Set;
68 
69 /**
70  * This class contains the accessibility related logic of the window manger.
71  */
72 final class AccessibilityController {
73 
74     private final WindowManagerService mWindowManagerService;
75 
76     private static final float[] sTempFloats = new float[9];
77 
AccessibilityController(WindowManagerService service)78     public AccessibilityController(WindowManagerService service) {
79         mWindowManagerService = service;
80     }
81 
82     private DisplayMagnifier mDisplayMagnifier;
83 
84     private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver;
85 
setMagnificationCallbacksLocked(MagnificationCallbacks callbacks)86     public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) {
87         if (callbacks != null) {
88             if (mDisplayMagnifier != null) {
89                 throw new IllegalStateException("Magnification callbacks already set!");
90             }
91             mDisplayMagnifier = new DisplayMagnifier(mWindowManagerService, callbacks);
92         } else {
93             if  (mDisplayMagnifier == null) {
94                 throw new IllegalStateException("Magnification callbacks already cleared!");
95             }
96             mDisplayMagnifier.destroyLocked();
97             mDisplayMagnifier = null;
98         }
99     }
100 
setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback)101     public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
102         if (callback != null) {
103             if (mWindowsForAccessibilityObserver != null) {
104                 throw new IllegalStateException(
105                         "Windows for accessibility callback already set!");
106             }
107             mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver(
108                     mWindowManagerService, callback);
109         } else {
110             if (mWindowsForAccessibilityObserver == null) {
111                 throw new IllegalStateException(
112                         "Windows for accessibility callback already cleared!");
113             }
114             mWindowsForAccessibilityObserver = null;
115         }
116     }
117 
performComputeChangedWindowsNotLocked()118     public void performComputeChangedWindowsNotLocked() {
119         WindowsForAccessibilityObserver observer = null;
120         synchronized (mWindowManagerService) {
121             observer = mWindowsForAccessibilityObserver;
122         }
123         if (observer != null) {
124             observer.performComputeChangedWindowsNotLocked();
125         }
126     }
127 
setMagnificationSpecLocked(MagnificationSpec spec)128     public void setMagnificationSpecLocked(MagnificationSpec spec) {
129         if (mDisplayMagnifier != null) {
130             mDisplayMagnifier.setMagnificationSpecLocked(spec);
131         }
132         if (mWindowsForAccessibilityObserver != null) {
133             mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
134         }
135     }
136 
getMagnificationRegionLocked(Region outMagnificationRegion)137     public void getMagnificationRegionLocked(Region outMagnificationRegion) {
138         if (mDisplayMagnifier != null) {
139             mDisplayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
140         }
141     }
142 
onRectangleOnScreenRequestedLocked(Rect rectangle)143     public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
144         if (mDisplayMagnifier != null) {
145             mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
146         }
147         // Not relevant for the window observer.
148     }
149 
onWindowLayersChangedLocked()150     public void onWindowLayersChangedLocked() {
151         if (mDisplayMagnifier != null) {
152             mDisplayMagnifier.onWindowLayersChangedLocked();
153         }
154         if (mWindowsForAccessibilityObserver != null) {
155             mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
156         }
157     }
158 
onRotationChangedLocked(DisplayContent displayContent)159     public void onRotationChangedLocked(DisplayContent displayContent) {
160         if (mDisplayMagnifier != null) {
161             mDisplayMagnifier.onRotationChangedLocked(displayContent);
162         }
163         if (mWindowsForAccessibilityObserver != null) {
164             mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
165         }
166     }
167 
onAppWindowTransitionLocked(WindowState windowState, int transition)168     public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
169         if (mDisplayMagnifier != null) {
170             mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition);
171         }
172         // Not relevant for the window observer.
173     }
174 
onWindowTransitionLocked(WindowState windowState, int transition)175     public void onWindowTransitionLocked(WindowState windowState, int transition) {
176         if (mDisplayMagnifier != null) {
177             mDisplayMagnifier.onWindowTransitionLocked(windowState, transition);
178         }
179         if (mWindowsForAccessibilityObserver != null) {
180             mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
181         }
182     }
183 
onWindowFocusChangedNotLocked()184     public void onWindowFocusChangedNotLocked() {
185         // Not relevant for the display magnifier.
186 
187         WindowsForAccessibilityObserver observer = null;
188         synchronized (mWindowManagerService) {
189             observer = mWindowsForAccessibilityObserver;
190         }
191         if (observer != null) {
192             observer.performComputeChangedWindowsNotLocked();
193         }
194     }
195 
196 
onSomeWindowResizedOrMovedLocked()197     public void onSomeWindowResizedOrMovedLocked() {
198         // Not relevant for the display magnifier.
199 
200         if (mWindowsForAccessibilityObserver != null) {
201             mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
202         }
203     }
204 
205     /** NOTE: This has to be called within a surface transaction. */
drawMagnifiedRegionBorderIfNeededLocked()206     public void drawMagnifiedRegionBorderIfNeededLocked() {
207         if (mDisplayMagnifier != null) {
208             mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
209         }
210         // Not relevant for the window observer.
211     }
212 
getMagnificationSpecForWindowLocked(WindowState windowState)213     public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
214         if (mDisplayMagnifier != null) {
215             return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
216         }
217         return null;
218     }
219 
hasCallbacksLocked()220     public boolean hasCallbacksLocked() {
221         return (mDisplayMagnifier != null
222                 || mWindowsForAccessibilityObserver != null);
223     }
224 
setForceShowMagnifiableBoundsLocked(boolean show)225     public void setForceShowMagnifiableBoundsLocked(boolean show) {
226         if (mDisplayMagnifier != null) {
227             mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show);
228             mDisplayMagnifier.showMagnificationBoundsIfNeeded();
229         }
230     }
231 
populateTransformationMatrixLocked(WindowState windowState, Matrix outMatrix)232     private static void populateTransformationMatrixLocked(WindowState windowState,
233             Matrix outMatrix) {
234         sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
235         sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
236         sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDtDy;
237         sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDsDy;
238         sTempFloats[Matrix.MTRANS_X] = windowState.mShownPosition.x;
239         sTempFloats[Matrix.MTRANS_Y] = windowState.mShownPosition.y;
240         sTempFloats[Matrix.MPERSP_0] = 0;
241         sTempFloats[Matrix.MPERSP_1] = 0;
242         sTempFloats[Matrix.MPERSP_2] = 1;
243         outMatrix.setValues(sTempFloats);
244     }
245 
246     /**
247      * This class encapsulates the functionality related to display magnification.
248      */
249     private static final class DisplayMagnifier {
250 
251         private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM;
252 
253         private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
254         private static final boolean DEBUG_ROTATION = false;
255         private static final boolean DEBUG_LAYERS = false;
256         private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
257         private static final boolean DEBUG_VIEWPORT_WINDOW = false;
258 
259         private final Rect mTempRect1 = new Rect();
260         private final Rect mTempRect2 = new Rect();
261 
262         private final Region mTempRegion1 = new Region();
263         private final Region mTempRegion2 = new Region();
264         private final Region mTempRegion3 = new Region();
265         private final Region mTempRegion4 = new Region();
266 
267         private final Context mContext;
268         private final WindowManagerService mWindowManagerService;
269         private final MagnifiedViewport mMagnifedViewport;
270         private final Handler mHandler;
271 
272         private final MagnificationCallbacks mCallbacks;
273 
274         private final long mLongAnimationDuration;
275 
276         private boolean mForceShowMagnifiableBounds = false;
277 
DisplayMagnifier(WindowManagerService windowManagerService, MagnificationCallbacks callbacks)278         public DisplayMagnifier(WindowManagerService windowManagerService,
279                 MagnificationCallbacks callbacks) {
280             mContext = windowManagerService.mContext;
281             mWindowManagerService = windowManagerService;
282             mCallbacks = callbacks;
283             mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
284             mMagnifedViewport = new MagnifiedViewport();
285             mLongAnimationDuration = mContext.getResources().getInteger(
286                     com.android.internal.R.integer.config_longAnimTime);
287         }
288 
setMagnificationSpecLocked(MagnificationSpec spec)289         public void setMagnificationSpecLocked(MagnificationSpec spec) {
290             mMagnifedViewport.updateMagnificationSpecLocked(spec);
291             mMagnifedViewport.recomputeBoundsLocked();
292             mWindowManagerService.scheduleAnimationLocked();
293         }
294 
setForceShowMagnifiableBoundsLocked(boolean show)295         public void setForceShowMagnifiableBoundsLocked(boolean show) {
296             mForceShowMagnifiableBounds = show;
297             mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true);
298         }
299 
isForceShowingMagnifiableBoundsLocked()300         public boolean isForceShowingMagnifiableBoundsLocked() {
301             return mForceShowMagnifiableBounds;
302         }
303 
onRectangleOnScreenRequestedLocked(Rect rectangle)304         public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
305             if (DEBUG_RECTANGLE_REQUESTED) {
306                 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
307             }
308             if (!mMagnifedViewport.isMagnifyingLocked()) {
309                 return;
310             }
311             Rect magnifiedRegionBounds = mTempRect2;
312             mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
313             if (magnifiedRegionBounds.contains(rectangle)) {
314                 return;
315             }
316             SomeArgs args = SomeArgs.obtain();
317             args.argi1 = rectangle.left;
318             args.argi2 = rectangle.top;
319             args.argi3 = rectangle.right;
320             args.argi4 = rectangle.bottom;
321             mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
322                     args).sendToTarget();
323         }
324 
onWindowLayersChangedLocked()325         public void onWindowLayersChangedLocked() {
326             if (DEBUG_LAYERS) {
327                 Slog.i(LOG_TAG, "Layers changed.");
328             }
329             mMagnifedViewport.recomputeBoundsLocked();
330             mWindowManagerService.scheduleAnimationLocked();
331         }
332 
onRotationChangedLocked(DisplayContent displayContent)333         public void onRotationChangedLocked(DisplayContent displayContent) {
334             if (DEBUG_ROTATION) {
335                 final int rotation = displayContent.getRotation();
336                 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
337                         + " displayId: " + displayContent.getDisplayId());
338             }
339             mMagnifedViewport.onRotationChangedLocked();
340             mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
341         }
342 
onAppWindowTransitionLocked(WindowState windowState, int transition)343         public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
344             if (DEBUG_WINDOW_TRANSITIONS) {
345                 Slog.i(LOG_TAG, "Window transition: "
346                         + AppTransition.appTransitionToString(transition)
347                         + " displayId: " + windowState.getDisplayId());
348             }
349             final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
350             if (magnifying) {
351                 switch (transition) {
352                     case AppTransition.TRANSIT_ACTIVITY_OPEN:
353                     case AppTransition.TRANSIT_TASK_OPEN:
354                     case AppTransition.TRANSIT_TASK_TO_FRONT:
355                     case AppTransition.TRANSIT_WALLPAPER_OPEN:
356                     case AppTransition.TRANSIT_WALLPAPER_CLOSE:
357                     case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
358                         mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
359                     }
360                 }
361             }
362         }
363 
onWindowTransitionLocked(WindowState windowState, int transition)364         public void onWindowTransitionLocked(WindowState windowState, int transition) {
365             if (DEBUG_WINDOW_TRANSITIONS) {
366                 Slog.i(LOG_TAG, "Window transition: "
367                         + AppTransition.appTransitionToString(transition)
368                         + " displayId: " + windowState.getDisplayId());
369             }
370             final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
371             final int type = windowState.mAttrs.type;
372             switch (transition) {
373                 case WindowManagerPolicy.TRANSIT_ENTER:
374                 case WindowManagerPolicy.TRANSIT_SHOW: {
375                     if (!magnifying) {
376                         break;
377                     }
378                     switch (type) {
379                         case WindowManager.LayoutParams.TYPE_APPLICATION:
380                         case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
381                         case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
382                         case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
383                         case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
384                         case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
385                         case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
386                         case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
387                         case WindowManager.LayoutParams.TYPE_PHONE:
388                         case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
389                         case WindowManager.LayoutParams.TYPE_TOAST:
390                         case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
391                         case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
392                         case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
393                         case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
394                         case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
395                         case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
396                         case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
397                         case WindowManager.LayoutParams.TYPE_QS_DIALOG:
398                         case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
399                             Rect magnifiedRegionBounds = mTempRect2;
400                             mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
401                                     magnifiedRegionBounds);
402                             Rect touchableRegionBounds = mTempRect1;
403                             windowState.getTouchableRegion(mTempRegion1);
404                             mTempRegion1.getBounds(touchableRegionBounds);
405                             if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
406                                 mCallbacks.onRectangleOnScreenRequested(
407                                         touchableRegionBounds.left,
408                                         touchableRegionBounds.top,
409                                         touchableRegionBounds.right,
410                                         touchableRegionBounds.bottom);
411                             }
412                         } break;
413                     } break;
414                 }
415             }
416         }
417 
getMagnificationSpecForWindowLocked(WindowState windowState)418         public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
419             MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
420             if (spec != null && !spec.isNop()) {
421                 WindowManagerPolicy policy = mWindowManagerService.mPolicy;
422                 final int windowType = windowState.mAttrs.type;
423                 if (!policy.isTopLevelWindow(windowType) && windowState.isChildWindow()
424                         && !policy.canMagnifyWindow(windowType)) {
425                     return null;
426                 }
427                 if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
428                     return null;
429                 }
430             }
431             return spec;
432         }
433 
getMagnificationRegionLocked(Region outMagnificationRegion)434         public void getMagnificationRegionLocked(Region outMagnificationRegion) {
435             mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
436         }
437 
destroyLocked()438         public void destroyLocked() {
439             mMagnifedViewport.destroyWindow();
440         }
441 
442         // Can be called outside of a surface transaction
showMagnificationBoundsIfNeeded()443         public void showMagnificationBoundsIfNeeded() {
444             mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
445                     .sendToTarget();
446         }
447 
448         /** NOTE: This has to be called within a surface transaction. */
drawMagnifiedRegionBorderIfNeededLocked()449         public void drawMagnifiedRegionBorderIfNeededLocked() {
450             mMagnifedViewport.drawWindowIfNeededLocked();
451         }
452 
453         private final class MagnifiedViewport {
454 
455             private final SparseArray<WindowState> mTempWindowStates =
456                     new SparseArray<WindowState>();
457 
458             private final RectF mTempRectF = new RectF();
459 
460             private final Point mTempPoint = new Point();
461 
462             private final Matrix mTempMatrix = new Matrix();
463 
464             private final Region mMagnificationRegion = new Region();
465             private final Region mOldMagnificationRegion = new Region();
466 
467             private final Path mCircularPath;
468 
469             private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
470 
471             private final WindowManager mWindowManager;
472 
473             private final float mBorderWidth;
474             private final int mHalfBorderWidth;
475             private final int mDrawBorderInset;
476 
477             private final ViewportWindow mWindow;
478 
479             private boolean mFullRedrawNeeded;
480 
MagnifiedViewport()481             public MagnifiedViewport() {
482                 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
483                 mBorderWidth = mContext.getResources().getDimension(
484                         com.android.internal.R.dimen.accessibility_magnification_indicator_width);
485                 mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
486                 mDrawBorderInset = (int) mBorderWidth / 2;
487                 mWindow = new ViewportWindow(mContext);
488 
489                 if (mContext.getResources().getConfiguration().isScreenRound()) {
490                     mCircularPath = new Path();
491                     mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
492                     final int centerXY = mTempPoint.x / 2;
493                     mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
494                 } else {
495                     mCircularPath = null;
496                 }
497 
498                 recomputeBoundsLocked();
499             }
500 
getMagnificationRegionLocked(@onNull Region outMagnificationRegion)501             public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) {
502                 outMagnificationRegion.set(mMagnificationRegion);
503             }
504 
updateMagnificationSpecLocked(MagnificationSpec spec)505             public void updateMagnificationSpecLocked(MagnificationSpec spec) {
506                 if (spec != null) {
507                     mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
508                 } else {
509                     mMagnificationSpec.clear();
510                 }
511                 // If this message is pending we are in a rotation animation and do not want
512                 // to show the border. We will do so when the pending message is handled.
513                 if (!mHandler.hasMessages(
514                         MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
515                     setMagnifiedRegionBorderShownLocked(
516                             isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true);
517                 }
518             }
519 
recomputeBoundsLocked()520             public void recomputeBoundsLocked() {
521                 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
522                 final int screenWidth = mTempPoint.x;
523                 final int screenHeight = mTempPoint.y;
524 
525                 mMagnificationRegion.set(0, 0, 0, 0);
526                 final Region availableBounds = mTempRegion1;
527                 availableBounds.set(0, 0, screenWidth, screenHeight);
528 
529                 if (mCircularPath != null) {
530                     availableBounds.setPath(mCircularPath, availableBounds);
531                 }
532 
533                 Region nonMagnifiedBounds = mTempRegion4;
534                 nonMagnifiedBounds.set(0, 0, 0, 0);
535 
536                 SparseArray<WindowState> visibleWindows = mTempWindowStates;
537                 visibleWindows.clear();
538                 populateWindowsOnScreenLocked(visibleWindows);
539 
540                 final int visibleWindowCount = visibleWindows.size();
541                 for (int i = visibleWindowCount - 1; i >= 0; i--) {
542                     WindowState windowState = visibleWindows.valueAt(i);
543                     if (windowState.mAttrs.type == WindowManager
544                             .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
545                         continue;
546                     }
547 
548                     // Consider the touchable portion of the window
549                     Matrix matrix = mTempMatrix;
550                     populateTransformationMatrixLocked(windowState, matrix);
551                     Region touchableRegion = mTempRegion3;
552                     windowState.getTouchableRegion(touchableRegion);
553                     Rect touchableFrame = mTempRect1;
554                     touchableRegion.getBounds(touchableFrame);
555                     RectF windowFrame = mTempRectF;
556                     windowFrame.set(touchableFrame);
557                     windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
558                     matrix.mapRect(windowFrame);
559                     Region windowBounds = mTempRegion2;
560                     windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
561                             (int) windowFrame.right, (int) windowFrame.bottom);
562                     // Only update new regions
563                     Region portionOfWindowAlreadyAccountedFor = mTempRegion3;
564                     portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion);
565                     portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
566                     windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
567 
568                     if (mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
569                         mMagnificationRegion.op(windowBounds, Region.Op.UNION);
570                         mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
571                     } else {
572                         nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
573                         availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
574                     }
575 
576                     // Update accounted bounds
577                     Region accountedBounds = mTempRegion2;
578                     accountedBounds.set(mMagnificationRegion);
579                     accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
580                     accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
581 
582                     if (accountedBounds.isRect()) {
583                         Rect accountedFrame = mTempRect1;
584                         accountedBounds.getBounds(accountedFrame);
585                         if (accountedFrame.width() == screenWidth
586                                 && accountedFrame.height() == screenHeight) {
587                             break;
588                         }
589                     }
590                 }
591 
592                 visibleWindows.clear();
593 
594                 mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
595                         screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
596                         Region.Op.INTERSECT);
597 
598                 final boolean magnifiedChanged =
599                         !mOldMagnificationRegion.equals(mMagnificationRegion);
600                 if (magnifiedChanged) {
601                     mWindow.setBounds(mMagnificationRegion);
602                     final Rect dirtyRect = mTempRect1;
603                     if (mFullRedrawNeeded) {
604                         mFullRedrawNeeded = false;
605                         dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
606                                 screenWidth - mDrawBorderInset,
607                                 screenHeight - mDrawBorderInset);
608                         mWindow.invalidate(dirtyRect);
609                     } else {
610                         final Region dirtyRegion = mTempRegion3;
611                         dirtyRegion.set(mMagnificationRegion);
612                         dirtyRegion.op(mOldMagnificationRegion, Region.Op.UNION);
613                         dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
614                         dirtyRegion.getBounds(dirtyRect);
615                         mWindow.invalidate(dirtyRect);
616                     }
617 
618                     mOldMagnificationRegion.set(mMagnificationRegion);
619                     final SomeArgs args = SomeArgs.obtain();
620                     args.arg1 = Region.obtain(mMagnificationRegion);
621                     mHandler.obtainMessage(
622                             MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args)
623                             .sendToTarget();
624                 }
625             }
626 
onRotationChangedLocked()627             public void onRotationChangedLocked() {
628                 // If we are showing the magnification border, hide it immediately so
629                 // the user does not see strange artifacts during rotation. The screenshot
630                 // used for rotation already has the border. After the rotation is complete
631                 // we will show the border.
632                 if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) {
633                     setMagnifiedRegionBorderShownLocked(false, false);
634                     final long delay = (long) (mLongAnimationDuration
635                             * mWindowManagerService.getWindowAnimationScaleLocked());
636                     Message message = mHandler.obtainMessage(
637                             MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
638                     mHandler.sendMessageDelayed(message, delay);
639                 }
640                 recomputeBoundsLocked();
641                 mWindow.updateSize();
642             }
643 
setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate)644             public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
645                 if (shown) {
646                     mFullRedrawNeeded = true;
647                     mOldMagnificationRegion.set(0, 0, 0, 0);
648                 }
649                 mWindow.setShown(shown, animate);
650             }
651 
getMagnifiedFrameInContentCoordsLocked(Rect rect)652             public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
653                 MagnificationSpec spec = mMagnificationSpec;
654                 mMagnificationRegion.getBounds(rect);
655                 rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
656                 rect.scale(1.0f / spec.scale);
657             }
658 
isMagnifyingLocked()659             public boolean isMagnifyingLocked() {
660                 return mMagnificationSpec.scale > 1.0f;
661             }
662 
getMagnificationSpecLocked()663             public MagnificationSpec getMagnificationSpecLocked() {
664                 return mMagnificationSpec;
665             }
666 
667             /** NOTE: This has to be called within a surface transaction. */
drawWindowIfNeededLocked()668             public void drawWindowIfNeededLocked() {
669                 recomputeBoundsLocked();
670                 mWindow.drawIfNeeded();
671             }
672 
destroyWindow()673             public void destroyWindow() {
674                 mWindow.releaseSurface();
675             }
676 
populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows)677             private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
678                 final DisplayContent dc = mWindowManagerService.getDefaultDisplayContentLocked();
679                 dc.forAllWindows((w) -> {
680                     if (w.isOnScreen() && w.isVisibleLw()
681                             && !w.mWinAnimator.mEnterAnimationPending) {
682                         outWindows.put(w.mLayer, w);
683                     }
684                 }, false /* traverseTopToBottom */ );
685             }
686 
687             private final class ViewportWindow {
688                 private static final String SURFACE_TITLE = "Magnification Overlay";
689 
690                 private final Region mBounds = new Region();
691                 private final Rect mDirtyRect = new Rect();
692                 private final Paint mPaint = new Paint();
693 
694                 private final SurfaceControl mSurfaceControl;
695                 private final Surface mSurface = new Surface();
696 
697                 private final AnimationController mAnimationController;
698 
699                 private boolean mShown;
700                 private int mAlpha;
701 
702                 private boolean mInvalidated;
703 
ViewportWindow(Context context)704                 public ViewportWindow(Context context) {
705                     SurfaceControl surfaceControl = null;
706                     try {
707                         mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
708                         surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession,
709                                 SURFACE_TITLE, mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT,
710                                 SurfaceControl.HIDDEN);
711                     } catch (OutOfResourcesException oore) {
712                         /* ignore */
713                     }
714                     mSurfaceControl = surfaceControl;
715                     mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
716                             .getLayerStack());
717                     mSurfaceControl.setLayer(mWindowManagerService.mPolicy.getWindowLayerFromTypeLw(
718                             WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
719                             * WindowManagerService.TYPE_LAYER_MULTIPLIER);
720                     mSurfaceControl.setPosition(0, 0);
721                     mSurface.copyFrom(mSurfaceControl);
722 
723                     mAnimationController = new AnimationController(context,
724                             mWindowManagerService.mH.getLooper());
725 
726                     TypedValue typedValue = new TypedValue();
727                     context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
728                             typedValue, true);
729                     final int borderColor = context.getColor(typedValue.resourceId);
730 
731                     mPaint.setStyle(Paint.Style.STROKE);
732                     mPaint.setStrokeWidth(mBorderWidth);
733                     mPaint.setColor(borderColor);
734 
735                     mInvalidated = true;
736                 }
737 
setShown(boolean shown, boolean animate)738                 public void setShown(boolean shown, boolean animate) {
739                     synchronized (mWindowManagerService.mWindowMap) {
740                         if (mShown == shown) {
741                             return;
742                         }
743                         mShown = shown;
744                         mAnimationController.onFrameShownStateChanged(shown, animate);
745                         if (DEBUG_VIEWPORT_WINDOW) {
746                             Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
747                         }
748                     }
749                 }
750 
751                 @SuppressWarnings("unused")
752                 // Called reflectively from an animator.
getAlpha()753                 public int getAlpha() {
754                     synchronized (mWindowManagerService.mWindowMap) {
755                         return mAlpha;
756                     }
757                 }
758 
setAlpha(int alpha)759                 public void setAlpha(int alpha) {
760                     synchronized (mWindowManagerService.mWindowMap) {
761                         if (mAlpha == alpha) {
762                             return;
763                         }
764                         mAlpha = alpha;
765                         invalidate(null);
766                         if (DEBUG_VIEWPORT_WINDOW) {
767                             Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
768                         }
769                     }
770                 }
771 
setBounds(Region bounds)772                 public void setBounds(Region bounds) {
773                     synchronized (mWindowManagerService.mWindowMap) {
774                         if (mBounds.equals(bounds)) {
775                             return;
776                         }
777                         mBounds.set(bounds);
778                         invalidate(mDirtyRect);
779                         if (DEBUG_VIEWPORT_WINDOW) {
780                             Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
781                         }
782                     }
783                 }
784 
updateSize()785                 public void updateSize() {
786                     synchronized (mWindowManagerService.mWindowMap) {
787                         mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
788                         mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
789                         invalidate(mDirtyRect);
790                     }
791                 }
792 
invalidate(Rect dirtyRect)793                 public void invalidate(Rect dirtyRect) {
794                     if (dirtyRect != null) {
795                         mDirtyRect.set(dirtyRect);
796                     } else {
797                         mDirtyRect.setEmpty();
798                     }
799                     mInvalidated = true;
800                     mWindowManagerService.scheduleAnimationLocked();
801                 }
802 
803                 /** NOTE: This has to be called within a surface transaction. */
drawIfNeeded()804                 public void drawIfNeeded() {
805                     synchronized (mWindowManagerService.mWindowMap) {
806                         if (!mInvalidated) {
807                             return;
808                         }
809                         mInvalidated = false;
810                         Canvas canvas = null;
811                         try {
812                             // Empty dirty rectangle means unspecified.
813                             if (mDirtyRect.isEmpty()) {
814                                 mBounds.getBounds(mDirtyRect);
815                             }
816                             mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
817                             canvas = mSurface.lockCanvas(mDirtyRect);
818                             if (DEBUG_VIEWPORT_WINDOW) {
819                                 Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
820                             }
821                         } catch (IllegalArgumentException iae) {
822                             /* ignore */
823                         } catch (Surface.OutOfResourcesException oore) {
824                             /* ignore */
825                         }
826                         if (canvas == null) {
827                             return;
828                         }
829                         if (DEBUG_VIEWPORT_WINDOW) {
830                             Slog.i(LOG_TAG, "Bounds: " + mBounds);
831                         }
832                         canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
833                         mPaint.setAlpha(mAlpha);
834                         Path path = mBounds.getBoundaryPath();
835                         canvas.drawPath(path, mPaint);
836 
837                         mSurface.unlockCanvasAndPost(canvas);
838 
839                         if (mAlpha > 0) {
840                             mSurfaceControl.show();
841                         } else {
842                             mSurfaceControl.hide();
843                         }
844                     }
845                 }
846 
releaseSurface()847                 public void releaseSurface() {
848                     mSurfaceControl.release();
849                     mSurface.release();
850                 }
851 
852                 private final class AnimationController extends Handler {
853                     private static final String PROPERTY_NAME_ALPHA = "alpha";
854 
855                     private static final int MIN_ALPHA = 0;
856                     private static final int MAX_ALPHA = 255;
857 
858                     private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1;
859 
860                     private final ValueAnimator mShowHideFrameAnimator;
861 
AnimationController(Context context, Looper looper)862                     public AnimationController(Context context, Looper looper) {
863                         super(looper);
864                         mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
865                                 PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);
866 
867                         Interpolator interpolator = new DecelerateInterpolator(2.5f);
868                         final long longAnimationDuration = context.getResources().getInteger(
869                                 com.android.internal.R.integer.config_longAnimTime);
870 
871                         mShowHideFrameAnimator.setInterpolator(interpolator);
872                         mShowHideFrameAnimator.setDuration(longAnimationDuration);
873                     }
874 
onFrameShownStateChanged(boolean shown, boolean animate)875                     public void onFrameShownStateChanged(boolean shown, boolean animate) {
876                         obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
877                                 shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
878                     }
879 
880                     @Override
handleMessage(Message message)881                     public void handleMessage(Message message) {
882                         switch (message.what) {
883                             case MSG_FRAME_SHOWN_STATE_CHANGED: {
884                                 final boolean shown = message.arg1 == 1;
885                                 final boolean animate = message.arg2 == 1;
886 
887                                 if (animate) {
888                                     if (mShowHideFrameAnimator.isRunning()) {
889                                         mShowHideFrameAnimator.reverse();
890                                     } else {
891                                         if (shown) {
892                                             mShowHideFrameAnimator.start();
893                                         } else {
894                                             mShowHideFrameAnimator.reverse();
895                                         }
896                                     }
897                                 } else {
898                                     mShowHideFrameAnimator.cancel();
899                                     if (shown) {
900                                         setAlpha(MAX_ALPHA);
901                                     } else {
902                                         setAlpha(MIN_ALPHA);
903                                     }
904                                 }
905                             } break;
906                         }
907                     }
908                 }
909             }
910         }
911 
912         private class MyHandler extends Handler {
913             public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
914             public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
915             public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
916             public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
917             public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
918 
MyHandler(Looper looper)919             public MyHandler(Looper looper) {
920                 super(looper);
921             }
922 
923             @Override
handleMessage(Message message)924             public void handleMessage(Message message) {
925                 switch (message.what) {
926                     case MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED: {
927                         final SomeArgs args = (SomeArgs) message.obj;
928                         final Region magnifiedBounds = (Region) args.arg1;
929                         mCallbacks.onMagnificationRegionChanged(magnifiedBounds);
930                         magnifiedBounds.recycle();
931                     } break;
932 
933                     case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
934                         SomeArgs args = (SomeArgs) message.obj;
935                         final int left = args.argi1;
936                         final int top = args.argi2;
937                         final int right = args.argi3;
938                         final int bottom = args.argi4;
939                         mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
940                         args.recycle();
941                     } break;
942 
943                     case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
944                         mCallbacks.onUserContextChanged();
945                     } break;
946 
947                     case MESSAGE_NOTIFY_ROTATION_CHANGED: {
948                         final int rotation = message.arg1;
949                         mCallbacks.onRotationChanged(rotation);
950                     } break;
951 
952                     case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
953                         synchronized (mWindowManagerService.mWindowMap) {
954                             if (mMagnifedViewport.isMagnifyingLocked()
955                                     || isForceShowingMagnifiableBoundsLocked()) {
956                                 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
957                                 mWindowManagerService.scheduleAnimationLocked();
958                             }
959                         }
960                     } break;
961                 }
962             }
963         }
964     }
965 
966     /**
967      * This class encapsulates the functionality related to computing the windows
968      * reported for accessibility purposes. These windows are all windows a sighted
969      * user can see on the screen.
970      */
971     private static final class WindowsForAccessibilityObserver {
972         private static final String LOG_TAG = TAG_WITH_CLASS_NAME ?
973                 "WindowsForAccessibilityObserver" : TAG_WM;
974 
975         private static final boolean DEBUG = false;
976 
977         private final SparseArray<WindowState> mTempWindowStates =
978                 new SparseArray<WindowState>();
979 
980         private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>();
981 
982         private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>();
983 
984         private final RectF mTempRectF = new RectF();
985 
986         private final Matrix mTempMatrix = new Matrix();
987 
988         private final Point mTempPoint = new Point();
989 
990         private final Rect mTempRect = new Rect();
991 
992         private final Region mTempRegion = new Region();
993 
994         private final Region mTempRegion1 = new Region();
995 
996         private final Context mContext;
997 
998         private final WindowManagerService mWindowManagerService;
999 
1000         private final Handler mHandler;
1001 
1002         private final WindowsForAccessibilityCallback mCallback;
1003 
1004         private final long mRecurringAccessibilityEventsIntervalMillis;
1005 
WindowsForAccessibilityObserver(WindowManagerService windowManagerService, WindowsForAccessibilityCallback callback)1006         public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
1007                 WindowsForAccessibilityCallback callback) {
1008             mContext = windowManagerService.mContext;
1009             mWindowManagerService = windowManagerService;
1010             mCallback = callback;
1011             mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
1012             mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
1013                     .getSendRecurringAccessibilityEventsInterval();
1014             computeChangedWindows();
1015         }
1016 
performComputeChangedWindowsNotLocked()1017         public void performComputeChangedWindowsNotLocked() {
1018             mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
1019             computeChangedWindows();
1020         }
1021 
scheduleComputeChangedWindowsLocked()1022         public void scheduleComputeChangedWindowsLocked() {
1023             if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
1024                 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
1025                         mRecurringAccessibilityEventsIntervalMillis);
1026             }
1027         }
1028 
computeChangedWindows()1029         public void computeChangedWindows() {
1030             if (DEBUG) {
1031                 Slog.i(LOG_TAG, "computeChangedWindows()");
1032             }
1033 
1034             boolean windowsChanged = false;
1035             List<WindowInfo> windows = new ArrayList<WindowInfo>();
1036 
1037             synchronized (mWindowManagerService.mWindowMap) {
1038                 // Do not send the windows if there is no current focus as
1039                 // the window manager is still looking for where to put it.
1040                 // We will do the work when we get a focus change callback.
1041                 if (mWindowManagerService.mCurrentFocus == null) {
1042                     return;
1043                 }
1044 
1045                 WindowManager windowManager = (WindowManager)
1046                         mContext.getSystemService(Context.WINDOW_SERVICE);
1047                 windowManager.getDefaultDisplay().getRealSize(mTempPoint);
1048                 final int screenWidth = mTempPoint.x;
1049                 final int screenHeight = mTempPoint.y;
1050 
1051                 Region unaccountedSpace = mTempRegion;
1052                 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
1053 
1054                 final SparseArray<WindowState> visibleWindows = mTempWindowStates;
1055                 populateVisibleWindowsOnScreenLocked(visibleWindows);
1056                 Set<IBinder> addedWindows = mTempBinderSet;
1057                 addedWindows.clear();
1058 
1059                 boolean focusedWindowAdded = false;
1060 
1061                 final int visibleWindowCount = visibleWindows.size();
1062                 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
1063                 for (int i = visibleWindowCount - 1; i >= 0; i--) {
1064                     final WindowState windowState = visibleWindows.valueAt(i);
1065                     final int flags = windowState.mAttrs.flags;
1066                     final Task task = windowState.getTask();
1067 
1068                     // If the window is part of a task that we're finished with - ignore.
1069                     if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
1070                         continue;
1071                     }
1072 
1073                     // If the window is not touchable - ignore.
1074                     if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
1075                         continue;
1076                     }
1077 
1078                     // Compute the bounds in the screen.
1079                     final Rect boundsInScreen = mTempRect;
1080                     computeWindowBoundsInScreen(windowState, boundsInScreen);
1081 
1082                     // If the window is completely covered by other windows - ignore.
1083                     if (unaccountedSpace.quickReject(boundsInScreen)) {
1084                         continue;
1085                     }
1086 
1087                     // Add windows of certain types not covered by modal windows.
1088                     if (isReportedWindowType(windowState.mAttrs.type)) {
1089                         // Add the window to the ones to be reported.
1090                         WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
1091                         addedWindows.add(window.token);
1092                         windows.add(window);
1093                         if (windowState.isFocused()) {
1094                             focusedWindowAdded = true;
1095                         }
1096                     }
1097 
1098                     // Account for the space this window takes if the window
1099                     // is not an accessibility overlay which does not change
1100                     // the reported windows.
1101                     if (windowState.mAttrs.type !=
1102                             WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
1103                         unaccountedSpace.op(boundsInScreen, unaccountedSpace,
1104                                 Region.Op.REVERSE_DIFFERENCE);
1105                     }
1106 
1107                     // If a window is modal it prevents other windows from being touched
1108                     if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1109                             | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
1110                         // Account for all space in the task, whether the windows in it are
1111                         // touchable or not. The modal window blocks all touches from the task's
1112                         // area.
1113                         unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
1114                                 Region.Op.REVERSE_DIFFERENCE);
1115 
1116                         if (task != null) {
1117                             // If the window is associated with a particular task, we can skip the
1118                             // rest of the windows for that task.
1119                             skipRemainingWindowsForTasks.add(task.mTaskId);
1120                             continue;
1121                         } else {
1122                             // If the window is not associated with a particular task, then it is
1123                             // globally modal. In this case we can skip all remaining windows.
1124                             break;
1125                         }
1126                     }
1127                     // We figured out what is touchable for the entire screen - done.
1128                     if (unaccountedSpace.isEmpty()) {
1129                         break;
1130                     }
1131                 }
1132 
1133                 // Always report the focused window.
1134                 if (!focusedWindowAdded) {
1135                     for (int i = visibleWindowCount - 1; i >= 0; i--) {
1136                         WindowState windowState = visibleWindows.valueAt(i);
1137                         if (windowState.isFocused()) {
1138                             // Compute the bounds in the screen.
1139                             Rect boundsInScreen = mTempRect;
1140                             computeWindowBoundsInScreen(windowState, boundsInScreen);
1141 
1142                             // Add the window to the ones to be reported.
1143                             WindowInfo window = obtainPopulatedWindowInfo(windowState,
1144                                     boundsInScreen);
1145                             addedWindows.add(window.token);
1146                             windows.add(window);
1147                             break;
1148                         }
1149                     }
1150                 }
1151 
1152                 // Remove child/parent references to windows that were not added.
1153                 final int windowCount = windows.size();
1154                 for (int i = 0; i < windowCount; i++) {
1155                     WindowInfo window = windows.get(i);
1156                     if (!addedWindows.contains(window.parentToken)) {
1157                         window.parentToken = null;
1158                     }
1159                     if (window.childTokens != null) {
1160                         final int childTokenCount = window.childTokens.size();
1161                         for (int j = childTokenCount - 1; j >= 0; j--) {
1162                             if (!addedWindows.contains(window.childTokens.get(j))) {
1163                                 window.childTokens.remove(j);
1164                             }
1165                         }
1166                         // Leave the child token list if empty.
1167                     }
1168                 }
1169 
1170                 visibleWindows.clear();
1171                 addedWindows.clear();
1172 
1173                 // We computed the windows and if they changed notify the client.
1174                 if (mOldWindows.size() != windows.size()) {
1175                     // Different size means something changed.
1176                     windowsChanged = true;
1177                 } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
1178                     // Since we always traverse windows from high to low layer
1179                     // the old and new windows at the same index should be the
1180                     // same, otherwise something changed.
1181                     for (int i = 0; i < windowCount; i++) {
1182                         WindowInfo oldWindow = mOldWindows.get(i);
1183                         WindowInfo newWindow = windows.get(i);
1184                         // We do not care for layer changes given the window
1185                         // order does not change. This brings no new information
1186                         // to the clients.
1187                         if (windowChangedNoLayer(oldWindow, newWindow)) {
1188                             windowsChanged = true;
1189                             break;
1190                         }
1191                     }
1192                 }
1193 
1194                 if (windowsChanged) {
1195                     cacheWindows(windows);
1196                 }
1197             }
1198 
1199             // Now we do not hold the lock, so send the windows over.
1200             if (windowsChanged) {
1201                 if (DEBUG) {
1202                     Log.i(LOG_TAG, "Windows changed:" + windows);
1203                 }
1204                 mCallback.onWindowsForAccessibilityChanged(windows);
1205             } else {
1206                 if (DEBUG) {
1207                     Log.i(LOG_TAG, "No windows changed.");
1208                 }
1209             }
1210 
1211             // Recycle the windows as we do not need them.
1212             clearAndRecycleWindows(windows);
1213         }
1214 
computeWindowBoundsInScreen(WindowState windowState, Rect outBounds)1215         private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
1216             // Get the touchable frame.
1217             Region touchableRegion = mTempRegion1;
1218             windowState.getTouchableRegion(touchableRegion);
1219             Rect touchableFrame = mTempRect;
1220             touchableRegion.getBounds(touchableFrame);
1221 
1222             // Move to origin as all transforms are captured by the matrix.
1223             RectF windowFrame = mTempRectF;
1224             windowFrame.set(touchableFrame);
1225             windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
1226 
1227             // Map the frame to get what appears on the screen.
1228             Matrix matrix = mTempMatrix;
1229             populateTransformationMatrixLocked(windowState, matrix);
1230             matrix.mapRect(windowFrame);
1231 
1232             // Got the bounds.
1233             outBounds.set((int) windowFrame.left, (int) windowFrame.top,
1234                     (int) windowFrame.right, (int) windowFrame.bottom);
1235         }
1236 
obtainPopulatedWindowInfo( WindowState windowState, Rect boundsInScreen)1237         private static WindowInfo obtainPopulatedWindowInfo(
1238                 WindowState windowState, Rect boundsInScreen) {
1239             final WindowInfo window = windowState.getWindowInfo();
1240             window.boundsInScreen.set(boundsInScreen);
1241             return window;
1242         }
1243 
cacheWindows(List<WindowInfo> windows)1244         private void cacheWindows(List<WindowInfo> windows) {
1245             final int oldWindowCount = mOldWindows.size();
1246             for (int i = oldWindowCount - 1; i >= 0; i--) {
1247                 mOldWindows.remove(i).recycle();
1248             }
1249             final int newWindowCount = windows.size();
1250             for (int i = 0; i < newWindowCount; i++) {
1251                 WindowInfo newWindow = windows.get(i);
1252                 mOldWindows.add(WindowInfo.obtain(newWindow));
1253             }
1254         }
1255 
windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow)1256         private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
1257             if (oldWindow == newWindow) {
1258                 return false;
1259             }
1260             if (oldWindow == null) {
1261                 return true;
1262             }
1263             if (newWindow == null) {
1264                 return true;
1265             }
1266             if (oldWindow.type != newWindow.type) {
1267                 return true;
1268             }
1269             if (oldWindow.focused != newWindow.focused) {
1270                 return true;
1271             }
1272             if (oldWindow.token == null) {
1273                 if (newWindow.token != null) {
1274                     return true;
1275                 }
1276             } else if (!oldWindow.token.equals(newWindow.token)) {
1277                 return true;
1278             }
1279             if (oldWindow.parentToken == null) {
1280                 if (newWindow.parentToken != null) {
1281                     return true;
1282                 }
1283             } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
1284                 return true;
1285             }
1286             if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
1287                 return true;
1288             }
1289             if (oldWindow.childTokens != null && newWindow.childTokens != null
1290                     && !oldWindow.childTokens.equals(newWindow.childTokens)) {
1291                 return true;
1292             }
1293             if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
1294                 return true;
1295             }
1296             if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
1297                 return true;
1298             }
1299             return false;
1300         }
1301 
clearAndRecycleWindows(List<WindowInfo> windows)1302         private static void clearAndRecycleWindows(List<WindowInfo> windows) {
1303             final int windowCount = windows.size();
1304             for (int i = windowCount - 1; i >= 0; i--) {
1305                 windows.remove(i).recycle();
1306             }
1307         }
1308 
isReportedWindowType(int windowType)1309         private static boolean isReportedWindowType(int windowType) {
1310             return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
1311                     && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
1312                     && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
1313                     && windowType != WindowManager.LayoutParams.TYPE_DRAG
1314                     && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
1315                     && windowType != WindowManager.LayoutParams.TYPE_POINTER
1316                     && windowType != WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY
1317                     && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
1318                     && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
1319                     && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
1320         }
1321 
populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows)1322         private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
1323             final DisplayContent dc = mWindowManagerService.getDefaultDisplayContentLocked();
1324             dc.forAllWindows((w) -> {
1325                 if (w.isVisibleLw()) {
1326                     outWindows.put(w.mLayer, w);
1327                 }
1328             }, false /* traverseTopToBottom */ );
1329         }
1330 
1331         private class MyHandler extends Handler {
1332             public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1;
1333 
MyHandler(Looper looper)1334             public MyHandler(Looper looper) {
1335                 super(looper, null, false);
1336             }
1337 
1338             @Override
1339             @SuppressWarnings("unchecked")
handleMessage(Message message)1340             public void handleMessage(Message message) {
1341                 switch (message.what) {
1342                     case MESSAGE_COMPUTE_CHANGED_WINDOWS: {
1343                         computeChangedWindows();
1344                     } break;
1345                 }
1346             }
1347         }
1348     }
1349 }
1350