Merge "Add ability to cancel ANR dialogs"

This commit is contained in:
TreeHugger Robot
2020-11-17 06:38:53 +00:00
committed by Android (Google) Code Review
8 changed files with 421 additions and 233 deletions

View File

@@ -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

View File

@@ -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) {

View File

@@ -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

View File

@@ -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;

View 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;
}
}

View File

@@ -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. */

View File

@@ -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();
}

View File

@@ -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");