Loading core/java/android/app/ActivityManagerInternal.java +5 −0 Original line number Original line Diff line number Diff line Loading @@ -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 Loading services/core/java/com/android/server/am/ActivityManagerService.java +6 −0 Original line number Original line Diff line number Diff line Loading @@ -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) { services/core/java/com/android/server/input/InputManagerService.java +65 −15 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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 Loading Loading @@ -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 Loading services/core/java/com/android/server/wm/ActivityRecord.java +4 −4 Original line number Original line Diff line number Diff line Loading @@ -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; Loading services/core/java/com/android/server/wm/AnrController.java 0 → 100644 +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
core/java/android/app/ActivityManagerInternal.java +5 −0 Original line number Original line Diff line number Diff line Loading @@ -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 Loading
services/core/java/com/android/server/am/ActivityManagerService.java +6 −0 Original line number Original line Diff line number Diff line Loading @@ -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) {
services/core/java/com/android/server/input/InputManagerService.java +65 −15 Original line number Original line Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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 Loading Loading @@ -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 Loading
services/core/java/com/android/server/wm/ActivityRecord.java +4 −4 Original line number Original line Diff line number Diff line Loading @@ -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; Loading
services/core/java/com/android/server/wm/AnrController.java 0 → 100644 +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; } }