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

Commit 5d76b37e authored by Jacky Kao's avatar Jacky Kao Committed by Android (Google) Code Review
Browse files

Merge "Reset the features in A11yInputFilter per display"

parents db21f77c e6014fc0
Loading
Loading
Loading
Loading
+199 −113
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY

import android.accessibilityservice.AccessibilityTrace;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Region;
import android.os.PowerManager;
@@ -44,7 +45,10 @@ import com.android.server.accessibility.magnification.WindowMagnificationGesture
import com.android.server.accessibility.magnification.WindowMagnificationPromptController;
import com.android.server.policy.WindowManagerPolicy;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.StringJoiner;

/**
 * This class is an input filter for implementing accessibility features such
@@ -172,9 +176,9 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo

    private int mEnabledFeatures;

    private EventStreamState mMouseStreamState;
    private final SparseArray<EventStreamState> mMouseStreamStates = new SparseArray<>(0);

    private EventStreamState mTouchScreenStreamState;
    private final SparseArray<EventStreamState> mTouchScreenStreamStates = new SparseArray<>(0);

    private EventStreamState mKeyboardStreamState;

@@ -212,10 +216,17 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
        super.onUninstalled();
    }

    void onDisplayChanged() {
    void onDisplayAdded(@NonNull Display display) {
        if (mInstalled) {
            disableFeatures();
            enableFeatures();
            resetStreamStateForDisplay(display.getDisplayId());
            enableFeaturesForDisplay(display);
        }
    }

    void onDisplayRemoved(int displayId) {
        if (mInstalled) {
            disableFeaturesForDisplay(displayId);
            resetStreamStateForDisplay(displayId);
        }
    }

@@ -243,16 +254,17 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
            return;
        }

        int eventSource = event.getSource();
        final int eventSource = event.getSource();
        final int displayId = event.getDisplayId();
        if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
            state.reset();
            clearEventsForAllEventHandlers(eventSource);
            clearEventStreamHandler(displayId, eventSource);
            super.onInputEvent(event, policyFlags);
            return;
        }

        if (state.updateInputSource(event.getSource())) {
            clearEventsForAllEventHandlers(eventSource);
            clearEventStreamHandler(displayId, eventSource);
        }

        if (!state.inputSourceValid()) {
@@ -281,17 +293,23 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
     */
    private EventStreamState getEventStreamState(InputEvent event) {
        if (event instanceof MotionEvent) {
            final int displayId = event.getDisplayId();

            if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
              if (mTouchScreenStreamState == null) {
                  mTouchScreenStreamState = new TouchScreenEventStreamState();
                EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId);
                if (touchScreenStreamState == null) {
                    touchScreenStreamState = new TouchScreenEventStreamState();
                    mTouchScreenStreamStates.put(displayId, touchScreenStreamState);
                }
              return mTouchScreenStreamState;
                return touchScreenStreamState;
            }
            if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
              if (mMouseStreamState == null) {
                  mMouseStreamState = new MouseEventStreamState();
                EventStreamState mouseStreamState = mMouseStreamStates.get(displayId);
                if (mouseStreamState == null) {
                    mouseStreamState = new MouseEventStreamState();
                    mMouseStreamStates.put(displayId, mouseStreamState);
                }
              return mMouseStreamState;
                return mouseStreamState;
            }
        } else if (event instanceof KeyEvent) {
            if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
@@ -304,14 +322,12 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
        return null;
    }

    private void clearEventsForAllEventHandlers(int eventSource) {
        for (int i = 0; i < mEventHandler.size(); i++) {
            final EventStreamTransformation eventHandler = mEventHandler.valueAt(i);
    private void clearEventStreamHandler(int displayId, int eventSource) {
        final EventStreamTransformation eventHandler = mEventHandler.get(displayId);
        if (eventHandler != null) {
            eventHandler.clearEvents(eventSource);
        }
    }
    }

    private void processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags) {
        if (!state.shouldProcessScroll() && event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
@@ -425,19 +441,31 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
    private void enableFeatures() {
        if (DEBUG) Slog.i(TAG, "enableFeatures()");

        resetStreamState();
        resetAllStreamState();

        final ArrayList<Display> displaysList = mAms.getValidDisplayList();

        for (int i = displaysList.size() - 1; i >= 0; i--) {
            enableFeaturesForDisplay(displaysList.get(i));
        }
        enableDisplayIndependentFeatures();
    }

    private void enableFeaturesForDisplay(Display display) {
        if (DEBUG) {
            Slog.i(TAG, "enableFeaturesForDisplay() : display Id = " + display.getDisplayId());
        }

        final Context displayContext = mContext.createDisplayContext(display);
        final int displayId = display.getDisplayId();

        if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
            if (mAutoclickController == null) {
                mAutoclickController = new AutoclickController(
                        mContext, mUserId, mAms.getTraceManager());
            addFirstEventHandlerForAllDisplays(displaysList, mAutoclickController);
            }

        for (int i = displaysList.size() - 1; i >= 0; i--) {
            final int displayId = displaysList.get(i).getDisplayId();
            final Context displayContext = mContext.createDisplayContext(displaysList.get(i));
            addFirstEventHandler(displayId, mAutoclickController);
        }

        if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
            TouchExplorer explorer = new TouchExplorer(displayContext, mAms);
@@ -475,6 +503,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
        }
    }

    private void enableDisplayIndependentFeatures() {
        if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
            mAms.setMotionEventInjectors(mMotionEventInjectors);
        }
@@ -507,55 +536,57 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
        mEventHandler.put(displayId, eventHandler);
    }

    /**
     * Adds an event handler to the event handler chain for all displays. The handler is added at
     * the beginning of the chain.
     *
     * @param displayList The list of displays
     * @param handler The handler to be added to the event handlers list.
     */
    private void addFirstEventHandlerForAllDisplays(ArrayList<Display> displayList,
            EventStreamTransformation handler) {
        for (int i = 0; i < displayList.size(); i++) {
            final int displayId = displayList.get(i).getDisplayId();
            addFirstEventHandler(displayId, handler);
    private void disableFeatures() {
        final ArrayList<Display> displaysList = mAms.getValidDisplayList();

        for (int i = displaysList.size() - 1; i >= 0; i--) {
            disableFeaturesForDisplay(displaysList.get(i).getDisplayId());
        }
        mAms.setMotionEventInjectors(null);
        disableDisplayIndependentFeatures();

        resetAllStreamState();
    }

    private void disableFeaturesForDisplay(int displayId) {
        if (DEBUG) {
            Slog.i(TAG, "disableFeaturesForDisplay() : display Id = " + displayId);
        }

    private void disableFeatures() {
        for (int i = mMotionEventInjectors.size() - 1; i >= 0; i--) {
            final MotionEventInjector injector = mMotionEventInjectors.valueAt(i);
        final MotionEventInjector injector = mMotionEventInjectors.get(displayId);
        if (injector != null) {
            injector.onDestroy();
            mMotionEventInjectors.remove(displayId);
        }
        }
        mAms.setMotionEventInjectors(null);
        mMotionEventInjectors.clear();
        if (mAutoclickController != null) {
            mAutoclickController.onDestroy();
            mAutoclickController = null;
        }
        for (int i = mTouchExplorer.size() - 1; i >= 0; i--) {
            final TouchExplorer explorer = mTouchExplorer.valueAt(i);

        final TouchExplorer explorer = mTouchExplorer.get(displayId);
        if (explorer != null) {
            explorer.onDestroy();
            mTouchExplorer.remove(displayId);
        }
        }
        mTouchExplorer.clear();
        for (int i = mMagnificationGestureHandler.size() - 1; i >= 0; i--) {
            final MagnificationGestureHandler handler = mMagnificationGestureHandler.valueAt(i);

        final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId);
        if (handler != null) {
            handler.onDestroy();
            mMagnificationGestureHandler.remove(displayId);
        }

        final EventStreamTransformation eventStreamTransformation = mEventHandler.get(displayId);
        if (eventStreamTransformation != null) {
            mEventHandler.remove(displayId);
        }
    }

    private void disableDisplayIndependentFeatures() {
        if (mAutoclickController != null) {
            mAutoclickController.onDestroy();
            mAutoclickController = null;
        }
        mMagnificationGestureHandler.clear();

        if (mKeyboardInterceptor != null) {
            mKeyboardInterceptor.onDestroy();
            mKeyboardInterceptor = null;
        }

        mEventHandler.clear();
        resetStreamState();
    }

    private MagnificationGestureHandler createMagnificationGestureHandler(
@@ -584,18 +615,32 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
        return magnificationGestureHandler;
    }

    void resetStreamState() {
        if (mTouchScreenStreamState != null) {
            mTouchScreenStreamState.reset();
        }
        if (mMouseStreamState != null) {
            mMouseStreamState.reset();
    void resetAllStreamState() {
        final ArrayList<Display> displaysList = mAms.getValidDisplayList();

        for (int i = displaysList.size() - 1; i >= 0; i--) {
            resetStreamStateForDisplay(displaysList.get(i).getDisplayId());
        }

        if (mKeyboardStreamState != null) {
            mKeyboardStreamState.reset();
        }
    }

    void resetStreamStateForDisplay(int displayId) {
        final EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId);
        if (touchScreenStreamState != null) {
            touchScreenStreamState.reset();
            mTouchScreenStreamStates.remove(displayId);
        }

        final EventStreamState mouseStreamState = mMouseStreamStates.get(displayId);
        if (mouseStreamState != null) {
            mouseStreamState.reset();
            mMouseStreamStates.remove(displayId);
        }
    }

    @Override
    public void onDestroy() {
        /* ignore */
@@ -846,4 +891,45 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
            mTouchExplorer.get(displayId).setTouchExplorationPassthroughRegion(region);
        }
    }

    /**
     * Dumps all {@link AccessibilityInputFilter}s here.
     */
    public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
        if (mEventHandler == null) {
            return;
        }
        pw.append("A11yInputFilter Info : ");
        pw.println();

        final ArrayList<Display> displaysList = mAms.getValidDisplayList();
        for (int i = 0; i < displaysList.size(); i++) {
            final int displayId = displaysList.get(i).getDisplayId();
            EventStreamTransformation next = mEventHandler.get(displayId);
            if (next != null) {
                pw.append("Enabled features of Display [");
                pw.append(Integer.toString(displayId));
                pw.append("] = ");

                final StringJoiner joiner = new StringJoiner(",", "[", "]");

                while (next != null) {
                    if (next instanceof MagnificationGestureHandler) {
                        joiner.add("MagnificationGesture");
                    } else if (next instanceof KeyboardInterceptor) {
                        joiner.add("KeyboardInterceptor");
                    } else if (next instanceof TouchExplorer) {
                        joiner.add("TouchExplorer");
                    } else if (next instanceof AutoclickController) {
                        joiner.add("AutoclickController");
                    } else if (next instanceof MotionEventInjector) {
                        joiner.add("MotionEventInjector");
                    }
                    next = next.getNext();
                }
                pw.append(joiner.toString());
            }
            pw.println();
        }
    }
}
+5 −2
Original line number Diff line number Diff line
@@ -3340,6 +3340,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
                pw.println();
            }
            mA11yWindowManager.dump(fd, pw, args);
            if (mHasInputFilter && mInputFilter != null) {
                mInputFilter.dump(fd, pw, args);
            }
            pw.println("Global client list info:{");
            mGlobalClients.dump(pw, "    Client list ");
            pw.println("    Registered clients:{");
