summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/ViewRootImpl.java45
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java91
2 files changed, 118 insertions, 18 deletions
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0fc2d7a75ee1..b1eedd8e4eec 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5535,26 +5535,39 @@ public final class ViewRootImpl implements ViewParent,
if (mView == null || !mAdded) {
Slog.w(mTag, "Dropping event due to root view being removed: " + q.mEvent);
return true;
- } else if ((!mAttachInfo.mHasWindowFocus
+ }
+
+ // Find a reason for dropping or canceling the event.
+ final String reason;
+ if (!mAttachInfo.mHasWindowFocus
&& !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
- && !isAutofillUiShowing()) || mStopped
- || (mIsAmbientMode && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_BUTTON))
- || (mPausedForTransition && !isBack(q.mEvent))) {
- // This is a focus event and the window doesn't currently have input focus or
- // has stopped. This could be an event that came back from the previous stage
+ && !isAutofillUiShowing()) {
+ // This is a non-pointer event and the window doesn't currently have input focus
+ // This could be an event that came back from the previous stage
// but the window has lost focus or stopped in the meantime.
- if (isTerminalInputEvent(q.mEvent)) {
- // Don't drop terminal input events, however mark them as canceled.
- q.mEvent.cancel();
- Slog.w(mTag, "Cancelling event due to no window focus: " + q.mEvent);
- return false;
- }
+ reason = "no window focus";
+ } else if (mStopped) {
+ reason = "window is stopped";
+ } else if (mIsAmbientMode
+ && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_BUTTON)) {
+ reason = "non-button event in ambient mode";
+ } else if (mPausedForTransition && !isBack(q.mEvent)) {
+ reason = "paused for transition";
+ } else {
+ // Most common path: no reason to drop or cancel the event
+ return false;
+ }
- // Drop non-terminal input events.
- Slog.w(mTag, "Dropping event due to no window focus: " + q.mEvent);
- return true;
+ if (isTerminalInputEvent(q.mEvent)) {
+ // Don't drop terminal input events, however mark them as canceled.
+ q.mEvent.cancel();
+ Slog.w(mTag, "Cancelling event (" + reason + "):" + q.mEvent);
+ return false;
}
- return false;
+
+ // Drop non-terminal input events.
+ Slog.w(mTag, "Dropping event (" + reason + "):" + q.mEvent);
+ return true;
}
void dump(String prefix, PrintWriter writer) {
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 4cf6715ba0ca..8fa212a59984 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -26,6 +26,7 @@ import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
@@ -35,12 +36,14 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.content.Context;
+import android.os.Binder;
import android.platform.test.annotations.Presubmit;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Before;
import org.junit.Test;
@@ -58,13 +61,15 @@ import org.junit.runner.RunWith;
public class ViewRootImplTest {
private ViewRootImpl mViewRootImpl;
+ private Context mContext;
+ private volatile boolean mKeyReceived = false;
@Before
public void setUp() throws Exception {
- final Context context = getInstrumentation().getTargetContext();
+ mContext = getInstrumentation().getTargetContext();
getInstrumentation().runOnMainSync(() ->
- mViewRootImpl = new ViewRootImpl(context, context.getDisplayNoVerify()));
+ mViewRootImpl = new ViewRootImpl(mContext, mContext.getDisplayNoVerify()));
}
@Test
@@ -178,4 +183,86 @@ public class ViewRootImplTest {
// setSystemBarsBehavior.
assertEquals(behavior, controller.getSystemBarsBehavior());
}
+
+ /**
+ * When window doesn't have focus, keys should be dropped.
+ */
+ @Test
+ public void whenWindowDoesNotHaveFocus_keysAreDropped() {
+ checkKeyEvent(() -> {
+ mViewRootImpl.windowFocusChanged(false /*hasFocus*/, true /*inTouchMode*/);
+ }, false /*shouldReceiveKey*/);
+ }
+
+ /**
+ * When window has focus, keys should be received
+ */
+ @Test
+ public void whenWindowHasFocus_keysAreReceived() {
+ checkKeyEvent(() -> {
+ mViewRootImpl.windowFocusChanged(true /*hasFocus*/, true /*inTouchMode*/);
+ }, true /*shouldReceiveKey*/);
+ }
+
+ /**
+ * When window is in ambient mode, keys should be dropped
+ */
+ @Test
+ public void whenWindowIsInAmbientMode_keysAreDropped() {
+ checkKeyEvent(() -> {
+ mViewRootImpl.setIsAmbientMode(true /*ambient*/);
+ }, false /*shouldReceiveKey*/);
+ }
+
+ /**
+ * When window is paused for transition, keys should be dropped
+ */
+ @Test
+ public void whenWindowIsPausedForTransition_keysAreDropped() {
+ checkKeyEvent(() -> {
+ mViewRootImpl.setPausedForTransition(true /*paused*/);
+ }, false /*shouldReceiveKey*/);
+ }
+
+ class KeyView extends View {
+ KeyView(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean dispatchKeyEventPreIme(KeyEvent event) {
+ mKeyReceived = true;
+ return true /*handled*/;
+ }
+ }
+
+ /**
+ * Create a new view, and add it to window manager.
+ * Run the precondition 'setup'.
+ * Next, inject an event into this view, and check whether it is received.
+ */
+ private void checkKeyEvent(Runnable setup, boolean shouldReceiveKey) {
+ final KeyView view = new KeyView(mContext);
+
+ WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+ wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ WindowManager wm = mContext.getSystemService(WindowManager.class);
+ wm.addView(view, wmlp);
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ mViewRootImpl = view.getViewRootImpl();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(setup);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ // Inject a key event, and wait for it to be processed
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A);
+ mViewRootImpl.dispatchInputEvent(event);
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ assertEquals(mKeyReceived, shouldReceiveKey);
+ }
}