diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 4bc8a5ef0dd96..6e7bb83b2fcbd 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -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 diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 4ca135808996c..d8907628d2116 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -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) { diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 9658800fe952f..f2eb5af51616d 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -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 diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 107690614f82d..2686ef776a354 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -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; diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java new file mode 100644 index 0000000000000..892db9c33dbde --- /dev/null +++ b/services/core/java/com/android/server/wm/AnrController.java @@ -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 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. + *

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

+ * 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 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 firstPids = new ArrayList<>(1); + firstPids.add(ActivityManagerService.MY_PID); + ArrayList 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; + } +} diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index 0978636ea502c..35f5615347bb0 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -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. - *

- * 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 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 firstPids = new ArrayList<>(1); - firstPids.add(ActivityManagerService.MY_PID); - ArrayList 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. */ diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 8cbeccdfa15cd..782d6e39bf538 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -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(); } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 3a001965402f4..9e0fb567975a3 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -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, - const sp& token, const std::string& reason) override; + void notifyNoFocusedWindowAnr(const std::shared_ptr& handle) override; + void notifyConnectionUnresponsive(const sp& token, const std::string& reason) override; + void notifyConnectionResponsive(const sp& token) override; void notifyInputChannelBroken(const sp& token) override; void notifyFocusChanged(const sp& oldToken, const sp& 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, - const sp& token, const std::string& reason) { +void NativeInputManager::notifyNoFocusedWindowAnr( + const std::shared_ptr& 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& 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 reasonObj(env, env->NewStringUTF(reason.c_str())); + + env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyConnectionUnresponsive, tokenObj, + reasonObj.get()); + checkAndClearExceptionFromCallback(env, "notifyConnectionUnresponsive"); +} + +void NativeInputManager::notifyConnectionResponsive(const sp& 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& 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");