Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c06cc308 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add detailed reasons for dropping events in ViewRootImpl"

parents a3b6beaf d7965fd1
Loading
Loading
Loading
Loading
+29 −16
Original line number Diff line number Diff line
@@ -5511,27 +5511,40 @@ 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.
                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;
            }

            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);
                Slog.w(mTag, "Cancelling event (" + reason + "):" + q.mEvent);
                return false;
            }

            // Drop non-terminal input events.
                Slog.w(mTag, "Dropping event due to no window focus: " + q.mEvent);
            Slog.w(mTag, "Dropping event (" + reason + "):" + q.mEvent);
            return true;
        }
            return false;
        }

        void dump(String prefix, PrintWriter writer) {
            if (mNext != null) {
+89 −2
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE;
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;

@@ -36,12 +37,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;
@@ -59,13 +62,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
@@ -189,4 +194,86 @@ public class ViewRootImplTest {
        // Behavior must not be adjusted due to setting new LayoutParams.
        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);
    }
}