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