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