@@ -3587,7 +3590,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
            synchronized (mLock) {
                mDisplaysList.add(display);
                if (mInputFilter != null) {
                    mInputFilter.onDisplayChanged();
                    mInputFilter.onDisplayAdded(display);
                }
                AccessibilityUserState userState = getCurrentUserStateLocked();
                if (displayId != Display.DEFAULT_DISPLAY) {
@@ -3609,7 +3612,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
                    return;
                }
                if (mInputFilter != null) {
                    mInputFilter.onDisplayChanged();
                    mInputFilter.onDisplayRemoved(displayId);
                }
                AccessibilityUserState userState = getCurrentUserStateLocked();
                if (displayId != Display.DEFAULT_DISPLAY) {
+105 −8
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEA

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -176,7 +177,7 @@ public class AccessibilityInputFilterTest {
    }

    @Test
    public void testEventHandler_shouldChangeAfterOnDisplayChanged() {
    public void testEventHandler_shouldIncreaseAndHaveCorrectOrderAfterOnDisplayAdded() {
        prepareLooper();

        // Check if there is only one mEventHandler when there is one default display.
@@ -184,13 +185,51 @@ public class AccessibilityInputFilterTest {
        assertEquals(1, mEventHandler.size());

        // Check if it has correct numbers of mEventHandler for corresponding displays.
        setDisplayCount(4);
        mA11yInputFilter.onDisplayChanged();
        assertEquals(4, mEventHandler.size());
        setDisplayCount(2);
        mA11yInputFilter.onDisplayAdded(mDisplayList.get(SECOND_DISPLAY));
        assertEquals(2, mEventHandler.size());

        EventStreamTransformation next = mEventHandler.get(SECOND_DISPLAY);
        assertNotNull(next);

        // Start from index 1 because KeyboardInterceptor only exists in EventHandler for
        // DEFAULT_DISPLAY.
        for (int i = 1; next != null; i++) {
            assertEquals(next.getClass(), mExpectedEventHandlerTypes[i]);
            next = next.getNext();
        }
    }

    @Test
    public void testEventHandler_shouldDecreaseAfterOnDisplayRemoved() {
        prepareLooper();

        setDisplayCount(2);
        mA11yInputFilter.onDisplayChanged();
        mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
        assertEquals(2, mEventHandler.size());

        // Check if it has correct numbers of mEventHandler for corresponding displays.
        mA11yInputFilter.onDisplayRemoved(SECOND_DISPLAY);
        assertEquals(1, mEventHandler.size());

        EventStreamTransformation eventHandler = mEventHandler.get(SECOND_DISPLAY);
        assertNull(eventHandler);
    }

    @Test
    public void testEventHandler_shouldNoChangedInOtherDisplayAfterOnDisplayRemoved() {
        prepareLooper();

        setDisplayCount(2);
        mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
        EventStreamTransformation eventHandlerBeforeDisplayRemoved =
                mEventHandler.get(DEFAULT_DISPLAY);

        mA11yInputFilter.onDisplayRemoved(SECOND_DISPLAY);
        EventStreamTransformation eventHandlerAfterDisplayRemoved =
                mEventHandler.get(DEFAULT_DISPLAY);

        assertEquals(eventHandlerBeforeDisplayRemoved, eventHandlerAfterDisplayRemoved);
    }

    @Test
@@ -240,7 +279,7 @@ public class AccessibilityInputFilterTest {
    }

    @Test
    public void testInputEvent_shouldClearEventsForAllEventHandlers() {
    public void testInputEvent_shouldClearEventsForDisplayEventHandlers() {
        prepareLooper();

        mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
@@ -253,12 +292,70 @@ public class AccessibilityInputFilterTest {
        send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
        assertEquals(2, mCaptor1.mEvents.size());

        // InputEvent with different input source should trigger clearEvents() for each
        // EventStreamTransformation in EventHandler.
        // InputEvent with different input source to the same display should trigger
        // clearEvents() for the EventHandler in this display.
        send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_MOUSE));
        assertEquals(1, mCaptor1.mEvents.size());
    }

    @Test
    public void testInputEvent_shouldNotClearEventsForOtherDisplayEventHandlers() {
        prepareLooper();

        setDisplayCount(2);
        mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
        assertEquals(2, mEventHandler.size());

        mCaptor1 = new EventCaptor();
        mCaptor2 = new EventCaptor();
        mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);
        mEventHandler.put(SECOND_DISPLAY, mCaptor2);

        // InputEvent with different displayId should be dispatched to corresponding EventHandler.
        send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
        send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
        send(downEvent(SECOND_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));

        // InputEvent with different input source should not trigger clearEvents() for
        // the EventHandler in the other display.
        send(downEvent(SECOND_DISPLAY, InputDevice.SOURCE_MOUSE));
        assertEquals(2, mCaptor1.mEvents.size());
    }

    @Test
    public void testInputEvent_shouldNotClearEventsForOtherDisplayAfterOnDisplayAdded() {
        prepareLooper();

        mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
        mCaptor1 = new EventCaptor();
        mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);

        send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
        send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
        assertEquals(2, mCaptor1.mEvents.size());

        setDisplayCount(2);
        mA11yInputFilter.onDisplayAdded(mDisplayList.get(SECOND_DISPLAY));
        assertEquals(2, mCaptor1.mEvents.size());
    }

    @Test
    public void testInputEvent_shouldNotClearEventsForOtherDisplayAfterOnDisplayRemoved() {
        prepareLooper();

        setDisplayCount(2);
        mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
        mCaptor1 = new EventCaptor();
        mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);

        send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
        send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
        assertEquals(2, mCaptor1.mEvents.size());

        mA11yInputFilter.onDisplayRemoved(SECOND_DISPLAY);
        assertEquals(2, mCaptor1.mEvents.size());
    }

    @Test
    public void testEnabledFeatures_windowMagnificationMode_expectedMagnificationGestureHandler() {
        prepareLooper();