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