From 964305e1b2d7fdf04f7feb0d99887e1eedb0ae90 Mon Sep 17 00:00:00 2001 From: HQ Liu Date: Mon, 14 Feb 2022 16:52:31 -0800 Subject: [PATCH] Remove the current input focused window when ANR When a freeform app launched and focused, but its window is not ready, the input focus stays on the window of the previously focused freeform app. The focus should be removed from the previously foccused app, so ANR can be triggered correctly. Bug: 216852742 Test: atest AnrTests#slowOnCreateWithKeyEventTriggersAnr Change-Id: Id00c147ffab9b11fb2abdf0f3020bc017a31bea7 --- core/java/android/view/SurfaceControl.java | 12 ++++++++++++ core/jni/android_view_SurfaceControl.cpp | 11 +++++++++++ data/etc/services.core.protolog.json | 6 ++++++ .../java/com/android/server/wm/InputMonitor.java | 11 +++++++++++ 4 files changed, 40 insertions(+) diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 98cef95885bdb..2e155ddfd3d99 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -248,6 +248,7 @@ public final class SurfaceControl implements Parcelable { private static native void nativeSetFixedTransformHint(long transactionObj, long nativeObject, int transformHint); + private static native void nativeRemoveCurrentInputFocus(long nativeObject, int displayId); private static native void nativeSetFocusedWindow(long transactionObj, IBinder toToken, String windowName, IBinder focusedToken, String focusedWindowName, int displayId); private static native void nativeSetFrameTimelineVsync(long transactionObj, @@ -3652,6 +3653,17 @@ public final class SurfaceControl implements Parcelable { return this; } + /** + * Removes the input focus from the current window which is having the input focus. Should + * only be called when the current focused app is not responding and the current focused + * window is not beloged to the current focused app. + * @hide + */ + public Transaction removeCurrentInputFocus(int displayId) { + nativeRemoveCurrentInputFocus(mNativeObject, displayId); + return this; + } + /** * Adds or removes the flag SKIP_SCREENSHOT of the surface. Setting the flag is equivalent * to creating the Surface with the {@link #SKIP_SCREENSHOT} flag. diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index fb5b5ffaac48c..64571adac2ef6 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -1833,6 +1833,15 @@ static jlong nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) { return reinterpret_cast(surfaceControl->getHandle().get()); } +static void nativeRemoveCurrentInputFocus(JNIEnv* env, jclass clazz, jlong transactionObj, + jint displayId) { + auto transaction = reinterpret_cast(transactionObj); + FocusRequest request; + request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); + request.displayId = displayId; + transaction->setFocusedWindow(request); +} + static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj, jobject toTokenObj, jstring windowNameJstr, jobject focusedTokenObj, jstring focusedWindowNameJstr, @@ -2167,6 +2176,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetFixedTransformHint}, {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I)V", (void*)nativeSetFocusedWindow}, + {"nativeRemoveCurrentInputFocus", "(JI)V", + (void*)nativeRemoveCurrentInputFocus}, {"nativeSetFrameTimelineVsync", "(JJ)V", (void*)nativeSetFrameTimelineVsync }, {"nativeAddJankDataListener", "(JJ)V", diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 177965534b184..ed8986993ab0e 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -3793,6 +3793,12 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java" }, + "2001473656": { + "message": "App %s is focused, but the window is not ready. Start a transaction to remove focus from the window of non-focused apps.", + "level": "VERBOSE", + "group": "WM_DEBUG_FOCUS_LIGHT", + "at": "com\/android\/server\/wm\/InputMonitor.java" + }, "2018454757": { "message": "WS.removeImmediately: %s Already removed...", "level": "VERBOSE", diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 44818a8c9ee45..31ae864fc0909 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -414,6 +414,17 @@ final class InputMonitor { final IBinder focusToken = focus != null ? focus.mInputChannelToken : null; if (focusToken == null) { mInputFocus = null; + // When an app is focused, but its window is not showing yet, remove the input focus + // from the current window. + if (mDisplayContent.mFocusedApp != null) { + ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "App %s is focused," + + " but the window is not ready. Start a transaction to remove focus from" + + " the window of non-focused apps.", + mDisplayContent.mFocusedApp.getName()); + EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Requesting to set focus to null window", + "reason=UpdateInputWindows"); + mInputTransaction.removeCurrentInputFocus(mDisplayId); + } return; }