AccessibilityController.java revision fea963edeeb60c76465c0d644078def191f41e0f
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
17package com.android.server.wm;
18
19import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
20import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
21
22import android.animation.ObjectAnimator;
23import android.animation.ValueAnimator;
24import android.annotation.NonNull;
25import android.app.Service;
26import android.content.Context;
27import android.graphics.Canvas;
28import android.graphics.Color;
29import android.graphics.Matrix;
30import android.graphics.Paint;
31import android.graphics.Path;
32import android.graphics.PixelFormat;
33import android.graphics.Point;
34import android.graphics.PorterDuff.Mode;
35import android.graphics.Rect;
36import android.graphics.RectF;
37import android.graphics.Region;
38import android.os.Handler;
39import android.os.IBinder;
40import android.os.Looper;
41import android.os.Message;
42import android.text.TextUtils;
43import android.util.ArraySet;
44import android.util.Log;
45import android.util.Slog;
46import android.util.SparseArray;
47import android.util.TypedValue;
48import android.view.MagnificationSpec;
49import android.view.Surface;
50import android.view.Surface.OutOfResourcesException;
51import android.view.SurfaceControl;
52import android.view.ViewConfiguration;
53import android.view.WindowInfo;
54import android.view.WindowManager;
55import android.view.WindowManagerInternal.MagnificationCallbacks;
56import android.view.WindowManagerInternal.WindowsForAccessibilityCallback;
57import android.view.WindowManagerPolicy;
58import android.view.animation.DecelerateInterpolator;
59import android.view.animation.Interpolator;
60
61import com.android.internal.R;
62import com.android.internal.os.SomeArgs;
63
64import java.util.ArrayList;
65import java.util.HashSet;
66import java.util.List;
67import java.util.Set;
68
69/**
70 * This class contains the accessibility related logic of the window manger.
71 */
72final 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() &&
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