blob: b5e2f01d8fbb925c6c251098b44b1145cd03c02b [file] [log] [blame]
package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.app.ActivityManager;
import android.os.Debug;
import android.os.RemoteException;
import android.util.Slog;
import android.view.KeyEvent;
import android.view.WindowManager;
import com.android.server.input.InputApplicationHandle;
import com.android.server.input.InputManagerService;
import com.android.server.input.InputWindowHandle;
import java.io.PrintWriter;
final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
private final WindowManagerService mService;
// Set to true when the first input device configuration change notification
// is received to indicate that the input devices are ready.
private final Object mInputDevicesReadyMonitor = new Object();
private boolean mInputDevicesReady;
// When true, prevents input dispatch from proceeding until set to false again.
private boolean mInputDispatchFrozen;
// The reason the input is currently frozen or null if the input isn't frozen.
private String mInputFreezeReason = null;
// When true, input dispatch proceeds normally. Otherwise all events are dropped.
// Initially false, so that input does not get dispatched until boot is finished at
// which point the ActivityManager will enable dispatching.
private boolean mInputDispatchEnabled;
public InputManagerCallback(WindowManagerService service) {
mService = service;
}
/**
* Notifies the window manager about a broken input channel.
*
* Called by the InputManager.
*/
@Override
public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
if (inputWindowHandle == null) {
return;
}
synchronized (mService.mWindowMap) {
WindowState windowState = (WindowState) inputWindowHandle.windowState;
if (windowState != null) {
Slog.i(TAG_WM, "WINDOW DIED " + windowState);
windowState.removeIfPossible();
}
}
}
/**
* 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.
*/
@Override
public long notifyANR(InputApplicationHandle inputApplicationHandle,
InputWindowHandle inputWindowHandle, String reason) {
AppWindowToken appWindowToken = null;
WindowState windowState = null;
boolean aboveSystem = false;
synchronized (mService.mWindowMap) {
if (inputWindowHandle != null) {
windowState = (WindowState) inputWindowHandle.windowState;
if (windowState != null) {
appWindowToken = windowState.mAppToken;
}
}
if (appWindowToken == null && inputApplicationHandle != null) {
appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken;
}
if (windowState != null) {
Slog.i(TAG_WM, "Input event dispatching timed out "
+ "sending to " + windowState.mAttrs.getTitle()
+ ". Reason: " + reason);
// 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.
int systemAlertLayer = mService.mPolicy.getWindowLayerFromTypeLw(
TYPE_APPLICATION_OVERLAY, windowState.mOwnerCanAddInternalSystemWindow);
aboveSystem = windowState.mBaseLayer > systemAlertLayer;
} else if (appWindowToken != null) {
Slog.i(TAG_WM, "Input event dispatching timed out "
+ "sending to application " + appWindowToken.stringName
+ ". Reason: " + reason);
} else {
Slog.i(TAG_WM, "Input event dispatching timed out "
+ ". Reason: " + reason);
}
mService.saveANRStateLocked(appWindowToken, windowState, reason);
}
// All the calls below need to happen without the WM lock held since they call into AM.
mService.mAmInternal.saveANRState(reason);
if (appWindowToken != null && appWindowToken.appToken != null) {
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
final AppWindowContainerController controller = appWindowToken.getController();
final boolean abort = controller != null
&& controller.keyDispatchingTimedOut(reason,
(windowState != null) ? windowState.mSession.mPid : -1);
if (!abort) {
// The activity manager declined to abort dispatching.
// Wait a bit longer and timeout again later.
return appWindowToken.mInputDispatchingTimeoutNanos;
}
} else if (windowState != null) {
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
long timeout = mService.mAtmInternal.inputDispatchingTimedOut(
windowState.mSession.mPid, aboveSystem, reason);
if (timeout >= 0) {
// The activity manager declined to abort dispatching.
// Wait a bit longer and timeout again later.
return timeout * 1000000L; // nanoseconds
}
}
return 0; // abort dispatching
}
/** Notifies that the input device configuration has changed. */
@Override
public void notifyConfigurationChanged() {
// TODO(multi-display): Notify proper displays that are associated with this input device.
mService.sendNewConfiguration(DEFAULT_DISPLAY);
synchronized (mInputDevicesReadyMonitor) {
if (!mInputDevicesReady) {
mInputDevicesReady = true;
mInputDevicesReadyMonitor.notifyAll();
}
}
}
/** Notifies that the lid switch changed state. */
@Override
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
mService.mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
}
/** Notifies that the camera lens cover state has changed. */
@Override
public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
mService.mPolicy.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
}
/**
* Provides an opportunity for the window manager policy to intercept early key
* processing as soon as the key has been read from the device.
*/
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
}
/**
* Provides an opportunity for the window manager policy to intercept early motion event
* processing when the device is in a non-interactive state since these events are normally
* dropped.
*/
@Override
public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
return mService.mPolicy.interceptMotionBeforeQueueingNonInteractive(
whenNanos, policyFlags);
}
/**
* Provides an opportunity for the window manager policy to process a key before
* ordinary dispatch.
*/
@Override
public long interceptKeyBeforeDispatching(
InputWindowHandle focus, KeyEvent event, int policyFlags) {
WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
}
/**
* Provides an opportunity for the window manager policy to process a key that
* the application did not handle.
*/
@Override
public KeyEvent dispatchUnhandledKey(
InputWindowHandle focus, KeyEvent event, int policyFlags) {
WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
}
/** Callback to get pointer layer. */
@Override
public int getPointerLayer() {
return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER)
* WindowManagerService.TYPE_LAYER_MULTIPLIER
+ WindowManagerService.TYPE_LAYER_OFFSET;
}
/** Waits until the built-in input devices have been configured. */
public boolean waitForInputDevicesReady(long timeoutMillis) {
synchronized (mInputDevicesReadyMonitor) {
if (!mInputDevicesReady) {
try {
mInputDevicesReadyMonitor.wait(timeoutMillis);
} catch (InterruptedException ex) {
}
}
return mInputDevicesReady;
}
}
public void freezeInputDispatchingLw() {
if (!mInputDispatchFrozen) {
if (DEBUG_INPUT) {
Slog.v(TAG_WM, "Freezing input dispatching");
}
mInputDispatchFrozen = true;
if (DEBUG_INPUT) {
mInputFreezeReason = Debug.getCallers(6);
}
updateInputDispatchModeLw();
}
}
public void thawInputDispatchingLw() {
if (mInputDispatchFrozen) {
if (DEBUG_INPUT) {
Slog.v(TAG_WM, "Thawing input dispatching");
}
mInputDispatchFrozen = false;
mInputFreezeReason = null;
updateInputDispatchModeLw();
}
}
public void setEventDispatchingLw(boolean enabled) {
if (mInputDispatchEnabled != enabled) {
if (DEBUG_INPUT) {
Slog.v(TAG_WM, "Setting event dispatching to " + enabled);
}
mInputDispatchEnabled = enabled;
updateInputDispatchModeLw();
}
}
private void updateInputDispatchModeLw() {
mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
}
void dump(PrintWriter pw, String prefix) {
if (mInputFreezeReason != null) {
pw.println(prefix + "mInputFreezeReason=" + mInputFreezeReason);
}
}
}