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