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