From 8874a961f724cf84121966e618aefd142a13a086 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Sat, 28 Mar 2020 00:49:04 +0000 Subject: [PATCH 1/2] Fix input infos are inconsistent between WMS and InputFlinger (1/2) In single focus system, we would expect the focused display could be changed when receiving a key event. The way we update window infos from WMS to InputFlinger is using SurfaceControl.Transaction, and we would expect the api 'syncInputTransactions' could wait until all window infos update to InputFlinger. This would collect all input transactions from all displays into one to make sure all input windows could be sync at same time. - Change 'updateInputWindowsImmediately' that we could update the input transation and merge it into the passing Transaction that could be collected and applied later. - Wait for 'isAnimationScheduled' to make sure the pending trasaction had been applied before it update the input transaction. - Rename 'setInputWindow' to 'displayRemoved' to match the function more properly. Bug: 150250453 Test: atest --rerun-until-failure 100 WindowFocusTests#testMovingDisplayToTopByKeyEvent Test: atest --rerun-until-failure 10 WindowFocusTests Change-Id: Id06c70bfe76e8c8a0cd50a8ae66b38b444a9d0c2 --- .../server/input/InputManagerService.java | 6 +- .../com/android/server/wm/InputMonitor.java | 21 ++++--- .../com/android/server/wm/TaskPositioner.java | 5 +- .../server/wm/WindowManagerService.java | 10 ++- ...droid_server_input_InputManagerService.cpp | 63 +++---------------- 5 files changed, 32 insertions(+), 73 deletions(-) diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 0b22586bb3731..e6129b9b1f32f 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -76,7 +76,6 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputMonitor; -import android.view.InputWindowHandle; import android.view.KeyEvent; import android.view.PointerIcon; import android.view.Surface; @@ -221,8 +220,7 @@ public class InputManagerService extends IInputManager.Stub int policyFlags); private static native VerifiedInputEvent nativeVerifyInputEvent(long ptr, InputEvent event); private static native void nativeToggleCapsLock(long ptr, int deviceId); - private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles, - int displayId); + private static native void nativeDisplayRemoved(long ptr, int displayId); private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen); private static native void nativeSetSystemUiVisibility(long ptr, int visibility); private static native void nativeSetFocusedApplication(long ptr, @@ -1536,7 +1534,7 @@ public class InputManagerService extends IInputManager.Stub /** Clean up input window handles of the given display. */ public void onDisplayRemoved(int displayId) { - nativeSetInputWindows(mPtr, null /* windowHandles */, displayId); + nativeDisplayRemoved(mPtr, displayId); } @Override diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 88cdd1781aee5..18332b9484c0f 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -62,7 +62,7 @@ final class InputMonitor { // When true, need to call updateInputWindowsLw(). private boolean mUpdateInputWindowsNeeded = true; private boolean mUpdateInputWindowsPending; - private boolean mApplyImmediately; + private boolean mUpdateInputWindowsImmediately; // Currently focused input window handle. private InputWindowHandle mFocusedInputWindowHandle; @@ -347,14 +347,20 @@ final class InputMonitor { } } - void updateInputWindowsImmediately() { + /** + * Immediately update the input transaction and merge into the passing Transaction that could be + * collected and applied later. + */ + void updateInputWindowsImmediately(SurfaceControl.Transaction t) { mHandler.removeCallbacks(mUpdateInputWindows); - mApplyImmediately = true; + mUpdateInputWindowsImmediately = true; mUpdateInputWindows.run(); - mApplyImmediately = false; + mUpdateInputWindowsImmediately = false; + t.merge(mInputTransaction); } - /* Called when the current input focus changes. + /** + * Called when the current input focus changes. * Layer assignment is assumed to be complete by the time this is called. */ public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { @@ -465,10 +471,7 @@ final class InputMonitor { if (mAddWallpaperInputConsumerHandle) { mWallpaperInputConsumer.show(mInputTransaction, 0); } - - if (mApplyImmediately) { - mInputTransaction.apply(); - } else { + if (!mUpdateInputWindowsImmediately) { mDisplayContent.getPendingTransaction().merge(mInputTransaction); mDisplayContent.scheduleAnimation(); } diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index f046e8adc4783..be0d6f8a0b9f0 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -268,8 +268,9 @@ class TaskPositioner implements IBinder.DeathRecipient { mDisplayContent.getDisplayRotation().pause(); // Notify InputMonitor to take mDragWindowHandle. - mDisplayContent.getInputMonitor().updateInputWindowsImmediately(); - new SurfaceControl.Transaction().syncInputWindows().apply(true); + final SurfaceControl.Transaction t = mService.mTransactionFactory.get(); + mDisplayContent.getInputMonitor().updateInputWindowsImmediately(t); + t.syncInputWindows().apply(); final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics(); mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b1f22f8623d63..3ecc173909c66 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7734,19 +7734,23 @@ public class WindowManagerService extends IWindowManager.Stub public void syncInputTransactions() { waitForAnimationsToComplete(); + // Collect all input transactions from all displays to make sure we could sync all input + // windows at same time. + final SurfaceControl.Transaction t = mTransactionFactory.get(); synchronized (mGlobalLock) { mWindowPlacerLocked.performSurfacePlacementIfScheduled(); mRoot.forAllDisplays(displayContent -> - displayContent.getInputMonitor().updateInputWindowsImmediately()); + displayContent.getInputMonitor().updateInputWindowsImmediately(t)); } - mTransactionFactory.get().syncInputWindows().apply(true); + t.syncInputWindows().apply(); } private void waitForAnimationsToComplete() { synchronized (mGlobalLock) { long timeoutRemaining = ANIMATION_COMPLETED_TIMEOUT_MS; - while (mRoot.isAnimating(TRANSITION | CHILDREN) && timeoutRemaining > 0) { + while ((mAnimator.isAnimationScheduled() + || mRoot.isAnimating(TRANSITION | CHILDREN)) && timeoutRemaining > 0) { long startTime = System.currentTimeMillis(); try { mGlobalLock.wait(timeoutRemaining); diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index e3f9ae8969b35..9bc5d34c11af5 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -206,7 +206,7 @@ public: status_t unregisterInputChannel(JNIEnv* env, const sp& inputChannel); status_t pilferPointers(const sp& token); - void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId); + void displayRemoved(JNIEnv* env, int32_t displayId); void setFocusedApplication(JNIEnv* env, int32_t displayId, jobject applicationHandleObj); void setFocusedDisplay(JNIEnv* env, int32_t displayId); void setInputDispatchMode(bool enabled, bool frozen); @@ -771,55 +771,10 @@ void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration } } -void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, - int32_t displayId) { - std::vector > windowHandles; - - if (windowHandleObjArray) { - jsize length = env->GetArrayLength(windowHandleObjArray); - for (jsize i = 0; i < length; i++) { - jobject windowHandleObj = env->GetObjectArrayElement(windowHandleObjArray, i); - if (! windowHandleObj) { - break; // found null element indicating end of used portion of the array - } - - sp windowHandle = - android_view_InputWindowHandle_getHandle(env, windowHandleObj); - if (windowHandle != nullptr) { - windowHandles.push_back(windowHandle); - } - env->DeleteLocalRef(windowHandleObj); - } - } - - mInputManager->getDispatcher()->setInputWindows(windowHandles, displayId); - - // Do this after the dispatcher has updated the window handle state. - bool newPointerGesturesEnabled = true; - size_t numWindows = windowHandles.size(); - for (size_t i = 0; i < numWindows; i++) { - const sp& windowHandle = windowHandles[i]; - const InputWindowInfo* windowInfo = windowHandle->getInfo(); - if (windowInfo && windowInfo->hasFocus && (windowInfo->inputFeatures - & InputWindowInfo::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES)) { - newPointerGesturesEnabled = false; - } - } - - bool pointerGesturesEnabledChanged = false; - { // acquire lock - AutoMutex _l(mLock); - - if (mLocked.pointerGesturesEnabled != newPointerGesturesEnabled) { - mLocked.pointerGesturesEnabled = newPointerGesturesEnabled; - pointerGesturesEnabledChanged = true; - } - } // release lock - - if (pointerGesturesEnabledChanged) { - mInputManager->getReader()->requestRefreshConfiguration( - InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT); - } +void NativeInputManager::displayRemoved(JNIEnv* env, int32_t displayId) { + // Set an empty list to remove all handles from the specific display. + std::vector> windowHandles; + mInputManager->getDispatcher()->setInputWindows({{displayId, windowHandles}}); } void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId, @@ -1567,11 +1522,10 @@ static void nativeToggleCapsLock(JNIEnv* env, jclass /* clazz */, im->getInputManager()->getReader()->toggleCapsLockState(deviceId); } -static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */, - jlong ptr, jobjectArray windowHandleObjArray, jint displayId) { +static void nativeDisplayRemoved(JNIEnv* env, jclass /* clazz */, jlong ptr, jint displayId) { NativeInputManager* im = reinterpret_cast(ptr); - im->setInputWindows(env, windowHandleObjArray, displayId); + im->displayRemoved(env, displayId); } static void nativeSetFocusedApplication(JNIEnv* env, jclass /* clazz */, @@ -1815,8 +1769,7 @@ static const JNINativeMethod gInputManagerMethods[] = { {"nativeVerifyInputEvent", "(JLandroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;", (void*)nativeVerifyInputEvent}, {"nativeToggleCapsLock", "(JI)V", (void*)nativeToggleCapsLock}, - {"nativeSetInputWindows", "(J[Landroid/view/InputWindowHandle;I)V", - (void*)nativeSetInputWindows}, + {"nativeDisplayRemoved", "(JI)V", (void*)nativeDisplayRemoved}, {"nativeSetFocusedApplication", "(JILandroid/view/InputApplicationHandle;)V", (void*)nativeSetFocusedApplication}, {"nativeSetFocusedDisplay", "(JI)V", (void*)nativeSetFocusedDisplay}, From c7a46da46cc7d7e639e0d7487e4af4d48cdb8dbc Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Thu, 26 Mar 2020 17:38:57 +0800 Subject: [PATCH 2/2] Prevent waiting when inject event without animation Synchronously inject input event would wait for animations complete, but if no surface animation is on going and 'scheduleAnimation' is called in order to apply the pending transaction. It would wait until timeout(=5s) because no 'notify' would be called from framecallback. That would cause timeout problem when emulate swipe gesture via inject many events. This would provide a flag in WindowAnimator to ensure the pending transaction could be applied and exit waiting if there is no other animation. Bug: 150250453 Bug: 152478735 Bug: 152462354 Test: atest libinput_tests inputflinger_tests Test: atest --rerun-until-failure 100 WindowFocusTests#testMovingDisplayToTopByKeyEvent Test: atest MemoryTests#testActivityRecreation Test: Tests have inject events. Change-Id: If1db5c4d923ddbcfcd6eb3db38a41fee70913568 --- .../java/com/android/server/wm/WindowAnimator.java | 4 ++++ .../android/server/wm/WindowManagerService.java | 14 +++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 0b11dd28e5953..92a9e30c2f0a8 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -75,6 +75,7 @@ public class WindowAnimator { * vsync-app and then schedule the animation tick at the right time (vsync-sf). */ private boolean mAnimationFrameCallbackScheduled; + boolean mNotifyWhenNoAnimation = false; /** * A list of runnable that need to be run after {@link WindowContainer#prepareSurfaces} is @@ -97,6 +98,9 @@ public class WindowAnimator { synchronized (mService.mGlobalLock) { mAnimationFrameCallbackScheduled = false; animate(frameTimeNs); + if (mNotifyWhenNoAnimation && !mLastRootAnimating) { + mService.mGlobalLock.notifyAll(); + } } }; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 3ecc173909c66..8e457522c4b0a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -193,7 +193,6 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.DisplayMetrics; -import android.util.Log; import android.util.MergedConfiguration; import android.util.Slog; import android.util.SparseArray; @@ -7746,9 +7745,16 @@ public class WindowManagerService extends IWindowManager.Stub t.syncInputWindows().apply(); } + /** + * Wait until all container animations and surface operations behalf of WindowManagerService + * complete. + */ private void waitForAnimationsToComplete() { synchronized (mGlobalLock) { long timeoutRemaining = ANIMATION_COMPLETED_TIMEOUT_MS; + // This could prevent if there is no container animation, we still have to apply the + // pending transaction and exit waiting. + mAnimator.mNotifyWhenNoAnimation = true; while ((mAnimator.isAnimationScheduled() || mRoot.isAnimating(TRANSITION | CHILDREN)) && timeoutRemaining > 0) { long startTime = System.currentTimeMillis(); @@ -7758,9 +7764,11 @@ public class WindowManagerService extends IWindowManager.Stub } timeoutRemaining -= (System.currentTimeMillis() - startTime); } + mAnimator.mNotifyWhenNoAnimation = false; - if (mRoot.isAnimating(TRANSITION | CHILDREN)) { - Log.w(TAG, "Timed out waiting for animations to complete."); + if (mAnimator.isAnimationScheduled() + || mRoot.isAnimating(TRANSITION | CHILDREN)) { + Slog.w(TAG, "Timed out waiting for animations to complete."); } } }