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

Commit d7965fd1 authored by Siarhei Vishniakou's avatar Siarhei Vishniakou
Browse files

Add detailed reasons for dropping events in ViewRootImpl

Currently, ViewRootImpl drops events for several reasons. However, just
one reason is written to the log: "dropping event because the window
does not have focus".

But even if the window has focus, the event can be dropped.

Improve the logging by refactoring the "if" check inside ViewRootImpl

Test: atest ViewRootImplTest
Bug: 173675649
Change-Id: I2ba8270ba09dea63e6497bc830af4c704ada52ef
parent 250c00cd
Loading
Loading
Loading
Loading
+29 −16
Original line number Diff line number Diff line
@@ -5535,27 +5535,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
@@ -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);
    }
}