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