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 android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
     20 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
     21 
     22 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
     23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
     24 
     25 import android.animation.ObjectAnimator;
     26 import android.animation.ValueAnimator;
     27 import android.annotation.NonNull;
     28 import android.app.Service;
     29 import android.content.Context;
     30 import android.graphics.Canvas;
     31 import android.graphics.Color;
     32 import android.graphics.Matrix;
     33 import android.graphics.Paint;
     34 import android.graphics.Path;
     35 import android.graphics.PixelFormat;
     36 import android.graphics.Point;
     37 import android.graphics.PorterDuff.Mode;
     38 import android.graphics.Rect;
     39 import android.graphics.RectF;
     40 import android.graphics.Region;
     41 import android.os.Handler;
     42 import android.os.IBinder;
     43 import android.os.Looper;
     44 import android.os.Message;
     45 import android.text.TextUtils;
     46 import android.util.ArraySet;
     47 import android.util.Log;
     48 import android.util.Slog;
     49 import android.util.SparseArray;
     50 import android.util.TypedValue;
     51 import android.view.MagnificationSpec;
     52 import android.view.Surface;
     53 import android.view.Surface.OutOfResourcesException;
     54 import android.view.SurfaceControl;
     55 import android.view.ViewConfiguration;
     56 import android.view.WindowInfo;
     57 import android.view.WindowManager;
     58 import android.view.WindowManagerInternal.MagnificationCallbacks;
     59 import android.view.WindowManagerInternal.WindowsForAccessibilityCallback;
     60 import android.view.WindowManagerPolicy;
     61 import android.view.animation.DecelerateInterpolator;
     62 import android.view.animation.Interpolator;
     63 
     64 import com.android.internal.R;
     65 import com.android.internal.os.SomeArgs;
     66 
     67 import java.util.ArrayList;
     68 import java.util.HashSet;
     69 import java.util.List;
     70 import java.util.Set;
     71 
     72 /**
     73  * This class contains the accessibility related logic of the window manger.
     74  */
     75 final class AccessibilityController {
     76 
     77     private final WindowManagerService mWindowManagerService;
     78 
     79     private static final float[] sTempFloats = new float[9];
     80 
     81     public AccessibilityController(WindowManagerService service) {
     82         mWindowManagerService = service;
     83     }
     84 
     85     private DisplayMagnifier mDisplayMagnifier;
     86 
     87     private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver;
     88 
     89     public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) {
     90         if (callbacks != null) {
     91             if (mDisplayMagnifier != null) {
     92                 throw new IllegalStateException("Magnification callbacks already set!");
     93             }
     94             mDisplayMagnifier = new DisplayMagnifier(mWindowManagerService, callbacks);
     95         } else {
     96             if  (mDisplayMagnifier == null) {
     97                 throw new IllegalStateException("Magnification callbacks already cleared!");
     98             }
     99             mDisplayMagnifier.destroyLocked();
    100             mDisplayMagnifier = null;
    101         }
    102     }
    103 
    104     public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
    105         if (callback != null) {
    106             if (mWindowsForAccessibilityObserver != null) {
    107                 throw new IllegalStateException(
    108                         "Windows for accessibility callback already set!");
    109             }
    110             mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver(
    111                     mWindowManagerService, callback);
    112         } else {
    113             if (mWindowsForAccessibilityObserver == null) {
    114                 throw new IllegalStateException(
    115                         "Windows for accessibility callback already cleared!");
    116             }
    117             mWindowsForAccessibilityObserver = null;
    118         }
    119     }
    120 
    121     public void performComputeChangedWindowsNotLocked() {
    122         WindowsForAccessibilityObserver observer = null;
    123         synchronized (mWindowManagerService) {
    124             observer = mWindowsForAccessibilityObserver;
    125         }
    126         if (observer != null) {
    127             observer.performComputeChangedWindowsNotLocked();
    128         }
    129     }
    130 
    131     public void setMagnificationSpecLocked(MagnificationSpec spec) {
    132         if (mDisplayMagnifier != null) {
    133             mDisplayMagnifier.setMagnificationSpecLocked(spec);
    134         }
    135         if (mWindowsForAccessibilityObserver != null) {
    136             mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
    137         }
    138     }
    139 
    140     public void getMagnificationRegionLocked(Region outMagnificationRegion) {
    141         if (mDisplayMagnifier != null) {
    142             mDisplayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
    143         }
    144     }
    145 
    146     public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
    147         if (mDisplayMagnifier != null) {
    148             mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
    149         }
    150         // Not relevant for the window observer.
    151     }
    152 
    153     public void onWindowLayersChangedLocked() {
    154         if (mDisplayMagnifier != null) {
    155             mDisplayMagnifier.onWindowLayersChangedLocked();
    156         }
    157         if (mWindowsForAccessibilityObserver != null) {
    158             mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
    159         }
    160     }
    161 
    162     public void onRotationChangedLocked(DisplayContent displayContent) {
    163         if (mDisplayMagnifier != null) {
    164             mDisplayMagnifier.onRotationChangedLocked(displayContent);
    165         }
    166         if (mWindowsForAccessibilityObserver != null) {
    167             mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
    168         }
    169     }
    170 
    171     public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
    172         if (mDisplayMagnifier != null) {
    173             mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition);
    174         }
    175         // Not relevant for the window observer.
    176     }
    177 
    178     public void onWindowTransitionLocked(WindowState windowState, int transition) {
    179         if (mDisplayMagnifier != null) {
    180             mDisplayMagnifier.onWindowTransitionLocked(windowState, transition);
    181         }
    182         if (mWindowsForAccessibilityObserver != null) {
    183             mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
    184         }
    185     }
    186 
    187     public void onWindowFocusChangedNotLocked() {
    188         // Not relevant for the display magnifier.
    189 
    190         WindowsForAccessibilityObserver observer = null;
    191         synchronized (mWindowManagerService) {
    192             observer = mWindowsForAccessibilityObserver;
    193         }
    194         if (observer != null) {
    195             observer.performComputeChangedWindowsNotLocked();
    196         }
    197     }
    198 
    199 
    200     public void onSomeWindowResizedOrMovedLocked() {
    201         // Not relevant for the display magnifier.
    202 
    203         if (mWindowsForAccessibilityObserver != null) {
    204             mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
    205         }
    206     }
    207 
    208     /** NOTE: This has to be called within a surface transaction. */
    209     public void drawMagnifiedRegionBorderIfNeededLocked() {
    210         if (mDisplayMagnifier != null) {
    211             mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
    212         }
    213         // Not relevant for the window observer.
    214     }
    215 
    216     public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
    217         if (mDisplayMagnifier != null) {
    218             return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
    219         }
    220         return null;
    221     }
    222 
    223     public boolean hasCallbacksLocked() {
    224         return (mDisplayMagnifier != null
    225                 || mWindowsForAccessibilityObserver != null);
    226     }
    227 
    228     public void setForceShowMagnifiableBoundsLocked(boolean show) {
    229         if (mDisplayMagnifier != null) {
    230             mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show);
    231             mDisplayMagnifier.showMagnificationBoundsIfNeeded();
    232         }
    233     }
    234 
    235     private static void populateTransformationMatrixLocked(WindowState windowState,
    236             Matrix outMatrix) {
    237         sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
    238         sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
    239         sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDtDy;
    240         sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDsDy;
    241         sTempFloats[Matrix.MTRANS_X] = windowState.mShownPosition.x;
    242         sTempFloats[Matrix.MTRANS_Y] = windowState.mShownPosition.y;
    243         sTempFloats[Matrix.MPERSP_0] = 0;
    244         sTempFloats[Matrix.MPERSP_1] = 0;
    245         sTempFloats[Matrix.MPERSP_2] = 1;
    246         outMatrix.setValues(sTempFloats);
    247     }
    248 
    249     /**
    250      * This class encapsulates the functionality related to display magnification.
    251      */
    252     private static final class DisplayMagnifier {
    253 
    254         private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM;
    255 
    256         private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
    257         private static final boolean DEBUG_ROTATION = false;
    258         private static final boolean DEBUG_LAYERS = false;
    259         private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
    260         private static final boolean DEBUG_VIEWPORT_WINDOW = false;
    261 
    262         private final Rect mTempRect1 = new Rect();
    263         private final Rect mTempRect2 = new Rect();
    264 
    265         private final Region mTempRegion1 = new Region();
    266         private final Region mTempRegion2 = new Region();
    267         private final Region mTempRegion3 = new Region();
    268         private final Region mTempRegion4 = new Region();
    269 
    270         private final Context mContext;
    271         private final WindowManagerService mWindowManagerService;
    272         private final MagnifiedViewport mMagnifedViewport;
    273         private final Handler mHandler;
    274 
    275         private final MagnificationCallbacks mCallbacks;
    276 
    277         private final long mLongAnimationDuration;
    278 
    279         private boolean mForceShowMagnifiableBounds = false;
    280 
    281         public DisplayMagnifier(WindowManagerService windowManagerService,
    282                 MagnificationCallbacks callbacks) {
    283             mContext = windowManagerService.mContext;
    284             mWindowManagerService = windowManagerService;
    285             mCallbacks = callbacks;
    286             mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
    287             mMagnifedViewport = new MagnifiedViewport();
    288             mLongAnimationDuration = mContext.getResources().getInteger(
    289                     com.android.internal.R.integer.config_longAnimTime);
    290         }
    291 
    292         public void setMagnificationSpecLocked(MagnificationSpec spec) {
    293             mMagnifedViewport.updateMagnificationSpecLocked(spec);
    294             mMagnifedViewport.recomputeBoundsLocked();
    295             mWindowManagerService.scheduleAnimationLocked();
    296         }
    297 
    298         public void setForceShowMagnifiableBoundsLocked(boolean show) {
    299             mForceShowMagnifiableBounds = show;
    300             mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true);
    301         }
    302 
    303         public boolean isForceShowingMagnifiableBoundsLocked() {
    304             return mForceShowMagnifiableBounds;
    305         }
    306 
    307         public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
    308             if (DEBUG_RECTANGLE_REQUESTED) {
    309                 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
    310             }
    311             if (!mMagnifedViewport.isMagnifyingLocked()) {
    312                 return;
    313             }
    314             Rect magnifiedRegionBounds = mTempRect2;
    315             mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
    316             if (magnifiedRegionBounds.contains(rectangle)) {
    317                 return;
    318             }
    319             SomeArgs args = SomeArgs.obtain();
    320             args.argi1 = rectangle.left;
    321             args.argi2 = rectangle.top;
    322             args.argi3 = rectangle.right;
    323             args.argi4 = rectangle.bottom;
    324             mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
    325                     args).sendToTarget();
    326         }
    327 
    328         public void onWindowLayersChangedLocked() {
    329             if (DEBUG_LAYERS) {
    330                 Slog.i(LOG_TAG, "Layers changed.");
    331             }
    332             mMagnifedViewport.recomputeBoundsLocked();
    333             mWindowManagerService.scheduleAnimationLocked();
    334         }
    335 
    336         public void onRotationChangedLocked(DisplayContent displayContent) {
    337             if (DEBUG_ROTATION) {
    338                 final int rotation = displayContent.getRotation();
    339                 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
    340                         + " displayId: " + displayContent.getDisplayId());
    341             }
    342             mMagnifedViewport.onRotationChangedLocked();
    343             mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
    344         }
    345 
    346         public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
    347             if (DEBUG_WINDOW_TRANSITIONS) {
    348                 Slog.i(LOG_TAG, "Window transition: "
    349                         + AppTransition.appTransitionToString(transition)
    350                         + " displayId: " + windowState.getDisplayId());
    351             }
    352             final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
    353             if (magnifying) {
    354                 switch (transition) {
    355                     case AppTransition.TRANSIT_ACTIVITY_OPEN:
    356                     case AppTransition.TRANSIT_TASK_OPEN:
    357                     case AppTransition.TRANSIT_TASK_TO_FRONT:
    358                     case AppTransition.TRANSIT_WALLPAPER_OPEN:
    359                     case AppTransition.TRANSIT_WALLPAPER_CLOSE:
    360                     case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
    361                         mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
    362                     }
    363                 }
    364             }
    365         }
    366 
    367         public void onWindowTransitionLocked(WindowState windowState, int transition) {
    368             if (DEBUG_WINDOW_TRANSITIONS) {
    369                 Slog.i(LOG_TAG, "Window transition: "
    370                         + AppTransition.appTransitionToString(transition)
    371                         + " displayId: " + windowState.getDisplayId());
    372             }
    373             final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
    374             final int type = windowState.mAttrs.type;
    375             switch (transition) {
    376                 case WindowManagerPolicy.TRANSIT_ENTER:
    377                 case WindowManagerPolicy.TRANSIT_SHOW: {
    378                     if (!magnifying) {
    379                         break;
    380                     }
    381                     switch (type) {
    382                         case WindowManager.LayoutParams.TYPE_APPLICATION:
    383                         case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
    384                         case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
    385                         case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
    386                         case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
    387                         case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
    388                         case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
    389                         case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
    390                         case WindowManager.LayoutParams.TYPE_PHONE:
    391                         case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
    392                         case WindowManager.LayoutParams.TYPE_TOAST:
    393                         case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
    394                         case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
    395                         case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
    396                         case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
    397                         case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
    398                         case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
    399                         case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
    400                         case WindowManager.LayoutParams.TYPE_QS_DIALOG:
    401                         case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
    402                             Rect magnifiedRegionBounds = mTempRect2;
    403                             mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
    404                                     magnifiedRegionBounds);
    405                             Rect touchableRegionBounds = mTempRect1;
    406                             windowState.getTouchableRegion(mTempRegion1);
    407                             mTempRegion1.getBounds(touchableRegionBounds);
    408                             if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
    409                                 mCallbacks.onRectangleOnScreenRequested(
    410                                         touchableRegionBounds.left,
    411                                         touchableRegionBounds.top,
    412                                         touchableRegionBounds.right,
    413                                         touchableRegionBounds.bottom);
    414                             }
    415                         } break;
    416                     } break;
    417                 }
    418             }
    419         }
    420 
    421         public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
    422             MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
    423             if (spec != null && !spec.isNop()) {
    424                 if (!mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
    425                     return null;
    426                 }
    427             }
    428             return spec;
    429         }
    430 
    431         public void getMagnificationRegionLocked(Region outMagnificationRegion) {
    432             // Make sure we're working with the most current bounds
    433             mMagnifedViewport.recomputeBoundsLocked();
    434             mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
    435         }
    436 
    437         public void destroyLocked() {
    438             mMagnifedViewport.destroyWindow();
    439         }
    440 
    441         // Can be called outside of a surface transaction
    442         public void showMagnificationBoundsIfNeeded() {
    443             mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
    444                     .sendToTarget();
    445         }
    446 
    447         /** NOTE: This has to be called within a surface transaction. */
    448         public void drawMagnifiedRegionBorderIfNeededLocked() {
    449             mMagnifedViewport.drawWindowIfNeededLocked();
    450         }
    451 
    452         private final class MagnifiedViewport {
    453 
    454             private final SparseArray<WindowState> mTempWindowStates =
    455                     new SparseArray<WindowState>();
    456 
    457             private final RectF mTempRectF = new RectF();
    458 
    459             private final Point mTempPoint = new Point();
    460 
    461             private final Matrix mTempMatrix = new Matrix();
    462 
    463             private final Region mMagnificationRegion = new Region();
    464             private final Region mOldMagnificationRegion = new Region();
    465 
    466             private final Path mCircularPath;
    467 
    468             private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
    469 
    470             private final WindowManager mWindowManager;
    471 
    472             private final float mBorderWidth;
    473             private final int mHalfBorderWidth;
    474             private final int mDrawBorderInset;
    475 
    476             private final ViewportWindow mWindow;
    477 
    478             private boolean mFullRedrawNeeded;
    479 
    480             public MagnifiedViewport() {
    481                 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
    482                 mBorderWidth = mContext.getResources().getDimension(
    483                         com.android.internal.R.dimen.accessibility_magnification_indicator_width);
    484                 mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
    485                 mDrawBorderInset = (int) mBorderWidth / 2;
    486                 mWindow = new ViewportWindow(mContext);
    487 
    488                 if (mContext.getResources().getConfiguration().isScreenRound()) {
    489                     mCircularPath = new Path();
    490                     mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
    491                     final int centerXY = mTempPoint.x / 2;
    492                     mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
    493                 } else {
    494                     mCircularPath = null;
    495                 }
    496 
    497                 recomputeBoundsLocked();
    498             }
    499 
    500             public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) {
    501                 outMagnificationRegion.set(mMagnificationRegion);
    502             }
    503 
    504             public void updateMagnificationSpecLocked(MagnificationSpec spec) {
    505                 if (spec != null) {
    506                     mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
    507                 } else {
    508                     mMagnificationSpec.clear();
    509                 }
    510                 // If this message is pending we are in a rotation animation and do not want
    511                 // to show the border. We will do so when the pending message is handled.
    512                 if (!mHandler.hasMessages(
    513                         MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
    514                     setMagnifiedRegionBorderShownLocked(
    515                             isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true);
    516                 }
    517             }
    518 
    519             public void recomputeBoundsLocked() {
    520                 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
    521                 final int screenWidth = mTempPoint.x;
    522                 final int screenHeight = mTempPoint.y;
    523 
    524                 mMagnificationRegion.set(0, 0, 0, 0);
    525                 final Region availableBounds = mTempRegion1;
    526                 availableBounds.set(0, 0, screenWidth, screenHeight);
    527 
    528                 if (mCircularPath != null) {
    529                     availableBounds.setPath(mCircularPath, availableBounds);
    530                 }
    531 
    532                 Region nonMagnifiedBounds = mTempRegion4;
    533                 nonMagnifiedBounds.set(0, 0, 0, 0);
    534 
    535                 SparseArray<WindowState> visibleWindows = mTempWindowStates;
    536                 visibleWindows.clear();
    537                 populateWindowsOnScreenLocked(visibleWindows);
    538 
    539                 final int visibleWindowCount = visibleWindows.size();
    540                 for (int i = visibleWindowCount - 1; i >= 0; i--) {
    541                     WindowState windowState = visibleWindows.valueAt(i);
    542                     if ((windowState.mAttrs.type == TYPE_MAGNIFICATION_OVERLAY)
    543                             || ((windowState.mAttrs.privateFlags
    544                             & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) {
    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                             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 != 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