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