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