Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b56d5465 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add ability to cancel ANR dialogs"

parents d5e3147c 38309e8f
Loading
Loading
Loading
Loading
+5 −0
Original line number Original line Diff line number Diff line
@@ -352,6 +352,11 @@ public abstract class ActivityManagerInternal {
    public abstract boolean inputDispatchingTimedOut(Object proc, String activityShortComponentName,
    public abstract boolean inputDispatchingTimedOut(Object proc, String activityShortComponentName,
            ApplicationInfo aInfo, String parentShortComponentName, Object parentProc,
            ApplicationInfo aInfo, String parentShortComponentName, Object parentProc,
            boolean aboveSystem, String reason);
            boolean aboveSystem, String reason);
    /**
     * App started responding to input events. This signal can be used to abort the ANR process and
     * hide the ANR dialog.
     */
    public abstract void inputDispatchingResumed(int pid);


    /**
    /**
     * Sends {@link android.content.Intent#ACTION_CONFIGURATION_CHANGED} with all the appropriate
     * Sends {@link android.content.Intent#ACTION_CONFIGURATION_CHANGED} with all the appropriate
+6 −0
Original line number Original line Diff line number Diff line
@@ -16460,6 +16460,12 @@ public class ActivityManagerService extends IActivityManager.Stub
        }
        }
        @Override
        public void inputDispatchingResumed(int pid) {
            // TODO (b/171218828)
            return;
        }
        @Override
        @Override
        public void broadcastGlobalConfigurationChanged(int changes, boolean initLocale) {
        public void broadcastGlobalConfigurationChanged(int changes, boolean initLocale) {
            synchronized (ActivityManagerService.this) {
            synchronized (ActivityManagerService.this) {
+65 −15
Original line number Original line Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.server.input;
package com.android.server.input;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.PendingIntent;
@@ -2100,14 +2099,36 @@ public class InputManagerService extends IInputManager.Stub
    }
    }


    // Native callback.
    // Native callback.
    private long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token,
    private void notifyNoFocusedWindowAnr(InputApplicationHandle inputApplicationHandle) {
            String reason) {
        mWindowManagerCallbacks.notifyNoFocusedWindowAnr(inputApplicationHandle);
    }

    // Native callback
    private void notifyConnectionUnresponsive(IBinder token, String reason) {
        Integer gestureMonitorPid;
        Integer gestureMonitorPid;
        synchronized (mGestureMonitorPidsLock) {
        synchronized (mGestureMonitorPidsLock) {
            gestureMonitorPid = mGestureMonitorPidsByToken.get(token);
            gestureMonitorPid = mGestureMonitorPidsByToken.get(token);
        }
        }
        return mWindowManagerCallbacks.notifyANR(inputApplicationHandle, token, gestureMonitorPid,
        if (gestureMonitorPid != null) {
                reason);
            mWindowManagerCallbacks.notifyGestureMonitorUnresponsive(gestureMonitorPid, reason);
            return;
        }
        // If we couldn't find a gesture monitor for this token, it's a window
        mWindowManagerCallbacks.notifyWindowUnresponsive(token, reason);
    }

    // Native callback
    private void notifyConnectionResponsive(IBinder token) {
        Integer gestureMonitorPid;
        synchronized (mGestureMonitorPidsLock) {
            gestureMonitorPid = mGestureMonitorPidsByToken.get(token);
        }
        if (gestureMonitorPid != null) {
            mWindowManagerCallbacks.notifyGestureMonitorResponsive(gestureMonitorPid);
            return;
        }
        // If we couldn't find a gesture monitor for this token, it's a window
        mWindowManagerCallbacks.notifyWindowResponsive(token);
    }
    }


    // Native callback.
    // Native callback.
@@ -2354,29 +2375,58 @@ public class InputManagerService extends IInputManager.Stub
     */
     */
    public interface WindowManagerCallbacks extends LidSwitchCallback {
    public interface WindowManagerCallbacks extends LidSwitchCallback {
        /**
        /**
         * This callback is invoked when the confuguration changes.
         * This callback is invoked when the configuration changes.
         */
         */
        public void notifyConfigurationChanged();
        void notifyConfigurationChanged();


        /**
        /**
         * This callback is invoked when the camera lens cover switch changes state.
         * This callback is invoked when the camera lens cover switch changes state.
         * @param whenNanos the time when the change occurred
         * @param whenNanos the time when the change occurred
         * @param lensCovered true is the lens is covered
         * @param lensCovered true is the lens is covered
         */
         */
        public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered);
        void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered);


        /**
        /**
         * This callback is invoked when an input channel is closed unexpectedly.
         * This callback is invoked when an input channel is closed unexpectedly.
         * @param token the connection token of the broken channel
         * @param token the connection token of the broken channel
         */
         */
        public void notifyInputChannelBroken(IBinder token);
        void notifyInputChannelBroken(IBinder token);

        /**
         * Notify the window manager about the focused application that does not have any focused
         * window and is unable to respond to focused input events.
         */
        void notifyNoFocusedWindowAnr(InputApplicationHandle applicationHandle);

        /**
         * Notify the window manager about a gesture monitor that is unresponsive.
         *
         * @param pid the pid of the gesture monitor process
         * @param reason the reason why this connection is unresponsive
         */
        void notifyGestureMonitorUnresponsive(int pid, @NonNull String reason);


        /**
        /**
         * Notify the window manager about an application that is not responding.
         * Notify the window manager about a window that is unresponsive.
         * Return a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
         *
         * @param token the token that can be used to look up the window
         * @param reason the reason why this connection is unresponsive
         */
        void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull String reason);

        /**
         * Notify the window manager about a gesture monitor that has become responsive.
         *
         * @param pid the pid of the gesture monitor process
         */
        void notifyGestureMonitorResponsive(int pid);

        /**
         * Notify the window manager about a window that has become responsive.
         *
         * @param token the token that can be used to look up the window
         */
         */
        long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token,
        void notifyWindowResponsive(@NonNull IBinder token);
                @Nullable Integer pid, String reason);


        /**
        /**
         * This callback is invoked when an event first arrives to InputDispatcher and before it is
         * This callback is invoked when an event first arrives to InputDispatcher and before it is
@@ -2415,9 +2465,9 @@ public class InputManagerService extends IInputManager.Stub
         */
         */
        KeyEvent dispatchUnhandledKey(IBinder token, KeyEvent event, int policyFlags);
        KeyEvent dispatchUnhandledKey(IBinder token, KeyEvent event, int policyFlags);


        public int getPointerLayer();
        int getPointerLayer();


        public int getPointerDisplayId();
        int getPointerDisplayId();


        /**
        /**
         * Notifies window manager that a {@link android.view.MotionEvent#ACTION_DOWN} pointer event
         * Notifies window manager that a {@link android.view.MotionEvent#ACTION_DOWN} pointer event
+4 −4
Original line number Original line Diff line number Diff line
@@ -5637,14 +5637,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
    }
    }


    /**
    /**
     * Called when the key dispatching to a window associated with the app window container
     * Called when the input dispatching to a window associated with the app window container
     * timed-out.
     * timed-out.
     *
     *
     * @param reason The reason for the key dispatching time out.
     * @param reason The reason for input dispatching time out.
     * @param windowPid The pid of the window key dispatching timed out on.
     * @param windowPid The pid of the window input dispatching timed out on.
     * @return True if input dispatching should be aborted.
     * @return True if input dispatching should be aborted.
     */
     */
    public boolean keyDispatchingTimedOut(String reason, int windowPid) {
    public boolean inputDispatchingTimedOut(String reason, int windowPid) {
        ActivityRecord anrActivity;
        ActivityRecord anrActivity;
        WindowProcessController anrApp;
        WindowProcessController anrApp;
        boolean windowFromSameProcessAsActivity;
        boolean windowFromSameProcessAsActivity;
+249 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2020 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.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

import static com.android.server.wm.ActivityRecord.INVALID_PID;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;

import android.os.Build;
import android.os.IBinder;
import android.os.Process;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
import android.view.InputApplicationHandle;

import com.android.server.am.ActivityManagerService;
import com.android.server.wm.EmbeddedWindowController.EmbeddedWindow;

import java.io.File;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * Translates input channel tokens and app tokens to ProcessRecords and PIDs that AMS can use to
 * blame unresponsive apps. This class also handles dumping WMS state when an app becomes
 * unresponsive.
 */
class AnrController {
    /** Prevent spamming the traces because pre-dump cannot aware duplicated ANR. */
    private static final long PRE_DUMP_MIN_INTERVAL_MS = TimeUnit.SECONDS.toMillis(20);
    /** The timeout to detect if a monitor is held for a while. */
    private static final long PRE_DUMP_MONITOR_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(1);
    /** The last time pre-dump was executed. */
    private volatile long mLastPreDumpTimeMs;

    private final SparseArray<ActivityRecord> mUnresponsiveAppByDisplay = new SparseArray<>();

    private final WindowManagerService mService;
    AnrController(WindowManagerService service) {
        mService = service;
    }

    void notifyAppUnresponsive(InputApplicationHandle applicationHandle, String reason) {
        preDumpIfLockTooSlow();
        final ActivityRecord activity;
        synchronized (mService.mGlobalLock) {
            activity = ActivityRecord.forTokenLocked(applicationHandle.token);
            if (activity == null) {
                Slog.e(TAG_WM, "Unknown app appToken:" + applicationHandle.name
                        + ". Dropping notifyNoFocusedWindowAnr request");
                return;
            }
            Slog.i(TAG_WM, "ANR in " + activity.getName() + ".  Reason: " + reason);
            dumpAnrStateLocked(activity, null /* windowState */, reason);
            mUnresponsiveAppByDisplay.put(activity.getDisplayId(), activity);
        }
        activity.inputDispatchingTimedOut(reason, INVALID_PID);
    }

    void notifyWindowUnresponsive(IBinder inputToken, String reason) {
        preDumpIfLockTooSlow();
        final int pid;
        final boolean aboveSystem;
        final ActivityRecord activity;
        synchronized (mService.mGlobalLock) {
            WindowState windowState = mService.mInputToWindowMap.get(inputToken);
            if (windowState != null) {
                pid = windowState.mSession.mPid;
                activity = windowState.mActivityRecord;
                Slog.i(TAG_WM, "ANR in " + windowState.mAttrs.getTitle() + ". Reason:" + reason);
            } else {
                EmbeddedWindow embeddedWindow = mService.mEmbeddedWindowController.get(inputToken);
                if (embeddedWindow == null) {
                    Slog.e(TAG_WM, "Unknown token, dropping notifyConnectionUnresponsive request");
                    return;
                }
                pid = embeddedWindow.mOwnerPid;
                windowState = embeddedWindow.mHostWindowState;
                activity = null; // Don't blame the host process, instead blame the embedded pid.
            }
            aboveSystem = isWindowAboveSystem(windowState);
            dumpAnrStateLocked(activity, windowState, reason);
        }
        if (activity != null) {
            activity.inputDispatchingTimedOut(reason, pid);
        } else {
            mService.mAmInternal.inputDispatchingTimedOut(pid, aboveSystem, reason);
        }
    }

    void notifyWindowResponsive(IBinder inputToken) {
        final int pid;
        synchronized (mService.mGlobalLock) {
            WindowState windowState = mService.mInputToWindowMap.get(inputToken);
            if (windowState != null) {
                pid = windowState.mSession.mPid;
            } else {
                // Check if the token belongs to an embedded window.
                EmbeddedWindow embeddedWindow = mService.mEmbeddedWindowController.get(inputToken);
                if (embeddedWindow == null) {
                    Slog.e(TAG_WM,
                            "Unknown token, dropping notifyWindowConnectionResponsive request");
                    return;
                }
                pid = embeddedWindow.mOwnerPid;
            }
        }
        mService.mAmInternal.inputDispatchingResumed(pid);
    }

    void notifyGestureMonitorUnresponsive(int gestureMonitorPid, String reason) {
        preDumpIfLockTooSlow();
        synchronized (mService.mGlobalLock) {
            Slog.i(TAG_WM, "ANR in gesture monitor owned by pid:" + gestureMonitorPid
                    + ".  Reason: " + reason);
            dumpAnrStateLocked(null /* activity */, null /* windowState */, reason);
        }
        mService.mAmInternal.inputDispatchingTimedOut(gestureMonitorPid, /* aboveSystem */ true,
                reason);
    }

    void notifyGestureMonitorResponsive(int gestureMonitorPid) {
        mService.mAmInternal.inputDispatchingResumed(gestureMonitorPid);
    }

    /**
     * If we reported an unresponsive apps to AMS, notify AMS that the app is now responsive if a
     * window belonging to the app gets focused.
     * <p>
     * @param newFocus new focused window
     */
    void onFocusChanged(WindowState newFocus) {
        ActivityRecord unresponsiveApp;
        synchronized (mService.mGlobalLock) {
            unresponsiveApp = mUnresponsiveAppByDisplay.get(newFocus.getDisplayId());
            if (unresponsiveApp == null || unresponsiveApp != newFocus.mActivityRecord) {
                return;
            }
        }
        mService.mAmInternal.inputDispatchingResumed(unresponsiveApp.getPid());
    }

    /**
     * Pre-dump stack trace if the locks of activity manager or window manager (they may be locked
     * in the path of reporting ANR) cannot be acquired in time. That provides the stack traces
     * before the real blocking symptom has gone.
     * <p>
     * Do not hold the {@link WindowManagerGlobalLock} while calling this method.
     */
    private void preDumpIfLockTooSlow() {
        if (!Build.IS_DEBUGGABLE)  {
            return;
        }
        final long now = SystemClock.uptimeMillis();
        if (mLastPreDumpTimeMs > 0 && now - mLastPreDumpTimeMs < PRE_DUMP_MIN_INTERVAL_MS) {
            return;
        }

        final boolean[] shouldDumpSf = { true };
        final ArrayMap<String, Runnable> monitors = new ArrayMap<>(2);
        monitors.put(TAG_WM, mService::monitor);
        monitors.put("ActivityManager", mService.mAmInternal::monitor);
        final CountDownLatch latch = new CountDownLatch(monitors.size());
        // The pre-dump will execute if one of the monitors doesn't complete within the timeout.
        for (int i = 0; i < monitors.size(); i++) {
            final String name = monitors.keyAt(i);
            final Runnable monitor = monitors.valueAt(i);
            // Always create new thread to avoid noise of existing threads. Suppose here won't
            // create too many threads because it means that watchdog will be triggered first.
            new Thread() {
                @Override
                public void run() {
                    monitor.run();
                    latch.countDown();
                    final long elapsed = SystemClock.uptimeMillis() - now;
                    if (elapsed > PRE_DUMP_MONITOR_TIMEOUT_MS) {
                        Slog.i(TAG_WM, "Pre-dump acquired " + name + " in " + elapsed + "ms");
                    } else if (TAG_WM.equals(name)) {
                        // Window manager is the main client of SurfaceFlinger. If window manager
                        // is responsive, the stack traces of SurfaceFlinger may not be important.
                        shouldDumpSf[0] = false;
                    }
                };
            }.start();
        }
        try {
            if (latch.await(PRE_DUMP_MONITOR_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
                return;
            }
        } catch (InterruptedException ignored) { }
        mLastPreDumpTimeMs = now;
        Slog.i(TAG_WM, "Pre-dump for unresponsive");

        final ArrayList<Integer> firstPids = new ArrayList<>(1);
        firstPids.add(ActivityManagerService.MY_PID);
        ArrayList<Integer> nativePids = null;
        final int[] pids = shouldDumpSf[0]
                ? Process.getPidsForCommands(new String[] { "/system/bin/surfaceflinger" })
                : null;
        if (pids != null) {
            nativePids = new ArrayList<>(1);
            for (int pid : pids) {
                nativePids.add(pid);
            }
        }

        final File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
                null /* processCpuTracker */, null /* lastPids */, nativePids,
                null /* logExceptionCreatingFile */);
        if (tracesFile != null) {
            tracesFile.renameTo(new File(tracesFile.getParent(), tracesFile.getName() + "_pre"));
        }
    }

    private void dumpAnrStateLocked(ActivityRecord activity, WindowState windowState,
                                    String reason) {
        mService.saveANRStateLocked(activity, windowState, reason);
        mService.mAtmInternal.saveANRState(reason);
    }

    private boolean isWindowAboveSystem(WindowState windowState) {
        if (windowState == null) {
            // If the window state is not available we cannot easily determine its z order. Try to
            // place the anr dialog as high as possible.
            return true;
        }
        int systemAlertLayer = mService.mPolicy.getWindowLayerFromTypeLw(
                TYPE_APPLICATION_OVERLAY, windowState.mOwnerCanAddInternalSystemWindow);
        return windowState.mBaseLayer > systemAlertLayer;
    }
}
Loading