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