Make input dispatcher only ANR for foreground windows.
Redesigned the input dispatcher's ANR timeout mechanism so it is much
closer to Froyo's policy. ANR is only ever signalled if the dispatcher
is waiting on a window to finish processing its previous event(s) and
there is new pending input.
In the old code, we tracked the dispatch timeout separately for each
input channel. This was somewhat complicated and also resulted in the
situation where applications could ANR long after the user had pushed
them into the background.
Change-Id: I666ecada0952d4b95f1d67b9f733842b745c7f4b
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 90d036a..024aec5 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -24,18 +24,13 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Environment;
-import android.os.LocalPowerManager;
-import android.os.PowerManager;
import android.os.SystemProperties;
import android.util.Slog;
import android.util.Xml;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
import android.view.Surface;
-import android.view.WindowManagerPolicy;
import java.io.BufferedReader;
import java.io.File;
@@ -83,7 +78,6 @@
private static native void nativeSetInputWindows(InputWindow[] windows);
private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
private static native void nativeSetFocusedApplication(InputApplication application);
- private static native void nativePreemptInputDispatch();
private static native InputDevice nativeGetInputDevice(int deviceId);
private static native int[] nativeGetInputDeviceIds();
private static native String nativeDump();
@@ -331,10 +325,6 @@
nativeSetFocusedApplication(application);
}
- public void preemptInputDispatch() {
- nativePreemptInputDispatch();
- }
-
public void setInputDispatchMode(boolean enabled, boolean frozen) {
nativeSetInputDispatchMode(enabled, frozen);
}
@@ -395,20 +385,10 @@
public void notifyInputChannelBroken(InputChannel inputChannel) {
mWindowManagerService.mInputMonitor.notifyInputChannelBroken(inputChannel);
}
-
- @SuppressWarnings("unused")
- public long notifyInputChannelANR(InputChannel inputChannel) {
- return mWindowManagerService.mInputMonitor.notifyInputChannelANR(inputChannel);
- }
-
- @SuppressWarnings("unused")
- public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
- mWindowManagerService.mInputMonitor.notifyInputChannelRecoveredFromANR(inputChannel);
- }
@SuppressWarnings("unused")
- public long notifyANR(Object token) {
- return mWindowManagerService.mInputMonitor.notifyANR(token);
+ public long notifyANR(Object token, InputChannel inputChannel) {
+ return mWindowManagerService.mInputMonitor.notifyANR(token, inputChannel);
}
@SuppressWarnings("unused")
diff --git a/services/java/com/android/server/InputWindow.java b/services/java/com/android/server/InputWindow.java
index dbc59ef..befc770 100644
--- a/services/java/com/android/server/InputWindow.java
+++ b/services/java/com/android/server/InputWindow.java
@@ -27,6 +27,9 @@
// The input channel associated with the window.
public InputChannel inputChannel;
+ // The window name.
+ public String name;
+
// Window layout params attributes. (WindowManager.LayoutParams)
public int layoutParamsFlags;
public int layoutParamsType;
@@ -55,6 +58,9 @@
// Window is visible.
public boolean visible;
+ // Window can receive keys.
+ public boolean canReceiveKeys;
+
// Window has focus.
public boolean hasFocus;
@@ -64,6 +70,9 @@
// Input event dispatching is paused.
public boolean paused;
+ // Window layer.
+ public int layer;
+
// Id of process and user that owns the window.
public int ownerPid;
public int ownerUid;
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 27d875e..9835098 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5096,61 +5096,39 @@
}
}
- /* Notifies the window manager about an input channel that is not responding.
+ /* 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.
*
* Called by the InputManager.
*/
- public long notifyInputChannelANR(InputChannel inputChannel) {
- AppWindowToken token;
- synchronized (mWindowMap) {
- WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
- if (windowState == null) {
- return 0; // window is unknown, abort dispatching
+ public long notifyANR(Object token, InputChannel inputChannel) {
+ AppWindowToken appWindowToken = null;
+ if (inputChannel != null) {
+ synchronized (mWindowMap) {
+ WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+ if (windowState != null) {
+ Slog.i(TAG, "Input event dispatching timed out sending to "
+ + windowState.mAttrs.getTitle());
+ appWindowToken = windowState.mAppToken;
+ }
}
-
- Slog.i(TAG, "Input event dispatching timed out sending to "
- + windowState.mAttrs.getTitle());
- token = windowState.mAppToken;
}
- return notifyANRInternal(token);
- }
-
- /* Notifies the window manager about an input channel spontaneously recovering from ANR
- * by successfully delivering the event that originally timed out.
- *
- * Called by the InputManager.
- */
- public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
- // Nothing to do just now.
- // Just wait for the user to dismiss the ANR dialog.
- }
-
- /* Notifies the window manager about an application that is not responding
- * in general rather than with respect to a particular input channel.
- * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
- *
- * Called by the InputManager.
- */
- public long notifyANR(Object token) {
- AppWindowToken appWindowToken = (AppWindowToken) token;
+ if (appWindowToken == null && token != null) {
+ appWindowToken = (AppWindowToken) token;
+ Slog.i(TAG, "Input event dispatching timed out sending to application "
+ + appWindowToken.stringName);
+ }
- Slog.i(TAG, "Input event dispatching timed out sending to application "
- + appWindowToken.stringName);
- return notifyANRInternal(appWindowToken);
- }
-
- private long notifyANRInternal(AppWindowToken token) {
- if (token != null && token.appToken != null) {
+ if (appWindowToken != null && appWindowToken.appToken != null) {
try {
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
- boolean abort = token.appToken.keyDispatchingTimedOut();
+ boolean abort = appWindowToken.appToken.keyDispatchingTimedOut();
if (! abort) {
// The activity manager declined to abort dispatching.
// Wait a bit longer and timeout again later.
- return token.inputDispatchingTimeoutNanos;
+ return appWindowToken.inputDispatchingTimeoutNanos;
}
} catch (RemoteException ex) {
}
@@ -5203,13 +5181,16 @@
// Add a window to our list of input windows.
final InputWindow inputWindow = mTempInputWindows.add();
inputWindow.inputChannel = child.mInputChannel;
+ inputWindow.name = child.toString();
inputWindow.layoutParamsFlags = flags;
inputWindow.layoutParamsType = type;
inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
inputWindow.visible = isVisible;
+ inputWindow.canReceiveKeys = child.canReceiveKeys();
inputWindow.hasFocus = hasFocus;
inputWindow.hasWallpaper = hasWallpaper;
inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false;
+ inputWindow.layer = child.mLayer;
inputWindow.ownerPid = child.mSession.mPid;
inputWindow.ownerUid = child.mSession.mUid;
@@ -5302,23 +5283,6 @@
if (newWindow != mInputFocus) {
if (newWindow != null && newWindow.canReceiveKeys()) {
- // If the new input focus is an error window or appears above the current
- // input focus, preempt any pending synchronous dispatch so that we can
- // start delivering events to the new input focus as soon as possible.
- if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) {
- if (DEBUG_INPUT) {
- Slog.v(TAG, "New SYSTEM_ERROR window; resetting state");
- }
- preemptInputDispatchLw();
- } else if (mInputFocus != null && newWindow.mLayer > mInputFocus.mLayer) {
- if (DEBUG_INPUT) {
- Slog.v(TAG, "Transferring focus to new window at higher layer: "
- + "old win layer=" + mInputFocus.mLayer
- + ", new win layer=" + newWindow.mLayer);
- }
- preemptInputDispatchLw();
- }
-
// Displaying a window implicitly causes dispatching to be unpaused.
// This is to protect against bugs if someone pauses dispatching but
// forgets to resume.
@@ -5330,14 +5294,6 @@
}
}
- /* Tells the dispatcher to stop waiting for its current synchronous event targets.
- * Essentially, just makes those dispatches asynchronous so a new dispatch cycle
- * can begin.
- */
- private void preemptInputDispatchLw() {
- mInputManager.preemptInputDispatch();
- }
-
public void setFocusedAppLw(AppWindowToken newApp) {
// Focused app has changed.
if (newApp == null) {