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