Loading services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +199 −113 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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); } } Loading Loading @@ -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()) { Loading Loading @@ -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)) { Loading @@ -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) { Loading Loading @@ -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); Loading Loading @@ -475,6 +503,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } } private void enableDisplayIndependentFeatures() { if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) { mAms.setMotionEventInjectors(mMotionEventInjectors); } Loading Loading @@ -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( Loading Loading @@ -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 */ Loading Loading @@ -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(); } } } services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +5 −2 Original line number Diff line number Diff line Loading @@ -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:{"); Loading Loading @@ -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) { Loading @@ -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) { Loading services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java +105 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading @@ -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 Loading Loading @@ -240,7 +279,7 @@ public class AccessibilityInputFilterTest { } @Test public void testInputEvent_shouldClearEventsForAllEventHandlers() { public void testInputEvent_shouldClearEventsForDisplayEventHandlers() { prepareLooper(); mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures); Loading @@ -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(); Loading Loading
services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +199 −113 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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); } } Loading Loading @@ -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()) { Loading Loading @@ -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)) { Loading @@ -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) { Loading Loading @@ -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); Loading Loading @@ -475,6 +503,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } } private void enableDisplayIndependentFeatures() { if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) { mAms.setMotionEventInjectors(mMotionEventInjectors); } Loading Loading @@ -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( Loading Loading @@ -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 */ Loading Loading @@ -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(); } } }
services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +5 −2 Original line number Diff line number Diff line Loading @@ -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:{"); Loading Loading @@ -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) { Loading @@ -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) { Loading
services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java +105 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading @@ -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 Loading Loading @@ -240,7 +279,7 @@ public class AccessibilityInputFilterTest { } @Test public void testInputEvent_shouldClearEventsForAllEventHandlers() { public void testInputEvent_shouldClearEventsForDisplayEventHandlers() { prepareLooper(); mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures); Loading @@ -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(); Loading