Merge "Add ability to cancel ANR dialogs"
This commit is contained in:
committed by
Android (Google) Code Review
commit
b56d546518
@@ -352,6 +352,11 @@ public abstract class ActivityManagerInternal {
|
||||
public abstract boolean inputDispatchingTimedOut(Object proc, String activityShortComponentName,
|
||||
ApplicationInfo aInfo, String parentShortComponentName, Object parentProc,
|
||||
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
|
||||
|
||||
@@ -16460,6 +16460,12 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inputDispatchingResumed(int pid) {
|
||||
// TODO (b/171218828)
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastGlobalConfigurationChanged(int changes, boolean initLocale) {
|
||||
synchronized (ActivityManagerService.this) {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.android.server.input;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
@@ -2100,14 +2099,36 @@ public class InputManagerService extends IInputManager.Stub
|
||||
}
|
||||
|
||||
// Native callback.
|
||||
private long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token,
|
||||
String reason) {
|
||||
private void notifyNoFocusedWindowAnr(InputApplicationHandle inputApplicationHandle) {
|
||||
mWindowManagerCallbacks.notifyNoFocusedWindowAnr(inputApplicationHandle);
|
||||
}
|
||||
|
||||
// Native callback
|
||||
private void notifyConnectionUnresponsive(IBinder token, String reason) {
|
||||
Integer gestureMonitorPid;
|
||||
synchronized (mGestureMonitorPidsLock) {
|
||||
gestureMonitorPid = mGestureMonitorPidsByToken.get(token);
|
||||
}
|
||||
return mWindowManagerCallbacks.notifyANR(inputApplicationHandle, token, gestureMonitorPid,
|
||||
reason);
|
||||
if (gestureMonitorPid != null) {
|
||||
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.
|
||||
@@ -2354,29 +2375,58 @@ public class InputManagerService extends IInputManager.Stub
|
||||
*/
|
||||
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.
|
||||
* @param whenNanos the time when the change occurred
|
||||
* @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.
|
||||
* @param token the connection token of the broken channel
|
||||
*/
|
||||
public void notifyInputChannelBroken(IBinder token);
|
||||
void notifyInputChannelBroken(IBinder token);
|
||||
|
||||
/**
|
||||
* Notify the window manager about an application that is not responding.
|
||||
* Return a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
|
||||
* Notify the window manager about the focused application that does not have any focused
|
||||
* window and is unable to respond to focused input events.
|
||||
*/
|
||||
long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token,
|
||||
@Nullable Integer pid, String reason);
|
||||
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 a window that is unresponsive.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
void notifyWindowResponsive(@NonNull IBinder token);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
public int getPointerLayer();
|
||||
int getPointerLayer();
|
||||
|
||||
public int getPointerDisplayId();
|
||||
int getPointerDisplayId();
|
||||
|
||||
/**
|
||||
* Notifies window manager that a {@link android.view.MotionEvent#ACTION_DOWN} pointer event
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* @param reason The reason for the key dispatching time out.
|
||||
* @param windowPid The pid of the window key dispatching timed out on.
|
||||
* @param reason The reason for input dispatching time out.
|
||||
* @param windowPid The pid of the window input dispatching timed out on.
|
||||
* @return True if input dispatching should be aborted.
|
||||
*/
|
||||
public boolean keyDispatchingTimedOut(String reason, int windowPid) {
|
||||
public boolean inputDispatchingTimedOut(String reason, int windowPid) {
|
||||
ActivityRecord anrActivity;
|
||||
WindowProcessController anrApp;
|
||||
boolean windowFromSameProcessAsActivity;
|
||||
|
||||
249
services/core/java/com/android/server/wm/AnrController.java
Normal file
249
services/core/java/com/android/server/wm/AnrController.java
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,32 @@
|
||||
/*
|
||||
* 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.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
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.DEBUG_INPUT;
|
||||
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.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Build;
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Debug;
|
||||
import android.os.IBinder;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
import android.view.IWindow;
|
||||
import android.view.InputApplicationHandle;
|
||||
@@ -25,27 +34,14 @@ import android.view.KeyEvent;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.internal.util.function.pooled.PooledLambda;
|
||||
import com.android.server.am.ActivityManagerService;
|
||||
import com.android.server.input.InputManagerService;
|
||||
import com.android.server.wm.EmbeddedWindowController.EmbeddedWindow;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
|
||||
private static final String TAG = TAG_WITH_CLASS_NAME ? "InputManagerCallback" : TAG_WM;
|
||||
|
||||
/** 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 WindowManagerService mService;
|
||||
|
||||
// Set to true when the first input device configuration change notification
|
||||
@@ -96,186 +92,35 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
|
||||
}
|
||||
|
||||
/**
|
||||
* 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"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the window manager about an application that is not responding.
|
||||
* Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
|
||||
* Notifies the window manager about an application that is not responding because it has
|
||||
* no focused window.
|
||||
*
|
||||
* Called by the InputManager.
|
||||
*/
|
||||
@Override
|
||||
public long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token,
|
||||
@Nullable Integer pid, String reason) {
|
||||
final long startTime = SystemClock.uptimeMillis();
|
||||
try {
|
||||
return notifyANRInner(inputApplicationHandle, token, pid, reason);
|
||||
} finally {
|
||||
// Log the time because the method is called from InputDispatcher thread. It shouldn't
|
||||
// take too long because it blocks input while executing.
|
||||
Slog.d(TAG_WM, "notifyANR took " + (SystemClock.uptimeMillis() - startTime) + "ms");
|
||||
}
|
||||
public void notifyNoFocusedWindowAnr(@NonNull InputApplicationHandle applicationHandle) {
|
||||
mService.mAnrController.notifyAppUnresponsive(
|
||||
applicationHandle, "Application does not have a focused window");
|
||||
}
|
||||
|
||||
private long notifyANRInner(InputApplicationHandle inputApplicationHandle, IBinder token,
|
||||
@Nullable Integer pid, String reason) {
|
||||
ActivityRecord activity = null;
|
||||
WindowState windowState = null;
|
||||
boolean aboveSystem = false;
|
||||
int windowPid = pid != null ? pid : INVALID_PID;
|
||||
|
||||
preDumpIfLockTooSlow();
|
||||
|
||||
//TODO(b/141764879) Limit scope of wm lock when input calls notifyANR
|
||||
synchronized (mService.mGlobalLock) {
|
||||
|
||||
// Check if we can blame a window
|
||||
if (token != null) {
|
||||
windowState = mService.mInputToWindowMap.get(token);
|
||||
if (windowState != null) {
|
||||
activity = windowState.mActivityRecord;
|
||||
windowPid = windowState.mSession.mPid;
|
||||
// Figure out whether this window is layered above system windows.
|
||||
// We need to do this here to help the activity manager know how to
|
||||
// layer its ANR dialog.
|
||||
aboveSystem = isWindowAboveSystem(windowState);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we can blame an embedded window
|
||||
if (token != null && windowState == null) {
|
||||
EmbeddedWindow embeddedWindow = mService.mEmbeddedWindowController.get(token);
|
||||
if (embeddedWindow != null) {
|
||||
windowPid = embeddedWindow.mOwnerPid;
|
||||
WindowState hostWindowState = embeddedWindow.mHostWindowState;
|
||||
if (hostWindowState == null) {
|
||||
// The embedded window has no host window and we cannot easily determine
|
||||
// its z order. Try to place the anr dialog as high as possible.
|
||||
aboveSystem = true;
|
||||
} else {
|
||||
aboveSystem = isWindowAboveSystem(hostWindowState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we can blame an activity. If we don't have an activity to blame, pull out
|
||||
// the token passed in via input application handle. This can happen if there are no
|
||||
// focused windows but input dispatcher knows the focused app.
|
||||
if (activity == null && inputApplicationHandle != null) {
|
||||
activity = ActivityRecord.forTokenLocked(inputApplicationHandle.token);
|
||||
}
|
||||
|
||||
if (windowState != null) {
|
||||
Slog.i(TAG_WM, "Input event dispatching timed out "
|
||||
+ "sending to " + windowState.mAttrs.getTitle()
|
||||
+ ". Reason: " + reason);
|
||||
} else if (activity != null) {
|
||||
Slog.i(TAG_WM, "Input event dispatching timed out "
|
||||
+ "sending to application " + activity.stringName
|
||||
+ ". Reason: " + reason);
|
||||
} else {
|
||||
Slog.i(TAG_WM, "Input event dispatching timed out "
|
||||
+ ". Reason: " + reason);
|
||||
}
|
||||
|
||||
mService.saveANRStateLocked(activity, windowState, reason);
|
||||
}
|
||||
|
||||
// All the calls below need to happen without the WM lock held since they call into AM.
|
||||
mService.mAtmInternal.saveANRState(reason);
|
||||
|
||||
if (activity != null) {
|
||||
// Notify the activity manager about the timeout and let it decide whether
|
||||
// to abort dispatching or keep waiting.
|
||||
final boolean abort = activity.keyDispatchingTimedOut(reason, windowPid);
|
||||
if (!abort) {
|
||||
// The activity manager declined to abort dispatching.
|
||||
// Wait a bit longer and timeout again later.
|
||||
return TimeUnit.MILLISECONDS.toNanos(activity.mInputDispatchingTimeoutMillis);
|
||||
}
|
||||
} else if (windowState != null || windowPid != INVALID_PID) {
|
||||
// Notify the activity manager about the timeout and let it decide whether
|
||||
// to abort dispatching or keep waiting.
|
||||
long timeoutMillis =
|
||||
mService.mAmInternal.inputDispatchingTimedOut(windowPid, aboveSystem, reason);
|
||||
return TimeUnit.MILLISECONDS.toNanos(timeoutMillis);
|
||||
}
|
||||
return 0; // abort dispatching
|
||||
@Override
|
||||
public void notifyGestureMonitorUnresponsive(int pid, @NonNull String reason) {
|
||||
mService.mAnrController.notifyGestureMonitorUnresponsive(pid, reason);
|
||||
}
|
||||
|
||||
private boolean isWindowAboveSystem(WindowState windowState) {
|
||||
int systemAlertLayer = mService.mPolicy.getWindowLayerFromTypeLw(
|
||||
TYPE_APPLICATION_OVERLAY, windowState.mOwnerCanAddInternalSystemWindow);
|
||||
return windowState.mBaseLayer > systemAlertLayer;
|
||||
@Override
|
||||
public void notifyWindowUnresponsive(@NonNull IBinder token, String reason) {
|
||||
mService.mAnrController.notifyWindowUnresponsive(token, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyGestureMonitorResponsive(int pid) {
|
||||
mService.mAnrController.notifyGestureMonitorResponsive(pid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyWindowResponsive(@NonNull IBinder token) {
|
||||
mService.mAnrController.notifyWindowResponsive(token);
|
||||
}
|
||||
|
||||
/** Notifies that the input device configuration has changed. */
|
||||
|
||||
@@ -774,6 +774,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
|
||||
SettingsObserver mSettingsObserver;
|
||||
final EmbeddedWindowController mEmbeddedWindowController;
|
||||
final AnrController mAnrController;
|
||||
|
||||
@VisibleForTesting
|
||||
final class SettingsObserver extends ContentObserver {
|
||||
@@ -1376,7 +1377,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
mContext.getResources());
|
||||
|
||||
setGlobalShadowSettings();
|
||||
|
||||
mAnrController = new AnrController(this);
|
||||
mStartingSurfaceController = new StartingSurfaceController(this);
|
||||
}
|
||||
|
||||
@@ -4910,6 +4911,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
|
||||
if (newFocus != null) {
|
||||
mAnrController.onFocusChanged(newFocus);
|
||||
newFocus.reportFocusChangedSerialized(true);
|
||||
notifyFocusChanged();
|
||||
}
|
||||
|
||||
@@ -97,7 +97,9 @@ static struct {
|
||||
jmethodID notifyInputDevicesChanged;
|
||||
jmethodID notifySwitch;
|
||||
jmethodID notifyInputChannelBroken;
|
||||
jmethodID notifyANR;
|
||||
jmethodID notifyNoFocusedWindowAnr;
|
||||
jmethodID notifyConnectionUnresponsive;
|
||||
jmethodID notifyConnectionResponsive;
|
||||
jmethodID notifyFocusChanged;
|
||||
jmethodID notifyUntrustedTouch;
|
||||
jmethodID filterInputEvent;
|
||||
@@ -252,9 +254,9 @@ public:
|
||||
void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
|
||||
uint32_t policyFlags) override;
|
||||
void notifyConfigurationChanged(nsecs_t when) override;
|
||||
std::chrono::nanoseconds notifyAnr(
|
||||
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
|
||||
const sp<IBinder>& token, const std::string& reason) override;
|
||||
void notifyNoFocusedWindowAnr(const std::shared_ptr<InputApplicationHandle>& handle) override;
|
||||
void notifyConnectionUnresponsive(const sp<IBinder>& token, const std::string& reason) override;
|
||||
void notifyConnectionResponsive(const sp<IBinder>& token) override;
|
||||
void notifyInputChannelBroken(const sp<IBinder>& token) override;
|
||||
void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) override;
|
||||
void notifyUntrustedTouch(const std::string& obscuringPackage) override;
|
||||
@@ -713,11 +715,10 @@ static jobject getInputApplicationHandleObjLocalRef(
|
||||
return handle->getInputApplicationHandleObjLocalRef(env);
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds NativeInputManager::notifyAnr(
|
||||
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
|
||||
const sp<IBinder>& token, const std::string& reason) {
|
||||
void NativeInputManager::notifyNoFocusedWindowAnr(
|
||||
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
|
||||
#if DEBUG_INPUT_DISPATCHER_POLICY
|
||||
ALOGD("notifyANR");
|
||||
ALOGD("notifyNoFocusedWindowAnr");
|
||||
#endif
|
||||
ATRACE_CALL();
|
||||
|
||||
@@ -727,17 +728,42 @@ std::chrono::nanoseconds NativeInputManager::notifyAnr(
|
||||
jobject inputApplicationHandleObj =
|
||||
getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);
|
||||
|
||||
jobject tokenObj = javaObjectForIBinder(env, token);
|
||||
jstring reasonObj = env->NewStringUTF(reason.c_str());
|
||||
env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyNoFocusedWindowAnr,
|
||||
inputApplicationHandleObj);
|
||||
checkAndClearExceptionFromCallback(env, "notifyNoFocusedWindowAnr");
|
||||
}
|
||||
|
||||
jlong newTimeout = env->CallLongMethod(mServiceObj, gServiceClassInfo.notifyANR,
|
||||
inputApplicationHandleObj, tokenObj, reasonObj);
|
||||
if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
|
||||
newTimeout = 0; // abort dispatch
|
||||
} else {
|
||||
assert(newTimeout >= 0);
|
||||
}
|
||||
return std::chrono::nanoseconds(newTimeout);
|
||||
void NativeInputManager::notifyConnectionUnresponsive(const sp<IBinder>& token,
|
||||
const std::string& reason) {
|
||||
#if DEBUG_INPUT_DISPATCHER_POLICY
|
||||
ALOGD("notifyConnectionUnresponsive");
|
||||
#endif
|
||||
ATRACE_CALL();
|
||||
|
||||
JNIEnv* env = jniEnv();
|
||||
ScopedLocalFrame localFrame(env);
|
||||
|
||||
jobject tokenObj = javaObjectForIBinder(env, token);
|
||||
ScopedLocalRef<jstring> reasonObj(env, env->NewStringUTF(reason.c_str()));
|
||||
|
||||
env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyConnectionUnresponsive, tokenObj,
|
||||
reasonObj.get());
|
||||
checkAndClearExceptionFromCallback(env, "notifyConnectionUnresponsive");
|
||||
}
|
||||
|
||||
void NativeInputManager::notifyConnectionResponsive(const sp<IBinder>& token) {
|
||||
#if DEBUG_INPUT_DISPATCHER_POLICY
|
||||
ALOGD("notifyConnectionResponsive");
|
||||
#endif
|
||||
ATRACE_CALL();
|
||||
|
||||
JNIEnv* env = jniEnv();
|
||||
ScopedLocalFrame localFrame(env);
|
||||
|
||||
jobject tokenObj = javaObjectForIBinder(env, token);
|
||||
|
||||
env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyConnectionResponsive, tokenObj);
|
||||
checkAndClearExceptionFromCallback(env, "notifyConnectionResponsive");
|
||||
}
|
||||
|
||||
void NativeInputManager::notifyInputChannelBroken(const sp<IBinder>& token) {
|
||||
@@ -1909,9 +1935,14 @@ int register_android_server_InputManager(JNIEnv* env) {
|
||||
GET_METHOD_ID(gServiceClassInfo.notifyUntrustedTouch, clazz, "notifyUntrustedTouch",
|
||||
"(Ljava/lang/String;)V");
|
||||
|
||||
GET_METHOD_ID(gServiceClassInfo.notifyANR, clazz,
|
||||
"notifyANR",
|
||||
"(Landroid/view/InputApplicationHandle;Landroid/os/IBinder;Ljava/lang/String;)J");
|
||||
GET_METHOD_ID(gServiceClassInfo.notifyNoFocusedWindowAnr, clazz, "notifyNoFocusedWindowAnr",
|
||||
"(Landroid/view/InputApplicationHandle;)V");
|
||||
|
||||
GET_METHOD_ID(gServiceClassInfo.notifyConnectionUnresponsive, clazz,
|
||||
"notifyConnectionUnresponsive", "(Landroid/os/IBinder;Ljava/lang/String;)V");
|
||||
|
||||
GET_METHOD_ID(gServiceClassInfo.notifyConnectionResponsive, clazz, "notifyConnectionResponsive",
|
||||
"(Landroid/os/IBinder;)V");
|
||||
|
||||
GET_METHOD_ID(gServiceClassInfo.filterInputEvent, clazz,
|
||||
"filterInputEvent", "(Landroid/view/InputEvent;I)Z");
|
||||
|
||||
Reference in New Issue
Block a user