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