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