All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.wm.AccessibilityController Maven / Gradle / Ivy

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.wm;

import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CALLBACK;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK;
import static android.os.Build.IS_USER;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;

import static com.android.server.accessibility.AccessibilityTraceFileProto.ENTRY;
import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER;
import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER_H;
import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER_L;
import static com.android.server.accessibility.AccessibilityTraceProto.ACCESSIBILITY_SERVICE;
import static com.android.server.accessibility.AccessibilityTraceProto.CALENDAR_TIME;
import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PARAMS;
import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PKG;
import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_STACKS;
import static com.android.server.accessibility.AccessibilityTraceProto.CPU_STATS;
import static com.android.server.accessibility.AccessibilityTraceProto.ELAPSED_REALTIME_NANOS;
import static com.android.server.accessibility.AccessibilityTraceProto.LOGGING_TYPE;
import static com.android.server.accessibility.AccessibilityTraceProto.PROCESS_NAME;
import static com.android.server.accessibility.AccessibilityTraceProto.THREAD_ID_NAME;
import static com.android.server.accessibility.AccessibilityTraceProto.WHERE;
import static com.android.server.accessibility.AccessibilityTraceProto.WINDOW_MANAGER_SERVICE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowTracing.WINSCOPE_EXT;

import android.accessibilityservice.AccessibilityTrace;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.graphics.BLASTBufferQueue;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.SystemClock;
import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.TypedValue;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.InsetsSource;
import android.view.MagnificationSpec;
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
import android.view.ViewConfiguration;
import android.view.WindowInfo;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;

import com.android.internal.R;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.TraceBuffer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow;
import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal;
import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * This class contains the accessibility related logic of the window manager.
 */
final class AccessibilityController {
    private static final String TAG = AccessibilityController.class.getSimpleName();

    private static final Object STATIC_LOCK = new Object();
    static AccessibilityControllerInternalImpl
            getAccessibilityControllerInternal(WindowManagerService service) {
        return AccessibilityControllerInternalImpl.getInstance(service);
    }

    private final AccessibilityControllerInternalImpl mAccessibilityTracing;
    private final WindowManagerService mService;
    private static final Rect EMPTY_RECT = new Rect();
    private static final float[] sTempFloats = new float[9];

    private final SparseArray mDisplayMagnifiers = new SparseArray<>();
    private final SparseArray mWindowsForAccessibilityObserver =
            new SparseArray<>();
    private SparseArray mFocusedWindow = new SparseArray<>();
    private int mFocusedDisplay = -1;
    private final SparseBooleanArray mIsImeVisibleArray = new SparseBooleanArray();
    // Set to true if initializing window population complete.
    private boolean mAllObserversInitialized = true;
    private final AccessibilityWindowsPopulator mAccessibilityWindowsPopulator;

    AccessibilityController(WindowManagerService service) {
        mService = service;
        mAccessibilityTracing =
                AccessibilityController.getAccessibilityControllerInternal(service);

        mAccessibilityWindowsPopulator = new AccessibilityWindowsPopulator(mService, this);
    }

    boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) {
        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
            mAccessibilityTracing.logTrace(
                    TAG + ".setMagnificationCallbacks",
                    FLAGS_MAGNIFICATION_CALLBACK,
                    "displayId=" + displayId + "; callbacks={" + callbacks + "}");
        }
        boolean result = false;
        if (callbacks != null) {
            if (mDisplayMagnifiers.get(displayId) != null) {
                throw new IllegalStateException("Magnification callbacks already set!");
            }
            final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
            if (dc != null) {
                final Display display = dc.getDisplay();
                if (display != null && display.getType() != Display.TYPE_OVERLAY) {
                    final DisplayMagnifier magnifier = new DisplayMagnifier(
                            mService, dc, display, callbacks);
                    magnifier.notifyImeWindowVisibilityChanged(
                            mIsImeVisibleArray.get(displayId, false));
                    mDisplayMagnifiers.put(displayId, magnifier);
                    result = true;
                }
            }
        } else {
            final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
            if (displayMagnifier == null) {
                throw new IllegalStateException("Magnification callbacks already cleared!");
            }
            displayMagnifier.destroy();
            mDisplayMagnifiers.remove(displayId);
            result = true;
        }
        return result;
    }

    /**
     * Sets a callback for observing which windows are touchable for the purposes
     * of accessibility on specified display.
     *
     * @param displayId The logical display id.
     * @param callback The callback.
     */
    void setWindowsForAccessibilityCallback(int displayId,
            WindowsForAccessibilityCallback callback) {
        if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
            mAccessibilityTracing.logTrace(
                    TAG + ".setWindowsForAccessibilityCallback",
                    FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
                    "displayId=" + displayId + "; callback={" + callback + "}");
        }

        if (callback != null) {
            WindowsForAccessibilityObserver observer =
                    mWindowsForAccessibilityObserver.get(displayId);
            if (observer != null) {
                final String errorMessage = "Windows for accessibility callback of display "
                        + displayId + " already set!";
                Slog.e(TAG, errorMessage);
                if (Build.IS_DEBUGGABLE) {
                    throw new IllegalStateException(errorMessage);
                }
                mWindowsForAccessibilityObserver.remove(displayId);
            }
            mAccessibilityWindowsPopulator.setWindowsNotification(true);
            observer = new WindowsForAccessibilityObserver(mService, displayId, callback,
                    mAccessibilityWindowsPopulator);
            mWindowsForAccessibilityObserver.put(displayId, observer);
            mAllObserversInitialized &= observer.mInitialized;
        } else {
            final WindowsForAccessibilityObserver windowsForA11yObserver =
                    mWindowsForAccessibilityObserver.get(displayId);
            if (windowsForA11yObserver == null) {
                final String errorMessage = "Windows for accessibility callback of display "
                        + displayId + " already cleared!";
                Slog.e(TAG, errorMessage);
                if (Build.IS_DEBUGGABLE) {
                    throw new IllegalStateException(errorMessage);
                }
            }
            mWindowsForAccessibilityObserver.remove(displayId);

            if (mWindowsForAccessibilityObserver.size() <= 0) {
                mAccessibilityWindowsPopulator.setWindowsNotification(false);
            }
        }
    }

    void performComputeChangedWindowsNot(int displayId, boolean forceSend) {
        if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
            mAccessibilityTracing.logTrace(
                    TAG + ".performComputeChangedWindowsNot",
                    FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
                    "displayId=" + displayId + "; forceSend=" + forceSend);
        }
        WindowsForAccessibilityObserver observer = null;
        synchronized (mService.mGlobalLock) {
            final WindowsForAccessibilityObserver windowsForA11yObserver =
                    mWindowsForAccessibilityObserver.get(displayId);
            if (windowsForA11yObserver != null) {
                observer = windowsForA11yObserver;
            }
        }
        if (observer != null) {
            observer.performComputeChangedWindows(forceSend);
        }
    }

    void setMagnificationSpec(int displayId, MagnificationSpec spec) {
        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
                | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
            mAccessibilityTracing.logTrace(TAG + ".setMagnificationSpec",
                    FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
                    "displayId=" + displayId + "; spec={" + spec + "}");
        }
        mAccessibilityWindowsPopulator.setMagnificationSpec(displayId, spec);

        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
        if (displayMagnifier != null) {
            displayMagnifier.setMagnificationSpec(spec);
        }
        final WindowsForAccessibilityObserver windowsForA11yObserver =
                mWindowsForAccessibilityObserver.get(displayId);
        if (windowsForA11yObserver != null) {
            windowsForA11yObserver.scheduleComputeChangedWindows();
        }
    }

    void getMagnificationRegion(int displayId, Region outMagnificationRegion) {
        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
            mAccessibilityTracing.logTrace(TAG + ".getMagnificationRegion",
                    FLAGS_MAGNIFICATION_CALLBACK,
                    "displayId=" + displayId + "; outMagnificationRegion={" + outMagnificationRegion
                            + "}");
        }
        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
        if (displayMagnifier != null) {
            displayMagnifier.getMagnificationRegion(outMagnificationRegion);
        }
    }

    void onWindowLayersChanged(int displayId) {
        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
                | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
            mAccessibilityTracing.logTrace(TAG + ".onWindowLayersChanged",
                    FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
                    "displayId=" + displayId);
        }
        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
        if (displayMagnifier != null) {
            displayMagnifier.onWindowLayersChanged();
        }
        final WindowsForAccessibilityObserver windowsForA11yObserver =
                mWindowsForAccessibilityObserver.get(displayId);
        if (windowsForA11yObserver != null) {
            windowsForA11yObserver.scheduleComputeChangedWindows();
        }
    }

    void onDisplaySizeChanged(DisplayContent displayContent) {

        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
                | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
            mAccessibilityTracing.logTrace(TAG + ".onRotationChanged",
                    FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
                    "displayContent={" + displayContent + "}");
        }
        final int displayId = displayContent.getDisplayId();
        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
        if (displayMagnifier != null) {
            displayMagnifier.onDisplaySizeChanged(displayContent);
        }
    }

    void onAppWindowTransition(int displayId, int transition) {
        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
            mAccessibilityTracing.logTrace(TAG + ".onAppWindowTransition",
                    FLAGS_MAGNIFICATION_CALLBACK,
                    "displayId=" + displayId + "; transition=" + transition);
        }
        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
        if (displayMagnifier != null) {
            displayMagnifier.onAppWindowTransition(displayId, transition);
        }
        // Not relevant for the window observer.
    }

    void onWindowTransition(WindowState windowState, int transition) {
        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
                | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
            mAccessibilityTracing.logTrace(TAG + ".onWindowTransition",
                    FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
                    "windowState={" + windowState + "}; transition=" + transition);
        }
        final int displayId = windowState.getDisplayId();
        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
        if (displayMagnifier != null) {
            displayMagnifier.onWindowTransition(windowState, transition);
        }
    }

    void onWindowFocusChangedNot(int displayId) {
        // Not relevant for the display magnifier.
        if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
            mAccessibilityTracing.logTrace(TAG + ".onWindowFocusChangedNot",
                    FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "displayId=" + displayId);
        }
        WindowsForAccessibilityObserver observer = null;
        synchronized (mService.mGlobalLock) {
            final WindowsForAccessibilityObserver windowsForA11yObserver =
                    mWindowsForAccessibilityObserver.get(displayId);
            if (windowsForA11yObserver != null) {
                observer = windowsForA11yObserver;
            }
        }
        if (observer != null) {
            observer.performComputeChangedWindows(false);
        }
        // Since we abandon initializing observers if no window has focus, make sure all observers
        // are initialized.
        sendCallbackToUninitializedObserversIfNeeded();
    }

    private void sendCallbackToUninitializedObserversIfNeeded() {
        List unInitializedObservers;
        synchronized (mService.mGlobalLock) {
            if (mAllObserversInitialized) {
                return;
            }
            if (mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus == null) {
                return;
            }
            unInitializedObservers = new ArrayList<>();
            for (int i = mWindowsForAccessibilityObserver.size() - 1; i >= 0; --i) {
                final WindowsForAccessibilityObserver observer =
                        mWindowsForAccessibilityObserver.valueAt(i);
                if (!observer.mInitialized) {
                    unInitializedObservers.add(observer);
                }
            }
            // Reset the flag to record the new added observer.
            mAllObserversInitialized = true;
        }

        boolean areAllObserversInitialized = true;
        for (int i = unInitializedObservers.size() - 1; i >= 0; --i) {
            final  WindowsForAccessibilityObserver observer = unInitializedObservers.get(i);
            observer.performComputeChangedWindows(true);
            areAllObserversInitialized &= observer.mInitialized;
        }
        synchronized (mService.mGlobalLock) {
            mAllObserversInitialized &= areAllObserversInitialized;
        }
    }

    /**
     * Called when the location or the size of the window is changed. Moving the window to
     * another display is also taken into consideration.
     * @param displayIds the display ids of displays when the situation happens.
     */
    void onSomeWindowResizedOrMoved(int... displayIds) {
        onSomeWindowResizedOrMovedWithCallingUid(Binder.getCallingUid(), displayIds);
    }

    void onSomeWindowResizedOrMovedWithCallingUid(int callingUid, int... displayIds) {
        if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
            mAccessibilityTracing.logTrace(TAG + ".onSomeWindowResizedOrMoved",
                    FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
                    "displayIds={" + displayIds.toString() + "}", "".getBytes(), callingUid);
        }
        // Not relevant for the display magnifier.
        for (int i = 0; i < displayIds.length; i++) {
            final WindowsForAccessibilityObserver windowsForA11yObserver =
                    mWindowsForAccessibilityObserver.get(displayIds[i]);
            if (windowsForA11yObserver != null) {
                windowsForA11yObserver.scheduleComputeChangedWindows();
            }
        }
    }

    void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) {
        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
            mAccessibilityTracing.logTrace(
                    TAG + ".drawMagnifiedRegionBorderIfNeeded",
                    FLAGS_MAGNIFICATION_CALLBACK,
                    "displayId=" + displayId + "; transaction={" + t + "}");
        }
        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
        if (displayMagnifier != null) {
            displayMagnifier.drawMagnifiedRegionBorderIfNeeded(t);
        }
        // Not relevant for the window observer.
    }

    public Pair getWindowTransformationMatrixAndMagnificationSpec(
            IBinder token) {
        synchronized (mService.mGlobalLock) {
            final Matrix transformationMatrix = new Matrix();
            final MagnificationSpec magnificationSpec = new MagnificationSpec();

            final WindowState windowState = mService.mWindowMap.get(token);
            if (windowState != null) {
                windowState.getTransformationMatrix(new float[9], transformationMatrix);

                if (hasCallbacks()) {
                    final MagnificationSpec otherMagnificationSpec =
                            getMagnificationSpecForWindow(windowState);
                    if (otherMagnificationSpec != null && !otherMagnificationSpec.isNop()) {
                        magnificationSpec.setTo(otherMagnificationSpec);
                    }
                }
            }

            return new Pair<>(transformationMatrix, magnificationSpec);
        }
    }

    MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
            mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForWindow",
                    FLAGS_MAGNIFICATION_CALLBACK,
                    "windowState={" + windowState + "}");
        }
        final int displayId = windowState.getDisplayId();
        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
        if (displayMagnifier != null) {
            return displayMagnifier.getMagnificationSpecForWindow(windowState);
        }
        return null;
    }

    boolean hasCallbacks() {
        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
                | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
            mAccessibilityTracing.logTrace(TAG + ".hasCallbacks",
                    FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK);
        }
        return (mDisplayMagnifiers.size() > 0
                || mWindowsForAccessibilityObserver.size() > 0);
    }

    void setForceShowMagnifiableBounds(int displayId, boolean show) {
        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
            mAccessibilityTracing.logTrace(TAG + ".setForceShowMagnifiableBounds",
                    FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId + "; show=" + show);
        }
        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
        if (displayMagnifier != null) {
            displayMagnifier.setForceShowMagnifiableBounds(show);
            displayMagnifier.showMagnificationBoundsIfNeeded();
        }
    }

    void updateImeVisibilityIfNeeded(int displayId, boolean shown) {
        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
            mAccessibilityTracing.logTrace(TAG + ".updateImeVisibilityIfNeeded",
                    FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId + ";shown=" + shown);
        }

        final boolean isDisplayImeVisible = mIsImeVisibleArray.get(displayId, false);
        if (isDisplayImeVisible == shown) {
            return;
        }

        mIsImeVisibleArray.put(displayId, shown);
        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
        if (displayMagnifier != null) {
            displayMagnifier.notifyImeWindowVisibilityChanged(shown);
        }
    }

    private static void populateTransformationMatrix(WindowState windowState,
            Matrix outMatrix) {
        windowState.getTransformationMatrix(sTempFloats, outMatrix);
    }

    void dump(PrintWriter pw, String prefix) {
        for (int i = 0; i < mDisplayMagnifiers.size(); i++) {
            final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.valueAt(i);
            if (displayMagnifier != null) {
                displayMagnifier.dump(pw, prefix
                        + "Magnification display# " + mDisplayMagnifiers.keyAt(i));
            }
        }
        pw.println(prefix
                + "mWindowsForAccessibilityObserver=" + mWindowsForAccessibilityObserver);
    }

    void onFocusChanged(InputTarget lastTarget, InputTarget newTarget) {
        if (lastTarget != null) {
            mFocusedWindow.remove(lastTarget.getDisplayId());
        }
        if (newTarget != null) {
            int displayId = newTarget.getDisplayId();
            IBinder clientBinder = newTarget.getIWindow().asBinder();
            mFocusedWindow.put(displayId, clientBinder);
        }
    }

    public void onDisplayRemoved(int displayId) {
        mIsImeVisibleArray.delete(displayId);
        mFocusedWindow.remove(displayId);
    }

    public void setFocusedDisplay(int focusedDisplayId) {
        mFocusedDisplay = focusedDisplayId;
    }

    @Nullable IBinder getFocusedWindowToken() {
        return mFocusedWindow.get(mFocusedDisplay);
    }

    /**
     * This class encapsulates the functionality related to display magnification.
     */
    private static final class DisplayMagnifier {

        private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM;

        private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
        private static final boolean DEBUG_DISPLAY_SIZE = false;
        private static final boolean DEBUG_LAYERS = false;
        private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
        private static final boolean DEBUG_VIEWPORT_WINDOW = false;

        private final Rect mTempRect1 = new Rect();
        private final Rect mTempRect2 = new Rect();

        private final Region mTempRegion1 = new Region();
        private final Region mTempRegion2 = new Region();
        private final Region mTempRegion3 = new Region();
        private final Region mTempRegion4 = new Region();

        private final Context mDisplayContext;
        private final WindowManagerService mService;
        private final MagnifiedViewport mMagnifedViewport;
        private final Handler mHandler;
        private final DisplayContent mDisplayContent;
        private final Display mDisplay;
        private final AccessibilityControllerInternalImpl mAccessibilityTracing;

        private final MagnificationCallbacks mCallbacks;

        private final long mLongAnimationDuration;

        private boolean mForceShowMagnifiableBounds = false;

        DisplayMagnifier(WindowManagerService windowManagerService,
                DisplayContent displayContent,
                Display display,
                MagnificationCallbacks callbacks) {
            mDisplayContext = windowManagerService.mContext.createDisplayContext(display);
            mService = windowManagerService;
            mCallbacks = callbacks;
            mDisplayContent = displayContent;
            mDisplay = display;
            mHandler = new MyHandler(mService.mH.getLooper());
            mMagnifedViewport = new MagnifiedViewport();
            mAccessibilityTracing =
                    AccessibilityController.getAccessibilityControllerInternal(mService);
            mLongAnimationDuration = mDisplayContext.getResources().getInteger(
                    com.android.internal.R.integer.config_longAnimTime);
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".DisplayMagnifier.constructor",
                        FLAGS_MAGNIFICATION_CALLBACK,
                        "windowManagerService={" + windowManagerService + "}; displayContent={"
                                + displayContent + "}; display={" + display + "}; callbacks={"
                                + callbacks + "}");
            }
        }

        void setMagnificationSpec(MagnificationSpec spec) {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".setMagnificationSpec",
                        FLAGS_MAGNIFICATION_CALLBACK, "spec={" + spec + "}");
            }
            mMagnifedViewport.updateMagnificationSpec(spec);
            mMagnifedViewport.recomputeBounds();

            mService.applyMagnificationSpecLocked(mDisplay.getDisplayId(), spec);
            mService.scheduleAnimationLocked();
        }

        void setForceShowMagnifiableBounds(boolean show) {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".setForceShowMagnifiableBounds",
                        FLAGS_MAGNIFICATION_CALLBACK, "show=" + show);
            }
            mForceShowMagnifiableBounds = show;
            mMagnifedViewport.setMagnifiedRegionBorderShown(show, true);
        }

        boolean isForceShowingMagnifiableBounds() {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".isForceShowingMagnifiableBounds",
                        FLAGS_MAGNIFICATION_CALLBACK);
            }
            return mForceShowMagnifiableBounds;
        }

        void onWindowLayersChanged() {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                mAccessibilityTracing.logTrace(
                        LOG_TAG + ".onWindowLayersChanged", FLAGS_MAGNIFICATION_CALLBACK);
            }
            if (DEBUG_LAYERS) {
                Slog.i(LOG_TAG, "Layers changed.");
            }
            mMagnifedViewport.recomputeBounds();
            mService.scheduleAnimationLocked();
        }

        void onDisplaySizeChanged(DisplayContent displayContent) {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".onDisplaySizeChanged",
                        FLAGS_MAGNIFICATION_CALLBACK, "displayContent={" + displayContent + "}");
            }
            if (DEBUG_DISPLAY_SIZE) {
                final int rotation = displayContent.getRotation();
                Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
                        + " displayId: " + displayContent.getDisplayId());
            }
            mMagnifedViewport.onDisplaySizeChanged();
            mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED);
        }

        void onAppWindowTransition(int displayId, int transition) {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".onAppWindowTransition",
                        FLAGS_MAGNIFICATION_CALLBACK,
                        "displayId=" + displayId + "; transition=" + transition);
            }
            if (DEBUG_WINDOW_TRANSITIONS) {
                Slog.i(LOG_TAG, "Window transition: "
                        + AppTransition.appTransitionOldToString(transition)
                        + " displayId: " + displayId);
            }
            final boolean magnifying = mMagnifedViewport.isMagnifying();
            if (magnifying) {
                switch (transition) {
                    case WindowManager.TRANSIT_OLD_ACTIVITY_OPEN:
                    case WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN:
                    case WindowManager.TRANSIT_OLD_TASK_OPEN:
                    case WindowManager.TRANSIT_OLD_TASK_TO_FRONT:
                    case WindowManager.TRANSIT_OLD_WALLPAPER_OPEN:
                    case WindowManager.TRANSIT_OLD_WALLPAPER_CLOSE:
                    case WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN: {
                        mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
                    }
                }
            }
        }

        void onWindowTransition(WindowState windowState, int transition) {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".onWindowTransition",
                        FLAGS_MAGNIFICATION_CALLBACK,
                        "windowState={" + windowState + "}; transition=" + transition);
            }
            if (DEBUG_WINDOW_TRANSITIONS) {
                Slog.i(LOG_TAG, "Window transition: "
                        + AppTransition.appTransitionOldToString(transition)
                        + " displayId: " + windowState.getDisplayId());
            }
            final boolean magnifying = mMagnifedViewport.isMagnifying();
            final int type = windowState.mAttrs.type;
            switch (transition) {
                case WindowManagerPolicy.TRANSIT_ENTER:
                case WindowManagerPolicy.TRANSIT_SHOW: {
                    if (!magnifying) {
                        break;
                    }
                    switch (type) {
                        case WindowManager.LayoutParams.TYPE_APPLICATION:
                        case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
                        case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
                        case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
                        case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
                        case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
                        case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
                        case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
                        case WindowManager.LayoutParams.TYPE_PHONE:
                        case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
                        case WindowManager.LayoutParams.TYPE_TOAST:
                        case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
                        case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
                        case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
                        case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
                        case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
                        case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
                        case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
                        case WindowManager.LayoutParams.TYPE_QS_DIALOG:
                        case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
                            Rect magnifiedRegionBounds = mTempRect2;
                            mMagnifedViewport.getMagnifiedFrameInContentCoords(
                                    magnifiedRegionBounds);
                            Rect touchableRegionBounds = mTempRect1;
                            windowState.getTouchableRegion(mTempRegion1);
                            mTempRegion1.getBounds(touchableRegionBounds);
                            if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
                                mCallbacks.onRectangleOnScreenRequested(
                                        touchableRegionBounds.left,
                                        touchableRegionBounds.top,
                                        touchableRegionBounds.right,
                                        touchableRegionBounds.bottom);
                            }
                        } break;
                    } break;
                }
            }
        }

        void notifyImeWindowVisibilityChanged(boolean shown) {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".notifyImeWindowVisibilityChanged",
                        FLAGS_MAGNIFICATION_CALLBACK, "shown=" + shown);
            }
            mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED,
                    shown ? 1 : 0, 0).sendToTarget();
        }

        MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpecForWindow",
                        FLAGS_MAGNIFICATION_CALLBACK, "windowState={" + windowState + "}");
            }
            MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec();
            if (spec != null && !spec.isNop()) {
                if (!windowState.shouldMagnify()) {
                    return null;
                }
            }
            return spec;
        }

        void getMagnificationRegion(Region outMagnificationRegion) {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationRegion",
                        FLAGS_MAGNIFICATION_CALLBACK,
                        "outMagnificationRegion={" + outMagnificationRegion + "}");
            }
            // Make sure we're working with the most current bounds
            mMagnifedViewport.recomputeBounds();
            mMagnifedViewport.getMagnificationRegion(outMagnificationRegion);
        }

        void destroy() {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".destroy", FLAGS_MAGNIFICATION_CALLBACK);
            }
            mMagnifedViewport.destroyWindow();
        }

        // Can be called outside of a surface transaction
        void showMagnificationBoundsIfNeeded() {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".showMagnificationBoundsIfNeeded",
                        FLAGS_MAGNIFICATION_CALLBACK);
            }
            mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
                    .sendToTarget();
        }

        void drawMagnifiedRegionBorderIfNeeded(SurfaceControl.Transaction t) {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
                        FLAGS_MAGNIFICATION_CALLBACK, "transition={" + t + "}");
            }
            mMagnifedViewport.drawWindowIfNeeded(t);
        }

        void dump(PrintWriter pw, String prefix) {
            mMagnifedViewport.dump(pw, prefix);
        }

        private final class MagnifiedViewport {

            private final SparseArray mTempWindowStates =
                    new SparseArray();

            private final RectF mTempRectF = new RectF();

            private final Point mScreenSize = new Point();

            private final Matrix mTempMatrix = new Matrix();

            private final Region mMagnificationRegion = new Region();
            private final Region mOldMagnificationRegion = new Region();

            private final Path mCircularPath;

            private final MagnificationSpec mMagnificationSpec = new MagnificationSpec();

            private final float mBorderWidth;
            private final int mHalfBorderWidth;
            private final int mDrawBorderInset;

            private final ViewportWindow mWindow;

            private boolean mFullRedrawNeeded;
            private int mTempLayer = 0;

            MagnifiedViewport() {
                mBorderWidth = mDisplayContext.getResources().getDimension(
                        com.android.internal.R.dimen.accessibility_magnification_indicator_width);
                mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
                mDrawBorderInset = (int) mBorderWidth / 2;
                mWindow = new ViewportWindow(mDisplayContext);

                if (mDisplayContext.getResources().getConfiguration().isScreenRound()) {
                    mCircularPath = new Path();

                    getDisplaySizeLocked(mScreenSize);
                    final int centerXY = mScreenSize.x / 2;
                    mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
                } else {
                    mCircularPath = null;
                }

                recomputeBounds();
            }

            void getMagnificationRegion(@NonNull Region outMagnificationRegion) {
                outMagnificationRegion.set(mMagnificationRegion);
            }

            void updateMagnificationSpec(MagnificationSpec spec) {
                if (spec != null) {
                    mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
                } else {
                    mMagnificationSpec.clear();
                }
                // If this message is pending we are in a rotation animation and do not want
                // to show the border. We will do so when the pending message is handled.
                if (!mHandler.hasMessages(
                        MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
                    setMagnifiedRegionBorderShown(
                            isMagnifying() || isForceShowingMagnifiableBounds(), true);
                }
            }

            void recomputeBounds() {
                getDisplaySizeLocked(mScreenSize);
                final int screenWidth = mScreenSize.x;
                final int screenHeight = mScreenSize.y;

                mMagnificationRegion.set(0, 0, 0, 0);
                final Region availableBounds = mTempRegion1;
                availableBounds.set(0, 0, screenWidth, screenHeight);

                if (mCircularPath != null) {
                    availableBounds.setPath(mCircularPath, availableBounds);
                }

                Region nonMagnifiedBounds = mTempRegion4;
                nonMagnifiedBounds.set(0, 0, 0, 0);

                SparseArray visibleWindows = mTempWindowStates;
                visibleWindows.clear();
                populateWindowsOnScreen(visibleWindows);

                final int visibleWindowCount = visibleWindows.size();
                for (int i = visibleWindowCount - 1; i >= 0; i--) {
                    WindowState windowState = visibleWindows.valueAt(i);
                    final int windowType = windowState.mAttrs.type;
                    if (isExcludedWindowType(windowType)
                            || ((windowState.mAttrs.privateFlags
                            & PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION) != 0)
                            || ((windowState.mAttrs.privateFlags
                            & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) {
                        continue;
                    }

                    // Consider the touchable portion of the window
                    Matrix matrix = mTempMatrix;
                    populateTransformationMatrix(windowState, matrix);
                    Region touchableRegion = mTempRegion3;
                    windowState.getTouchableRegion(touchableRegion);
                    Rect touchableFrame = mTempRect1;
                    touchableRegion.getBounds(touchableFrame);
                    RectF windowFrame = mTempRectF;
                    windowFrame.set(touchableFrame);
                    windowFrame.offset(-windowState.getFrame().left,
                            -windowState.getFrame().top);
                    matrix.mapRect(windowFrame);
                    Region windowBounds = mTempRegion2;
                    windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
                            (int) windowFrame.right, (int) windowFrame.bottom);
                    // Only update new regions
                    Region portionOfWindowAlreadyAccountedFor = mTempRegion3;
                    portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion);
                    portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
                    windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);

                    if (windowState.shouldMagnify()) {
                        mMagnificationRegion.op(windowBounds, Region.Op.UNION);
                        mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
                    } else {
                        nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
                        availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
                    }

                    // If the navigation bar window doesn't have touchable region, count
                    // navigation bar insets into nonMagnifiedBounds. It happens when
                    // navigation mode is gestural.
                    if (isUntouchableNavigationBar(windowState, mTempRegion3)) {
                        final Rect navBarInsets = getNavBarInsets(mDisplayContent);
                        nonMagnifiedBounds.op(navBarInsets, Region.Op.UNION);
                        availableBounds.op(navBarInsets, Region.Op.DIFFERENCE);
                    }

                    // Count letterbox into nonMagnifiedBounds
                    if (windowState.areAppWindowBoundsLetterboxed()) {
                        Region letterboxBounds = getLetterboxBounds(windowState);
                        nonMagnifiedBounds.op(letterboxBounds, Region.Op.UNION);
                        availableBounds.op(letterboxBounds, Region.Op.DIFFERENCE);
                    }

                    // Update accounted bounds
                    Region accountedBounds = mTempRegion2;
                    accountedBounds.set(mMagnificationRegion);
                    accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
                    accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);

                    if (accountedBounds.isRect()) {
                        Rect accountedFrame = mTempRect1;
                        accountedBounds.getBounds(accountedFrame);
                        if (accountedFrame.width() == screenWidth
                                && accountedFrame.height() == screenHeight) {
                            break;
                        }
                    }
                }
                visibleWindows.clear();

                mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
                        screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
                        Region.Op.INTERSECT);

                final boolean magnifiedChanged =
                        !mOldMagnificationRegion.equals(mMagnificationRegion);
                if (magnifiedChanged) {
                    mWindow.setBounds(mMagnificationRegion);
                    final Rect dirtyRect = mTempRect1;
                    if (mFullRedrawNeeded) {
                        mFullRedrawNeeded = false;
                        dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
                                screenWidth - mDrawBorderInset,
                                screenHeight - mDrawBorderInset);
                        mWindow.invalidate(dirtyRect);
                    } else {
                        final Region dirtyRegion = mTempRegion3;
                        dirtyRegion.set(mMagnificationRegion);
                        dirtyRegion.op(mOldMagnificationRegion, Region.Op.XOR);
                        dirtyRegion.getBounds(dirtyRect);
                        mWindow.invalidate(dirtyRect);
                    }

                    mOldMagnificationRegion.set(mMagnificationRegion);
                    final SomeArgs args = SomeArgs.obtain();
                    args.arg1 = Region.obtain(mMagnificationRegion);
                    mHandler.obtainMessage(
                            MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args)
                            .sendToTarget();
                }
            }

            private boolean isExcludedWindowType(int windowType) {
                return windowType == TYPE_MAGNIFICATION_OVERLAY
                        // Omit the touch region of window magnification to avoid the cut out of the
                        // magnification and the magnified center of window magnification could be
                        // in the bounds
                        || windowType == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
            }

            void onDisplaySizeChanged() {
                // If we are showing the magnification border, hide it immediately so
                // the user does not see strange artifacts during display size changed caused by
                // rotation or folding/unfolding the device. In the rotation case, the screenshot
                // used for rotation already has the border. After the rotation is complete
                // we will show the border.
                if (isMagnifying() || isForceShowingMagnifiableBounds()) {
                    setMagnifiedRegionBorderShown(false, false);
                    final long delay = (long) (mLongAnimationDuration
                            * mService.getWindowAnimationScaleLocked());
                    Message message = mHandler.obtainMessage(
                            MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
                    mHandler.sendMessageDelayed(message, delay);
                }
                recomputeBounds();
                mWindow.updateSize();
            }

            void setMagnifiedRegionBorderShown(boolean shown, boolean animate) {
                if (shown) {
                    mFullRedrawNeeded = true;
                    mOldMagnificationRegion.set(0, 0, 0, 0);
                }
                mWindow.setShown(shown, animate);
            }

            void getMagnifiedFrameInContentCoords(Rect rect) {
                MagnificationSpec spec = mMagnificationSpec;
                mMagnificationRegion.getBounds(rect);
                rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
                rect.scale(1.0f / spec.scale);
            }

            boolean isMagnifying() {
                return mMagnificationSpec.scale > 1.0f;
            }

            MagnificationSpec getMagnificationSpec() {
                return mMagnificationSpec;
            }

            void drawWindowIfNeeded(SurfaceControl.Transaction t) {
                recomputeBounds();
                mWindow.drawIfNeeded(t);
            }

            void destroyWindow() {
                mWindow.releaseSurface();
            }

            private void populateWindowsOnScreen(SparseArray outWindows) {
                mTempLayer = 0;
                mDisplayContent.forAllWindows((w) -> {
                    if (w.isOnScreen() && w.isVisible()
                            && (w.mAttrs.alpha != 0)) {
                        mTempLayer++;
                        outWindows.put(mTempLayer, w);
                    }
                }, false /* traverseTopToBottom */ );
            }

            private void getDisplaySizeLocked(Point outSize) {
                final Rect bounds =
                        mDisplayContent.getConfiguration().windowConfiguration.getBounds();
                outSize.set(bounds.width(), bounds.height());
            }

            void dump(PrintWriter pw, String prefix) {
                mWindow.dump(pw, prefix);
            }

            private final class ViewportWindow {
                private static final String SURFACE_TITLE = "Magnification Overlay";

                private final Region mBounds = new Region();
                private final Rect mDirtyRect = new Rect();
                private final Paint mPaint = new Paint();

                private final SurfaceControl mSurfaceControl;
                private final BLASTBufferQueue mBlastBufferQueue;
                private final Surface mSurface;

                private final AnimationController mAnimationController;

                private boolean mShown;
                private int mAlpha;

                private boolean mInvalidated;

                ViewportWindow(Context context) {
                    SurfaceControl surfaceControl = null;
                    try {
                        surfaceControl = mDisplayContent
                                .makeOverlay()
                                .setName(SURFACE_TITLE)
                                .setBLASTLayer()
                                .setFormat(PixelFormat.TRANSLUCENT)
                                .setCallsite("ViewportWindow")
                                .build();
                    } catch (OutOfResourcesException oore) {
                        /* ignore */
                    }
                    mSurfaceControl = surfaceControl;
                    mDisplay.getRealSize(mScreenSize);
                    mBlastBufferQueue = new BLASTBufferQueue(SURFACE_TITLE, mSurfaceControl,
                            mScreenSize.x, mScreenSize.y, PixelFormat.RGBA_8888);

                    final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
                    final int layer =
                            mService.mPolicy.getWindowLayerFromTypeLw(TYPE_MAGNIFICATION_OVERLAY) *
                                    WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
                    t.setLayer(mSurfaceControl, layer).setPosition(mSurfaceControl, 0, 0);
                    InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t,
                            mDisplayContent.getDisplayId(), "Magnification Overlay");
                    t.apply();
                    mSurface = mBlastBufferQueue.createSurface();

                    mAnimationController = new AnimationController(context,
                            mService.mH.getLooper());

                    TypedValue typedValue = new TypedValue();
                    context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
                            typedValue, true);
                    final int borderColor = context.getColor(typedValue.resourceId);

                    mPaint.setStyle(Paint.Style.STROKE);
                    mPaint.setStrokeWidth(mBorderWidth);
                    mPaint.setColor(borderColor);

                    mInvalidated = true;
                }

                void setShown(boolean shown, boolean animate) {
                    synchronized (mService.mGlobalLock) {
                        if (mShown == shown) {
                            return;
                        }
                        mShown = shown;
                        mAnimationController.onFrameShownStateChanged(shown, animate);
                        if (DEBUG_VIEWPORT_WINDOW) {
                            Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
                        }
                    }
                }

                @SuppressWarnings("unused")
                // Called reflectively from an animator.
                int getAlpha() {
                    synchronized (mService.mGlobalLock) {
                        return mAlpha;
                    }
                }

                void setAlpha(int alpha) {
                    synchronized (mService.mGlobalLock) {
                        if (mAlpha == alpha) {
                            return;
                        }
                        mAlpha = alpha;
                        invalidate(null);
                        if (DEBUG_VIEWPORT_WINDOW) {
                            Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
                        }
                    }
                }

                void setBounds(Region bounds) {
                    synchronized (mService.mGlobalLock) {
                        if (mBounds.equals(bounds)) {
                            return;
                        }
                        mBounds.set(bounds);
                        invalidate(mDirtyRect);
                        if (DEBUG_VIEWPORT_WINDOW) {
                            Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
                        }
                    }
                }

                void updateSize() {
                    synchronized (mService.mGlobalLock) {
                        getDisplaySizeLocked(mScreenSize);
                        mBlastBufferQueue.update(mSurfaceControl, mScreenSize.x, mScreenSize.y,
                                PixelFormat.RGBA_8888);
                        invalidate(mDirtyRect);
                    }
                }

                void invalidate(Rect dirtyRect) {
                    if (dirtyRect != null) {
                        mDirtyRect.set(dirtyRect);
                    } else {
                        mDirtyRect.setEmpty();
                    }
                    mInvalidated = true;
                    mService.scheduleAnimationLocked();
                }

                void drawIfNeeded(SurfaceControl.Transaction t) {
                    synchronized (mService.mGlobalLock) {
                        if (!mInvalidated) {
                            return;
                        }
                        mInvalidated = false;
                        if (mAlpha > 0) {
                            Canvas canvas = null;
                            try {
                                // Empty dirty rectangle means unspecified.
                                if (mDirtyRect.isEmpty()) {
                                    mBounds.getBounds(mDirtyRect);
                                }
                                mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth);
                                canvas = mSurface.lockCanvas(mDirtyRect);
                                if (DEBUG_VIEWPORT_WINDOW) {
                                    Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
                                }
                            } catch (IllegalArgumentException iae) {
                                /* ignore */
                            } catch (Surface.OutOfResourcesException oore) {
                                /* ignore */
                            }
                            if (canvas == null) {
                                return;
                            }
                            if (DEBUG_VIEWPORT_WINDOW) {
                                Slog.i(LOG_TAG, "Bounds: " + mBounds);
                            }
                            canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
                            mPaint.setAlpha(mAlpha);
                            Path path = mBounds.getBoundaryPath();
                            canvas.drawPath(path, mPaint);

                            mSurface.unlockCanvasAndPost(canvas);
                            t.show(mSurfaceControl);
                        } else {
                            t.hide(mSurfaceControl);
                        }
                    }
                }

                void releaseSurface() {
                    if (mBlastBufferQueue != null) {
                        mBlastBufferQueue.destroy();
                    }
                    mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
                    mSurface.release();
                }

                void dump(PrintWriter pw, String prefix) {
                    pw.println(prefix
                            + " mBounds= " + mBounds
                            + " mDirtyRect= " + mDirtyRect
                            + " mWidth= " + mScreenSize.x
                            + " mHeight= " + mScreenSize.y);
                }

                private final class AnimationController extends Handler {
                    private static final String PROPERTY_NAME_ALPHA = "alpha";

                    private static final int MIN_ALPHA = 0;
                    private static final int MAX_ALPHA = 255;

                    private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1;

                    private final ValueAnimator mShowHideFrameAnimator;

                    AnimationController(Context context, Looper looper) {
                        super(looper);
                        mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
                                PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);

                        Interpolator interpolator = new DecelerateInterpolator(2.5f);
                        final long longAnimationDuration = context.getResources().getInteger(
                                com.android.internal.R.integer.config_longAnimTime);

                        mShowHideFrameAnimator.setInterpolator(interpolator);
                        mShowHideFrameAnimator.setDuration(longAnimationDuration);
                    }

                    void onFrameShownStateChanged(boolean shown, boolean animate) {
                        obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
                                shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
                    }

                    @Override
                    public void handleMessage(Message message) {
                        switch (message.what) {
                            case MSG_FRAME_SHOWN_STATE_CHANGED: {
                                final boolean shown = message.arg1 == 1;
                                final boolean animate = message.arg2 == 1;

                                if (animate) {
                                    if (mShowHideFrameAnimator.isRunning()) {
                                        mShowHideFrameAnimator.reverse();
                                    } else {
                                        if (shown) {
                                            mShowHideFrameAnimator.start();
                                        } else {
                                            mShowHideFrameAnimator.reverse();
                                        }
                                    }
                                } else {
                                    mShowHideFrameAnimator.cancel();
                                    if (shown) {
                                        setAlpha(MAX_ALPHA);
                                    } else {
                                        setAlpha(MIN_ALPHA);
                                    }
                                }
                            } break;
                        }
                    }
                }
            }
        }

        private class MyHandler extends Handler {
            public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
            public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
            public static final int MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED = 4;
            public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
            public static final int MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED = 6;

            MyHandler(Looper looper) {
                super(looper);
            }

            @Override
            public void handleMessage(Message message) {
                switch (message.what) {
                    case MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED: {
                        final SomeArgs args = (SomeArgs) message.obj;
                        final Region magnifiedBounds = (Region) args.arg1;
                        mCallbacks.onMagnificationRegionChanged(magnifiedBounds);
                        magnifiedBounds.recycle();
                    } break;

                    case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
                        mCallbacks.onUserContextChanged();
                    } break;

                    case MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED: {
                        mCallbacks.onDisplaySizeChanged();
                    } break;

                    case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
                        synchronized (mService.mGlobalLock) {
                            if (mMagnifedViewport.isMagnifying()
                                    || isForceShowingMagnifiableBounds()) {
                                mMagnifedViewport.setMagnifiedRegionBorderShown(true, true);
                                mService.scheduleAnimationLocked();
                            }
                        }
                    } break;

                    case MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED: {
                        final boolean shown = message.arg1 == 1;
                        mCallbacks.onImeWindowVisibilityChanged(shown);
                    } break;
                }
            }
        }
    }

    static boolean isUntouchableNavigationBar(WindowState windowState,
            Region touchableRegion) {
        if (windowState.mAttrs.type != WindowManager.LayoutParams.TYPE_NAVIGATION_BAR) {
            return false;
        }

        // Gets the touchable region.
        windowState.getTouchableRegion(touchableRegion);

        return touchableRegion.isEmpty();
    }

    static Rect getNavBarInsets(DisplayContent displayContent) {
        final InsetsSource source = displayContent.getInsetsStateController().getRawInsetsState()
                .peekSource(ITYPE_NAVIGATION_BAR);
        return source != null ? source.getFrame() : EMPTY_RECT;
    }

    static Region getLetterboxBounds(WindowState windowState) {
        final ActivityRecord appToken = windowState.mActivityRecord;
        if (appToken == null) {
            return new Region();
        }
        final Rect letterboxInsets = appToken.getLetterboxInsets();
        final Rect nonLetterboxRect = windowState.getBounds();
        nonLetterboxRect.inset(letterboxInsets);
        final Region letterboxBounds = new Region();
        letterboxBounds.set(windowState.getBounds());
        letterboxBounds.op(nonLetterboxRect, Region.Op.DIFFERENCE);
        return letterboxBounds;
    }

    /**
     * This class encapsulates the functionality related to computing the windows
     * reported for accessibility purposes. These windows are all windows a sighted
     * user can see on the screen.
     */
    private static final class WindowsForAccessibilityObserver {
        private static final String LOG_TAG = TAG_WITH_CLASS_NAME ?
                "WindowsForAccessibilityObserver" : TAG_WM;

        private static final boolean DEBUG = false;

        private final List mTempA11yWindows = new ArrayList<>();

        private final Set mTempBinderSet = new ArraySet<>();

        private final Point mTempPoint = new Point();

        private final Region mTempRegion = new Region();

        private final Region mTempRegion1 = new Region();

        private final Region mTempRegion2 = new Region();

        private final WindowManagerService mService;

        private final Handler mHandler;

        private final AccessibilityControllerInternalImpl mAccessibilityTracing;

        private final WindowsForAccessibilityCallback mCallback;

        private final int mDisplayId;

        private final long mRecurringAccessibilityEventsIntervalMillis;

        // Set to true if initializing window population complete.
        private boolean mInitialized;
        private final AccessibilityWindowsPopulator mA11yWindowsPopulator;

        WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
                int displayId, WindowsForAccessibilityCallback callback,
                AccessibilityWindowsPopulator accessibilityWindowsPopulator) {
            mService = windowManagerService;
            mCallback = callback;
            mDisplayId = displayId;
            mHandler = new MyHandler(mService.mH.getLooper());
            mAccessibilityTracing =
                    AccessibilityController.getAccessibilityControllerInternal(mService);
            mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
                    .getSendRecurringAccessibilityEventsInterval();
            mA11yWindowsPopulator = accessibilityWindowsPopulator;
            computeChangedWindows(true);
        }

        void performComputeChangedWindows(boolean forceSend) {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".performComputeChangedWindows",
                        FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "forceSend=" + forceSend);
            }
            mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
            computeChangedWindows(forceSend);
        }

        void scheduleComputeChangedWindows() {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".scheduleComputeChangedWindows",
                        FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK);
            }
            if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
                mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
                        mRecurringAccessibilityEventsIntervalMillis);
            }
        }

        /**
         * Check if windows have changed, and send them to the accessibility subsystem if they have.
         *
         * @param forceSend Send the windows the accessibility even if they haven't changed.
         */
        void computeChangedWindows(boolean forceSend) {
            if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
                mAccessibilityTracing.logTrace(LOG_TAG + ".computeChangedWindows",
                        FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "forceSend=" + forceSend);
            }
            if (DEBUG) {
                Slog.i(LOG_TAG, "computeChangedWindows()");
            }

            List windows = new ArrayList<>();
            final int topFocusedDisplayId;
            IBinder topFocusedWindowToken = null;

            synchronized (mService.mGlobalLock) {
                // If there is a recents animation running, then use the animation target as the
                // top window state. Otherwise,do not send the windows if there is no top focus as
                // the window manager is still looking for where to put it. We will do the work when
                // we get a focus change callback.
                final RecentsAnimationController controller =
                        mService.getRecentsAnimationController();
                final WindowState topFocusedWindowState = controller != null
                        ? controller.getTargetAppMainWindow()
                        : getTopFocusWindow();
                if (topFocusedWindowState == null) {
                    if (DEBUG) {
                        Slog.d(LOG_TAG, "top focused window is null, compute it again later");
                    }
                    return;
                }

                final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
                if (dc == null) {
                    //It should not happen because it is created while adding the callback.
                    Slog.w(LOG_TAG, "display content is null, should be created later");
                    return;
                }
                final Display display = dc.getDisplay();
                display.getRealSize(mTempPoint);
                final int screenWidth = mTempPoint.x;
                final int screenHeight = mTempPoint.y;

                Region unaccountedSpace = mTempRegion;
                unaccountedSpace.set(0, 0, screenWidth, screenHeight);

                final List visibleWindows = mTempA11yWindows;
                mA11yWindowsPopulator.populateVisibleWindowsOnScreenLocked(
                        mDisplayId, visibleWindows);
                Set addedWindows = mTempBinderSet;
                addedWindows.clear();

                boolean focusedWindowAdded = false;

                final int visibleWindowCount = visibleWindows.size();

                // Iterate until we figure out what is touchable for the entire screen.
                for (int i = 0; i < visibleWindowCount; i++) {
                    final AccessibilityWindow a11yWindow = visibleWindows.get(i);
                    final Region regionInWindow = new Region();
                    a11yWindow.getTouchableRegionInWindow(regionInWindow);
                    if (windowMattersToAccessibility(a11yWindow, regionInWindow,
                            unaccountedSpace)) {
                        addPopulatedWindowInfo(a11yWindow, regionInWindow, windows, addedWindows);
                        if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) {
                            updateUnaccountedSpace(a11yWindow, unaccountedSpace);
                        }
                        focusedWindowAdded |= a11yWindow.isFocused();
                    } else if (a11yWindow.isUntouchableNavigationBar()) {
                        // If this widow is navigation bar without touchable region, accounting the
                        // region of navigation bar inset because all touch events from this region
                        // would be received by launcher, i.e. this region is a un-touchable one
                        // for the application.
                        unaccountedSpace.op(getNavBarInsets(dc), unaccountedSpace,
                                Region.Op.REVERSE_DIFFERENCE);
                    }

                    if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
                        break;
                    }
                }

                // Remove child/parent references to windows that were not added.
                final int windowCount = windows.size();
                for (int i = 0; i < windowCount; i++) {
                    WindowInfo window = windows.get(i);
                    if (!addedWindows.contains(window.parentToken)) {
                        window.parentToken = null;
                    }
                    if (window.childTokens != null) {
                        final int childTokenCount = window.childTokens.size();
                        for (int j = childTokenCount - 1; j >= 0; j--) {
                            if (!addedWindows.contains(window.childTokens.get(j))) {
                                window.childTokens.remove(j);
                            }
                        }
                        // Leave the child token list if empty.
                    }
                }

                visibleWindows.clear();
                addedWindows.clear();

                // Gets the top focused display Id and window token for supporting multi-display.
                topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId();
                topFocusedWindowToken = topFocusedWindowState.mClient.asBinder();
            }
            mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId,
                    topFocusedWindowToken, windows);

            // Recycle the windows as we do not need them.
            clearAndRecycleWindows(windows);
            mInitialized = true;
        }

        // Some windows should be excluded from unaccounted space computation, though they still
        // should be reported
        private boolean windowMattersToUnaccountedSpaceComputation(AccessibilityWindow a11yWindow) {
            // Do not account space of trusted non-touchable windows, except the split-screen
            // divider.
            // If it's not trusted, touch events are not sent to the windows behind it.
            if (!a11yWindow.isTouchable()
                    && (a11yWindow.getType() != TYPE_DOCK_DIVIDER)
                    && a11yWindow.isTrustedOverlay()) {
                return false;
            }

            if (a11yWindow.getType() == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
                return false;
            }
            return true;
        }

        private boolean windowMattersToAccessibility(AccessibilityWindow a11yWindow,
                Region regionInScreen, Region unaccountedSpace) {
            if (a11yWindow.ignoreRecentsAnimationForAccessibility()) {
                return false;
            }

            if (a11yWindow.isFocused()) {
                return true;
            }

            // Ignore non-touchable windows, except the split-screen divider, which is
            // occasionally non-touchable but still useful for identifying split-screen
            // mode and the PIP menu.
            if (!a11yWindow.isTouchable()
                    && (a11yWindow.getType() != TYPE_DOCK_DIVIDER
                    && !a11yWindow.isPIPMenu())) {
                return false;
            }

            // If the window is completely covered by other windows - ignore.
            if (unaccountedSpace.quickReject(regionInScreen)) {
                return false;
            }

            // Add windows of certain types not covered by modal windows.
            if (isReportedWindowType(a11yWindow.getType())) {
                return true;
            }

            return false;
        }

        private void updateUnaccountedSpace(AccessibilityWindow a11yWindow,
                Region unaccountedSpace) {
            if (a11yWindow.getType()
                    != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
                // Account for the space this window takes if the window
                // is not an accessibility overlay which does not change
                // the reported windows.
                final Region touchableRegion = mTempRegion2;
                a11yWindow.getTouchableRegionInScreen(touchableRegion);
                unaccountedSpace.op(touchableRegion, unaccountedSpace,
                        Region.Op.REVERSE_DIFFERENCE);
                // Account for the space of letterbox.
                final Region letterboxBounds = mTempRegion1;
                if (a11yWindow.setLetterBoxBoundsIfNeeded(letterboxBounds)) {
                    unaccountedSpace.op(letterboxBounds,
                            unaccountedSpace, Region.Op.REVERSE_DIFFERENCE);
                }
            }
        }

        private static void addPopulatedWindowInfo(AccessibilityWindow a11yWindow,
                Region regionInScreen, List out, Set tokenOut) {
            final WindowInfo window = a11yWindow.getWindowInfo();
            window.regionInScreen.set(regionInScreen);
            window.layer = tokenOut.size();
            out.add(window);
            tokenOut.add(window.token);
        }

        private static void clearAndRecycleWindows(List windows) {
            final int windowCount = windows.size();
            for (int i = windowCount - 1; i >= 0; i--) {
                windows.remove(i).recycle();
            }
        }

        private static boolean isReportedWindowType(int windowType) {
            return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
                    && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
                    && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
                    && windowType != WindowManager.LayoutParams.TYPE_DRAG
                    && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
                    && windowType != WindowManager.LayoutParams.TYPE_POINTER
                    && windowType != TYPE_MAGNIFICATION_OVERLAY
                    && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
                    && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
                    && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
        }

        private WindowState getTopFocusWindow() {
            return mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus;
        }

        @Override
        public String toString() {
            return "WindowsForAccessibilityObserver{"
                    + "mDisplayId=" + mDisplayId
                    + ", mInitialized=" + mInitialized
                    + '}';
        }

        private class MyHandler extends Handler {
            public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1;

            public MyHandler(Looper looper) {
                super(looper, null, false);
            }

            @Override
            @SuppressWarnings("unchecked")
            public void handleMessage(Message message) {
                switch (message.what) {
                    case MESSAGE_COMPUTE_CHANGED_WINDOWS: {
                        computeChangedWindows(false);
                    } break;
                }
            }
        }
    }

    static final class AccessibilityControllerInternalImpl
            implements AccessibilityControllerInternal {

        private static AccessibilityControllerInternalImpl sInstance;
        static AccessibilityControllerInternalImpl getInstance(WindowManagerService service) {
            synchronized (STATIC_LOCK) {
                if (sInstance == null) {
                    sInstance = new AccessibilityControllerInternalImpl(service);
                }
                return sInstance;
            }
        }

        private final AccessibilityTracing mTracing;
        private volatile long mEnabledTracingFlags;
        private UiChangesForAccessibilityCallbacksDispatcher mCallbacksDispatcher;
        private final Looper mLooper;

        private AccessibilityControllerInternalImpl(WindowManagerService service) {
            mLooper = service.mH.getLooper();
            mTracing = AccessibilityTracing.getInstance(service);
            mEnabledTracingFlags = 0L;
        }

        @Override
        public void startTrace(long loggingTypes) {
            mEnabledTracingFlags = loggingTypes;
            mTracing.startTrace();
        }

        @Override
        public void stopTrace() {
            mTracing.stopTrace();
            mEnabledTracingFlags = 0L;
        }

        @Override
        public boolean isAccessibilityTracingEnabled() {
            return mTracing.isEnabled();
        }

        boolean isTracingEnabled(long flags) {
            return (flags & mEnabledTracingFlags) != 0L;
        }

        void logTrace(String where, long loggingTypes) {
            logTrace(where, loggingTypes, "");
        }

        void logTrace(String where, long loggingTypes, String callingParams) {
            logTrace(where, loggingTypes, callingParams, "".getBytes(), Binder.getCallingUid());
        }

        void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump,
                int callingUid) {
            mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid,
                    new HashSet(Arrays.asList("logTrace")));
        }

        @Override
        public void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump,
                int callingUid, StackTraceElement[] stackTrace, Set ignoreStackEntries) {
            mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid, stackTrace,
                    ignoreStackEntries);
        }

        @Override
        public void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump,
                int callingUid, StackTraceElement[] callStack, long timeStamp, int processId,
                long threadId, Set ignoreStackEntries) {
            mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid, callStack,
                    timeStamp, processId, threadId, ignoreStackEntries);
        }

        @Override
        public void setUiChangesForAccessibilityCallbacks(
                UiChangesForAccessibilityCallbacks callbacks) {
            if (isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                logTrace(
                        TAG + ".setAccessibilityWindowManagerCallbacks",
                        FLAGS_MAGNIFICATION_CALLBACK,
                        "callbacks={" + callbacks + "}");
            }
            if (callbacks != null) {
                if (mCallbacksDispatcher != null) {
                    throw new IllegalStateException("Accessibility window manager callback already "
                            + "set!");
                }
                mCallbacksDispatcher =
                        new UiChangesForAccessibilityCallbacksDispatcher(this, mLooper,
                                callbacks);
            } else {
                if (mCallbacksDispatcher == null) {
                    throw new IllegalStateException("Accessibility window manager callback already "
                            + "cleared!");
                }
                mCallbacksDispatcher = null;
            }
        }

        public boolean hasWindowManagerEventDispatcher() {
            if (isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
                    | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
                logTrace(TAG + ".hasCallbacks",
                        FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK);
            }
            return mCallbacksDispatcher != null;
        }

        public void onRectangleOnScreenRequested(int displayId, Rect rectangle) {
            if (isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                logTrace(
                        TAG + ".onRectangleOnScreenRequested",
                        FLAGS_MAGNIFICATION_CALLBACK,
                        "rectangle={" + rectangle + "}");
            }
            if (mCallbacksDispatcher != null) {
                mCallbacksDispatcher.onRectangleOnScreenRequested(displayId, rectangle);
            }
        }

        private static final class UiChangesForAccessibilityCallbacksDispatcher {

            private static final String LOG_TAG = TAG_WITH_CLASS_NAME
                    ? "WindowManagerEventDispatcher" : TAG_WM;

            private static final boolean DEBUG_RECTANGLE_REQUESTED = false;

            private final AccessibilityControllerInternalImpl mAccessibilityTracing;

            @NonNull
            private final UiChangesForAccessibilityCallbacks mCallbacks;

            private final Handler mHandler;

            UiChangesForAccessibilityCallbacksDispatcher(
                    AccessibilityControllerInternalImpl accessibilityControllerInternal,
                    Looper looper, @NonNull UiChangesForAccessibilityCallbacks callbacks) {
                mAccessibilityTracing = accessibilityControllerInternal;
                mCallbacks = callbacks;
                mHandler = new Handler(looper);
            }

            void onRectangleOnScreenRequested(int displayId, Rect rectangle) {
                if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                    mAccessibilityTracing.logTrace(LOG_TAG + ".onRectangleOnScreenRequested",
                            FLAGS_MAGNIFICATION_CALLBACK, "rectangle={" + rectangle + "}");
                }
                if (DEBUG_RECTANGLE_REQUESTED) {
                    Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
                }
                final Message m = PooledLambda.obtainMessage(
                        mCallbacks::onRectangleOnScreenRequested, displayId, rectangle.left,
                        rectangle.top, rectangle.right, rectangle.bottom);
                mHandler.sendMessage(m);
            }
        }
    }

    private static final class AccessibilityTracing {
        private static AccessibilityTracing sInstance;
        static AccessibilityTracing getInstance(WindowManagerService service) {
            synchronized (STATIC_LOCK) {
                if (sInstance == null) {
                    sInstance = new AccessibilityTracing(service);
                }
                return sInstance;
            }
        }

        private static final int CPU_STATS_COUNT = 5;
        private static final int BUFFER_CAPACITY = 1024 * 1024 * 12;
        private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace"
                + WINSCOPE_EXT;
        private static final String TAG = "AccessibilityTracing";
        private static final long MAGIC_NUMBER_VALUE =
                ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;

        private final Object mLock = new Object();
        private final WindowManagerService mService;
        private final File mTraceFile;
        private final TraceBuffer mBuffer;
        private final LogHandler mHandler;
        private volatile boolean mEnabled;

        AccessibilityTracing(WindowManagerService service) {
            mService = service;
            mTraceFile = new File(TRACE_FILENAME);
            mBuffer = new TraceBuffer(BUFFER_CAPACITY);
            HandlerThread workThread = new HandlerThread(TAG);
            workThread.start();
            mHandler = new LogHandler(workThread.getLooper());
        }

        /**
         * Start the trace.
         */
        void startTrace() {
            if (IS_USER) {
                Slog.e(TAG, "Error: Tracing is not supported on user builds.");
                return;
            }
            synchronized (mLock) {
                mEnabled = true;
                mBuffer.resetBuffer();
            }
        }

        /**
         * Stops the trace and write the current buffer to disk
         */
        void stopTrace() {
            if (IS_USER) {
                Slog.e(TAG, "Error: Tracing is not supported on user builds.");
                return;
            }
            synchronized (mLock) {
                mEnabled = false;
                if (mEnabled) {
                    Slog.e(TAG, "Error: tracing enabled while waiting for flush.");
                    return;
                }
                writeTraceToFile();
            }
        }

        boolean isEnabled() {
            return mEnabled;
        }

        /**
         * Write an accessibility trace log entry.
         */
        void logState(String where, long loggingTypes) {
            if (!mEnabled) {
                return;
            }
            logState(where, loggingTypes, "");
        }

        /**
         * Write an accessibility trace log entry.
         */
        void logState(String where, long loggingTypes, String callingParams) {
            if (!mEnabled) {
                return;
            }
            logState(where, loggingTypes, callingParams, "".getBytes());
        }

        /**
         * Write an accessibility trace log entry.
         */
        void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump) {
            if (!mEnabled) {
                return;
            }
            logState(where, loggingTypes, callingParams, a11yDump, Binder.getCallingUid(),
                    new HashSet(Arrays.asList("logState")));
        }

        /**
         * Write an accessibility trace log entry.
         */
        void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump,
                int callingUid, Set ignoreStackEntries) {
            if (!mEnabled) {
                return;
            }
            StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
            ignoreStackEntries.add("logState");
            logState(where, loggingTypes, callingParams, a11yDump, callingUid, stackTraceElements,
                    ignoreStackEntries);
        }

        /**
         * Write an accessibility trace log entry.
         */
        void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump,
                int callingUid, StackTraceElement[] stackTrace, Set ignoreStackEntries) {
            if (!mEnabled) {
                return;
            }
            log(where, loggingTypes, callingParams, a11yDump, callingUid, stackTrace,
                    SystemClock.elapsedRealtimeNanos(),
                    Process.myPid() + ":" + Application.getProcessName(),
                    Thread.currentThread().getId() + ":" + Thread.currentThread().getName(),
                    ignoreStackEntries);
        }

        /**
         * Write an accessibility trace log entry.
         */
        void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump,
                int callingUid, StackTraceElement[] callingStack, long timeStamp, int processId,
                long threadId, Set ignoreStackEntries) {
            if (!mEnabled) {
                return;
            }
            log(where, loggingTypes, callingParams, a11yDump, callingUid, callingStack, timeStamp,
                    String.valueOf(processId), String.valueOf(threadId), ignoreStackEntries);
        }

        private  String toStackTraceString(StackTraceElement[] stackTraceElements,
                Set ignoreStackEntries) {

            if (stackTraceElements == null) {
                return "";
            }

            StringBuilder stringBuilder = new StringBuilder();
            int i = 0;

            // Skip the first a few elements until after any ignoreStackEntries
            int firstMatch = -1;
            while (i < stackTraceElements.length) {
                for (String ele : ignoreStackEntries) {
                    if (stackTraceElements[i].toString().contains(ele)) {
                        // found the first stack element containing the ignorable stack entries
                        firstMatch = i;
                        break;
                    }
                }
                if (firstMatch < 0) {
                    // Haven't found the first match yet, continue
                    i++;
                } else {
                    break;
                }
            }
            int lastMatch = firstMatch;
            if (i < stackTraceElements.length) {
                i++;
                // Found the first match. Now look for the last match.
                while (i < stackTraceElements.length) {
                    for (String ele : ignoreStackEntries) {
                        if (stackTraceElements[i].toString().contains(ele)) {
                            // This is a match. Look at the next stack element.
                            lastMatch = i;
                            break;
                        }
                    }
                    if (lastMatch != i) {
                        // Found a no-match.
                        break;
                    }
                    i++;
                }
            }

            i = lastMatch + 1;
            while (i < stackTraceElements.length) {
                stringBuilder.append(stackTraceElements[i].toString()).append("\n");
                i++;
            }
            return stringBuilder.toString();
        }

        /**
         * Write the current state to the buffer
         */
        private void log(String where, long loggingTypes, String callingParams, byte[] a11yDump,
                int callingUid, StackTraceElement[] callingStack, long timeStamp,
                String processName, String threadName, Set ignoreStackEntries) {
            SomeArgs args = SomeArgs.obtain();
            args.argl1 = timeStamp;
            args.argl2 = loggingTypes;
            args.arg1 = where;
            args.arg2 = processName;
            args.arg3 = threadName;
            args.arg4 = ignoreStackEntries;
            args.arg5 = callingParams;
            args.arg6 = callingStack;
            args.arg7 = a11yDump;

            mHandler.obtainMessage(
                    LogHandler.MESSAGE_LOG_TRACE_ENTRY, callingUid, 0, args).sendToTarget();
        }

        /**
         * Writes the trace buffer to new file for the bugreport.
         */
        void writeTraceToFile() {
            mHandler.sendEmptyMessage(LogHandler.MESSAGE_WRITE_FILE);
        }

        private class LogHandler extends Handler {
            public static final int MESSAGE_LOG_TRACE_ENTRY = 1;
            public static final int MESSAGE_WRITE_FILE = 2;

            LogHandler(Looper looper) {
                super(looper);
            }

            @Override
            public void handleMessage(Message message) {
                switch (message.what) {
                    case MESSAGE_LOG_TRACE_ENTRY: {
                        final SomeArgs args = (SomeArgs) message.obj;
                        try {
                            ProtoOutputStream os = new ProtoOutputStream();
                            PackageManagerInternal pmInternal =
                                    LocalServices.getService(PackageManagerInternal.class);

                            long tokenOuter = os.start(ENTRY);

                            long reportedTimeStampNanos = args.argl1;
                            long currentElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
                            long timeDiffNanos =
                                    currentElapsedRealtimeNanos - reportedTimeStampNanos;
                            long currentTimeMillis = (new Date()).getTime();
                            long reportedTimeMillis =
                                    currentTimeMillis - (long) (timeDiffNanos / 1000000);
                            SimpleDateFormat fm = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");

                            os.write(ELAPSED_REALTIME_NANOS, reportedTimeStampNanos);
                            os.write(CALENDAR_TIME, fm.format(reportedTimeMillis).toString());

                            long loggingTypes = args.argl2;
                            List loggingTypeNames =
                                    AccessibilityTrace.getNamesOfLoggingTypes(loggingTypes);

                            for (String type : loggingTypeNames) {
                                os.write(LOGGING_TYPE, type);
                            }
                            os.write(WHERE, (String) args.arg1);
                            os.write(PROCESS_NAME, (String) args.arg2);
                            os.write(THREAD_ID_NAME, (String) args.arg3);
                            os.write(CALLING_PKG, pmInternal.getNameForUid(message.arg1));
                            os.write(CALLING_PARAMS, (String) args.arg5);

                            String callingStack = toStackTraceString(
                                    (StackTraceElement[]) args.arg6, (Set) args.arg4);

                            os.write(CALLING_STACKS, callingStack);
                            os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg7);

                            long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
                            synchronized (mService.mGlobalLock) {
                                mService.dumpDebugLocked(os, WindowTraceLogLevel.ALL);
                            }
                            os.end(tokenInner);
                            os.write(CPU_STATS, printCpuStats(reportedTimeStampNanos));

                            os.end(tokenOuter);
                            synchronized (mLock) {
                                mBuffer.add(os);
                            }
                        } catch (Exception e) {
                            Slog.e(TAG, "Exception while tracing state", e);
                        }
                        break;
                    }
                    case MESSAGE_WRITE_FILE: {
                        synchronized (mLock) {
                            writeTraceToFileInternal();
                        }
                        break;
                    }
                }
            }
        }

        /**
         * Writes the trace buffer to disk.
         */
        private void writeTraceToFileInternal() {
            try {
                ProtoOutputStream proto = new ProtoOutputStream();
                proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
                mBuffer.writeTraceToFile(mTraceFile, proto);
            } catch (IOException e) {
                Slog.e(TAG, "Unable to write buffer to file", e);
            }
        }

        /**
         * Returns the string of CPU stats.
         */
        private String printCpuStats(long timeStampNanos) {
            Pair stats = mService.mAmInternal.getAppProfileStatsForDebugging(
                    timeStampNanos, CPU_STATS_COUNT);

            return stats.first + stats.second;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy