diff options
| author | 2025-01-29 14:01:54 -0800 | |
|---|---|---|
| committer | 2025-01-29 14:01:54 -0800 | |
| commit | 7143d096a8bedff00a89cc0f7ce80d180d957e89 (patch) | |
| tree | cb2079c2755d3c43389e670b9c1c9209ea2a08b9 | |
| parent | 48a6892d7b618875caa0d8e874b69691b5667ddb (diff) | |
| parent | 2945b46dd7e8346fa6228067683ac57a2a338e86 (diff) | |
Merge "Stop non-tool A11yServices from injecting gestures onto sensitive views." into main
9 files changed, 250 insertions, 105 deletions
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index bd6ff4c2af02..ae0e9c623571 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -532,14 +532,30 @@ public final class MotionEvent extends InputEvent implements Parcelable { public static final int FLAG_NO_FOCUS_CHANGE = MotionEventFlag.NO_FOCUS_CHANGE; /** - * This flag indicates that this event was modified by or generated from an accessibility - * service. Value = 0x800 + * This flag indicates that this event was injected from some + * {@link android.accessibilityservice.AccessibilityService}, which may be either an + * Accessibility Tool OR a service using that API for purposes other than assisting users with + * disabilities. Value = 0x800 + * @see #FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL * @hide */ @TestApi public static final int FLAG_IS_ACCESSIBILITY_EVENT = MotionEventFlag.IS_ACCESSIBILITY_EVENT; /** + * This flag indicates that this event was injected from an + * {@link android.accessibilityservice.AccessibilityService} with the + * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool()} property + * set to true. These services (known as "Accessibility Tools") are used to assist users with + * disabilities, so events from these services should be able to reach all Views including + * Views which set {@link View#isAccessibilityDataSensitive()} to true. + * Value = 0x1000 + * @hide + */ + public static final int FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL = + MotionEventFlag.INJECTED_FROM_ACCESSIBILITY_TOOL; + + /** * Private flag that indicates when the system has detected that this motion event * may be inconsistent with respect to the sequence of previously delivered motion events, * such as when a pointer move event is sent but the pointer is not down. @@ -2534,6 +2550,24 @@ public final class MotionEvent extends InputEvent implements Parcelable { : flags & ~FLAG_TARGET_ACCESSIBILITY_FOCUS); } + /** + * @see #FLAG_IS_ACCESSIBILITY_EVENT + * @hide + */ + public boolean isInjectedFromAccessibilityService() { + final int flags = getFlags(); + return (flags & FLAG_IS_ACCESSIBILITY_EVENT) != 0; + } + + /** + * @see #FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL + * @hide + */ + public boolean isInjectedFromAccessibilityTool() { + final int flags = getFlags(); + return (flags & FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL) != 0; + } + /** @hide */ public final boolean isHoverExitPending() { final int flags = getFlags(); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 7206906658ff..0866e0d832b1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -16654,6 +16654,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Window is obscured, drop this touch. return false; } + if (android.view.accessibility.Flags.preventA11yNontoolFromInjectingIntoSensitiveViews()) { + if (event.isInjectedFromAccessibilityService() + // If the event came from an Accessibility Service that does *not* declare + // itself as AccessibilityServiceInfo#isAccessibilityTool and this View is + // declared sensitive then drop the event. + // Only Accessibility Tools are allowed to interact with sensitive Views. + && !event.isInjectedFromAccessibilityTool() && isAccessibilityDataSensitive()) { + return false; + } + } return true; } diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index 6d2c0d0061dd..bb8958bc9c70 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -17,6 +17,7 @@ package android.view; import static android.os.IInputConstants.POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY; +import static android.os.IInputConstants.POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL; import static android.os.IInputConstants.POLICY_FLAG_KEY_GESTURE_TRIGGERED; import android.annotation.IntDef; @@ -37,6 +38,7 @@ public interface WindowManagerPolicyConstants { int FLAG_VIRTUAL = 0x00000002; int FLAG_INJECTED_FROM_ACCESSIBILITY = POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY; + int FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL = POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL; int FLAG_KEY_GESTURE_TRIGGERED = POLICY_FLAG_KEY_GESTURE_TRIGGERED; int FLAG_INJECTED = 0x01000000; int FLAG_TRUSTED = 0x02000000; diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig index 294e5da1edd1..37f393ec6511 100644 --- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig +++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig @@ -4,6 +4,14 @@ container: "system" # NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors. flag { + name: "a11y_character_in_window_api" + namespace: "accessibility" + description: "Enables new extra data key for an AccessibilityService to request character bounds in unmagnified window coordinates." + bug: "375429616" + is_exported: true +} + +flag { name: "a11y_expansion_state_api" namespace: "accessibility" description: "Enables new APIs for an app to convey if a node is expanded or collapsed." @@ -42,23 +50,15 @@ flag { } flag { - name: "a11y_character_in_window_api" - namespace: "accessibility" - description: "Enables new extra data key for an AccessibilityService to request character bounds in unmagnified window coordinates." - bug: "375429616" - is_exported: true -} - -flag { - namespace: "accessibility" name: "allow_shortcut_chooser_on_lockscreen" + namespace: "accessibility" description: "Allows the a11y shortcut disambig dialog to appear on the lockscreen" bug: "303871725" } flag { - namespace: "accessibility" name: "braille_display_hid" + namespace: "accessibility" is_exported: true description: "Enables new APIs for an AccessibilityService to communicate with a HID Braille display" bug: "303522222" @@ -72,47 +72,62 @@ flag { } flag { - namespace: "accessibility" name: "collection_info_item_counts" + namespace: "accessibility" is_exported: true description: "Fields for total items and the number of important for accessibility items in a collection" bug: "302376158" } flag { - namespace: "accessibility" name: "copy_events_for_gesture_detection" + namespace: "accessibility" description: "Creates copies of MotionEvents and GestureEvents in GestureMatcher" bug: "280130713" } flag { - namespace: "accessibility" name: "deprecate_accessibility_announcement_apis" + namespace: "accessibility" description: "Controls the deprecation of platform APIs related to disruptive accessibility announcements" bug: "376727542" is_exported: true } flag { - namespace: "accessibility" name: "deprecate_ani_label_for_apis" + namespace: "accessibility" description: "Controls the deprecation of AccessibilityNodeInfo labelFor apis" bug: "333783827" is_exported: true } flag { + name: "enable_system_pinch_zoom_gesture" namespace: "accessibility" + description: "Feature flag for system pinch zoom gesture detector and related opt-out apis" + bug: "283323770" +} + +flag { + name: "enable_type_window_control" + namespace: "accessibility" + is_exported: true + description: "adds new TYPE_WINDOW_CONTROL to AccessibilityWindowInfo for detecting Window Decorations" + bug: "320445550" +} + +flag { name: "flash_notification_system_api" + namespace: "accessibility" is_exported: true description: "Makes flash notification APIs as system APIs for calling from mainline module" bug: "303131332" } flag { - namespace: "accessibility" name: "focus_rect_min_size" + namespace: "accessibility" description: "Ensures the a11y focus rect is big enough to be drawn as visible" bug: "368667566" metadata { @@ -121,102 +136,101 @@ flag { } flag { - namespace: "accessibility" name: "force_invert_color" + namespace: "accessibility" description: "Enable force force-dark for smart inversion and dark theme everywhere" bug: "282821643" } flag { - name: "migrate_enable_shortcuts" + name: "global_action_media_play_pause" namespace: "accessibility" - description: "Refactors deprecated code to use AccessibilityManager#enableShortcutsForTargets." - bug: "332006721" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { - name: "motion_event_observing" + description: "Allow AccessibilityService to perform GLOBAL_ACTION_MEDIA_PLAY_PAUSE" + bug: "334954140" is_exported: true - namespace: "accessibility" - description: "Allows accessibility services to intercept but not consume motion events from specified sources." - bug: "297595990" } flag { - namespace: "accessibility" name: "global_action_menu" + namespace: "accessibility" description: "Allow AccessibilityService to perform GLOBAL_ACTION_MENU" bug: "334954140" is_exported: true } flag { + name: "granular_scrolling" namespace: "accessibility" - name: "global_action_media_play_pause" - description: "Allow AccessibilityService to perform GLOBAL_ACTION_MEDIA_PLAY_PAUSE" - bug: "334954140" is_exported: true + description: "Allow the use of granular scrolling. This allows scrollable nodes to scroll by increments other than a full screen" + bug: "302376158" } flag { + name: "indeterminate_range_info" namespace: "accessibility" - name: "granular_scrolling" + description: "Creates a way to create an INDETERMINATE RangeInfo" + bug: "376108874" is_exported: true - description: "Allow the use of granular scrolling. This allows scrollable nodes to scroll by increments other than a full screen" - bug: "302376158" } flag { + name: "migrate_enable_shortcuts" namespace: "accessibility" - name: "reduce_window_content_changed_event_throttle" - description: "Reduces the throttle of AccessibilityEvent of TYPE_WINDOW_CONTENT_CHANGED" - bug: "277305460" + description: "Refactors deprecated code to use AccessibilityManager#enableShortcutsForTargets." + bug: "332006721" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { + name: "motion_event_observing" + is_exported: true namespace: "accessibility" - name: "remove_child_hover_check_for_touch_exploration" - description: "Remove a check for a hovered child that prevents touch events from being delegated to non-direct descendants" - bug: "304770837" + description: "Allows accessibility services to intercept but not consume motion events from specified sources." + bug: "297595990" } flag { - name: "skip_accessibility_warning_dialog_for_trusted_services" + name: "prevent_a11y_nontool_from_injecting_into_sensitive_views" namespace: "accessibility" - description: "Skips showing the accessibility warning dialog for trusted services." - bug: "303511250" + description: "Prevents injected gestures from A11yServices without isAccessibilityTool=true from reaching AccessibilityDataSensitive UI elements" + bug: "284180538" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { + name: "prevent_leaking_viewrootimpl" namespace: "accessibility" - name: "enable_type_window_control" - is_exported: true - description: "adds new TYPE_WINDOW_CONTROL to AccessibilityWindowInfo for detecting Window Decorations" - bug: "320445550" + description: "Clear pending messages and callbacks of the handler in AccessibilityInteractionController when the ViewRootImpl is detached from Window to prevent leaking ViewRootImpl" + bug: "320701910" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { + name: "reduce_window_content_changed_event_throttle" namespace: "accessibility" - name: "update_always_on_a11y_service" - description: "Updates the Always-On A11yService state when the user changes the enablement of the shortcut." - bug: "298869916" + description: "Reduces the throttle of AccessibilityEvent of TYPE_WINDOW_CONTENT_CHANGED" + bug: "277305460" } flag { - name: "enable_system_pinch_zoom_gesture" + name: "remove_child_hover_check_for_touch_exploration" namespace: "accessibility" - description: "Feature flag for system pinch zoom gesture detector and related opt-out apis" - bug: "283323770" + description: "Remove a check for a hovered child that prevents touch events from being delegated to non-direct descendants" + bug: "304770837" } flag { - name: "prevent_leaking_viewrootimpl" + name: "restore_a11y_secure_settings_on_hsum_device" namespace: "accessibility" - description: "Clear pending messages and callbacks of the handler in AccessibilityInteractionController when the ViewRootImpl is detached from Window to prevent leaking ViewRootImpl" - bug: "320701910" + description: "Grab the a11y settings and send the settings restored broadcast for current visible foreground user" + bug: "381294327" metadata { purpose: PURPOSE_BUGFIX } @@ -233,13 +247,10 @@ flag { } flag { - name: "restore_a11y_secure_settings_on_hsum_device" + name: "skip_accessibility_warning_dialog_for_trusted_services" namespace: "accessibility" - description: "Grab the a11y settings and send the settings restored broadcast for current visible foreground user" - bug: "381294327" - metadata { - purpose: PURPOSE_BUGFIX - } + description: "Skips showing the accessibility warning dialog for trusted services." + bug: "303511250" } flag { @@ -274,6 +285,13 @@ flag { } flag { + namespace: "accessibility" + name: "update_always_on_a11y_service" + description: "Updates the Always-On A11yService state when the user changes the enablement of the shortcut." + bug: "298869916" +} + +flag { name: "warning_use_default_dialog_type" namespace: "accessibility" description: "Uses the default type for the A11yService warning dialog, instead of SYSTEM_ALERT_DIALOG" @@ -282,11 +300,3 @@ flag { purpose: PURPOSE_BUGFIX } } - -flag { - name: "indeterminate_range_info" - namespace: "accessibility" - description: "Creates a way to create an INDETERMINATE RangeInfo" - bug: "376108874" - is_exported: true -} diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index fda57d6bb986..e422fef6c22c 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -31,6 +31,7 @@ import android.os.Looper; import android.os.PowerManager; import android.os.SystemClock; import android.provider.Settings; +import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -71,9 +72,9 @@ import java.util.StringJoiner; */ class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation { - private static final String TAG = AccessibilityInputFilter.class.getSimpleName(); + private static final String TAG = "A11yInputFilter"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); /** * Flag for enabling the screen magnification feature. diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index a3fe9ec5ea22..6cba3633b940 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -554,7 +554,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect if (motionEventInjector != null && mWindowManagerService.isTouchOrFaketouchDevice()) { motionEventInjector.injectEvents( - gestureSteps.getList(), mClient, sequence, displayId); + gestureSteps.getList(), mClient, sequence, displayId, + mAccessibilityServiceInfo.isAccessibilityTool()); } else { try { if (svcClientTracingEnabled()) { diff --git a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java index 5cbd1a208ce1..b2169535d0de 100644 --- a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java +++ b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java @@ -105,12 +105,14 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement * either complete or cancelled. */ public void injectEvents(List<GestureStep> gestureSteps, - IAccessibilityServiceClient serviceInterface, int sequence, int displayId) { + IAccessibilityServiceClient serviceInterface, int sequence, int displayId, + boolean fromAccessibilityTool) { SomeArgs args = SomeArgs.obtain(); args.arg1 = gestureSteps; args.arg2 = serviceInterface; args.argi1 = sequence; args.argi2 = displayId; + args.argi3 = fromAccessibilityTool ? 1 : 0; mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_INJECT_EVENTS, args)); } @@ -132,9 +134,11 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement return; } cancelAnyPendingInjectedEvents(); - // Indicate that the input event is injected from accessibility, to let applications - // distinguish it from events injected by other means. - policyFlags |= WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY; + if (!android.view.accessibility.Flags.preventA11yNontoolFromInjectingIntoSensitiveViews()) { + // Indicate that the input event is injected from accessibility, to let applications + // distinguish it from events injected by other means. + policyFlags |= WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY; + } sendMotionEventToNext(event, rawEvent, policyFlags); } @@ -159,8 +163,12 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement public boolean handleMessage(Message message) { if (message.what == MESSAGE_INJECT_EVENTS) { SomeArgs args = (SomeArgs) message.obj; - injectEventsMainThread((List<GestureStep>) args.arg1, - (IAccessibilityServiceClient) args.arg2, args.argi1, args.argi2); + injectEventsMainThread( + /*gestureSteps=*/(List<GestureStep>) args.arg1, + /*serviceInterface=*/(IAccessibilityServiceClient) args.arg2, + /*sequence=*/args.argi1, + /*displayId=*/args.argi2, + /*fromAccessibilityTool=*/args.argi3 == 1); args.recycle(); return true; } @@ -169,9 +177,15 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement return false; } MotionEvent motionEvent = (MotionEvent) message.obj; - sendMotionEventToNext(motionEvent, motionEvent, - WindowManagerPolicyConstants.FLAG_PASS_TO_USER - | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY); + int policyFlags = WindowManagerPolicyConstants.FLAG_PASS_TO_USER + | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY; + if (android.view.accessibility.Flags.preventA11yNontoolFromInjectingIntoSensitiveViews()) { + boolean fromAccessibilityTool = message.arg2 == 1; + if (fromAccessibilityTool) { + policyFlags |= WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL; + } + } + sendMotionEventToNext(motionEvent, motionEvent, policyFlags); boolean isEndOfSequence = message.arg1 != 0; if (isEndOfSequence) { notifyService(mServiceInterfaceForCurrentGesture, mSequencesInProgress.get(0), true); @@ -181,7 +195,8 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement } private void injectEventsMainThread(List<GestureStep> gestureSteps, - IAccessibilityServiceClient serviceInterface, int sequence, int displayId) { + IAccessibilityServiceClient serviceInterface, int sequence, int displayId, + boolean fromAccessibilityTool) { if (mIsDestroyed) { try { serviceInterface.onPerformGestureResult(sequence, false); @@ -228,7 +243,8 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement event.setDisplayId(displayId); int isEndOfSequence = (i == events.size() - 1) ? 1 : 0; Message message = mHandler.obtainMessage( - MESSAGE_SEND_MOTION_EVENT, isEndOfSequence, 0, event); + MESSAGE_SEND_MOTION_EVENT, isEndOfSequence, + fromAccessibilityTool ? 1 : 0, event); mLastScheduledEventTime = event.getEventTime(); mHandler.sendMessageDelayed(message, Math.max(0, event.getEventTime() - currentTime)); } @@ -322,9 +338,16 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement long now = SystemClock.uptimeMillis(); MotionEvent cancelEvent = obtainMotionEvent(now, now, MotionEvent.ACTION_CANCEL, getLastTouchPoints(), 1); - sendMotionEventToNext(cancelEvent, cancelEvent, - WindowManagerPolicyConstants.FLAG_PASS_TO_USER - | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY); + int policyFlags = WindowManagerPolicyConstants.FLAG_PASS_TO_USER + | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY; + if (android.view.accessibility.Flags + .preventA11yNontoolFromInjectingIntoSensitiveViews()) { + // ACTION_CANCEL events are internal system details for event stream state + // management and not used for performing new actions, so always treat them as + // originating from an accessibility tool. + policyFlags |= WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL; + } + sendMotionEventToNext(cancelEvent, cancelEvent, policyFlags); mOpenGesturesInProgress.put(source, false); } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java index d4f2dcc24af6..5d94e7274a1f 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java @@ -250,9 +250,26 @@ public class AccessibilityServiceConnectionTest { } @Test - public void sendGesture_touchableDevice_injectEvents() - throws RemoteException { + public void sendGesture_touchableDevice_injectEvents_fromAccessibilityTool() { + when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(true); + when(mServiceInfo.isAccessibilityTool()).thenReturn(true); + setServiceBinding(COMPONENT_NAME); + mConnection.bindLocked(); + mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); + + ParceledListSlice parceledListSlice = mock(ParceledListSlice.class); + List<GestureDescription.GestureStep> gestureSteps = mock(List.class); + when(parceledListSlice.getList()).thenReturn(gestureSteps); + mConnection.dispatchGesture(0, parceledListSlice, Display.DEFAULT_DISPLAY); + + verify(mMockMotionEventInjector).injectEvents(gestureSteps, mMockServiceClient, 0, + Display.DEFAULT_DISPLAY, true); + } + + @Test + public void sendGesture_touchableDevice_injectEvents_fromNonTool() { when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(true); + when(mServiceInfo.isAccessibilityTool()).thenReturn(false); setServiceBinding(COMPONENT_NAME); mConnection.bindLocked(); mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); @@ -263,13 +280,14 @@ public class AccessibilityServiceConnectionTest { mConnection.dispatchGesture(0, parceledListSlice, Display.DEFAULT_DISPLAY); verify(mMockMotionEventInjector).injectEvents(gestureSteps, mMockServiceClient, 0, - Display.DEFAULT_DISPLAY); + Display.DEFAULT_DISPLAY, false); } @Test public void sendGesture_untouchableDevice_performGestureResultFailed() throws RemoteException { when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(false); + when(mServiceInfo.isAccessibilityTool()).thenReturn(true); setServiceBinding(COMPONENT_NAME); mConnection.bindLocked(); mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); @@ -280,7 +298,7 @@ public class AccessibilityServiceConnectionTest { mConnection.dispatchGesture(0, parceledListSlice, Display.DEFAULT_DISPLAY); verify(mMockMotionEventInjector, never()).injectEvents(gestureSteps, mMockServiceClient, 0, - Display.DEFAULT_DISPLAY); + Display.DEFAULT_DISPLAY, true); verify(mMockServiceClient).onPerformGestureResult(0, false); } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java index 233caf9c7761..d2d8c682ed90 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java @@ -20,8 +20,7 @@ import static android.view.KeyCharacterMap.VIRTUAL_KEYBOARD; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_HOVER_MOVE; import static android.view.MotionEvent.ACTION_UP; -import static android.view.WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY; -import static android.view.WindowManagerPolicyConstants.FLAG_PASS_TO_USER; +import static android.view.accessibility.Flags.FLAG_PREVENT_A11Y_NONTOOL_FROM_INJECTING_INTO_SENSITIVE_VIEWS; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.anyOf; @@ -48,10 +47,14 @@ import android.graphics.Point; import android.os.Handler; import android.os.Message; import android.os.RemoteException; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.view.Display; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.WindowManagerPolicyConstants; import android.view.accessibility.AccessibilityEvent; import androidx.test.runner.AndroidJUnit4; @@ -64,6 +67,7 @@ import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -77,7 +81,7 @@ import java.util.List; */ @RunWith(AndroidJUnit4.class) public class MotionEventInjectorTest { - private static final String LOG_TAG = "MotionEventInjectorTest"; + private static final Matcher<MotionEvent> IS_ACTION_DOWN = new MotionEventActionMatcher(ACTION_DOWN); private static final Matcher<MotionEvent> IS_ACTION_POINTER_DOWN = @@ -120,6 +124,9 @@ public class MotionEventInjectorTest { private static final float POINTER_SIZE = 1; private static final int METASTATE = 0; + @Rule + public SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + MotionEventInjector mMotionEventInjector; IAccessibilityServiceClient mServiceInterface; AccessibilityTraceManager mTrace; @@ -201,7 +208,8 @@ public class MotionEventInjectorTest { verifyNoMoreInteractions(next); mMessageCapturingHandler.sendOneMessage(); // Send a motion event - final int expectedFlags = FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY; + final int expectedFlags = WindowManagerPolicyConstants.FLAG_PASS_TO_USER + | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY; verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), eq(expectedFlags)); verify(next).onMotionEvent(argThat(mIsLineStart), argThat(mIsLineStart), eq(expectedFlags)); verifyNoMoreInteractions(next); @@ -227,6 +235,21 @@ public class MotionEventInjectorTest { } @Test + @EnableFlags(FLAG_PREVENT_A11Y_NONTOOL_FROM_INJECTING_INTO_SENSITIVE_VIEWS) + public void testInjectEvents_fromAccessibilityTool_providesToolPolicyFlag() { + EventStreamTransformation next = attachMockNext(mMotionEventInjector); + injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE, + /*fromAccessibilityTool=*/true); + + mMessageCapturingHandler.sendOneMessage(); // Send a motion event + verify(next).onMotionEvent( + argThat(mIsLineStart), argThat(mIsLineStart), + eq(WindowManagerPolicyConstants.FLAG_PASS_TO_USER + | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY + | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL)); + } + + @Test public void testInjectEvents_gestureWithTooManyPoints_shouldNotCrash() throws Exception { int tooManyPointsCount = 20; TouchPoint[] startTouchPoints = new TouchPoint[tooManyPointsCount]; @@ -251,14 +274,28 @@ public class MotionEventInjectorTest { } @Test - public void testRegularEvent_afterGestureComplete_shouldPassToNext() { + @DisableFlags(FLAG_PREVENT_A11Y_NONTOOL_FROM_INJECTING_INTO_SENSITIVE_VIEWS) + public void testRegularEvent_afterGestureComplete_shouldPassToNext_withFlagInjectedFromA11y() { + EventStreamTransformation next = attachMockNext(mMotionEventInjector); + injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); + mMessageCapturingHandler.sendAllMessages(); // Send all motion events + reset(next); + mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0); + verify(next).onMotionEvent(argThat(mIsClickDown), argThat(mIsClickDown), + eq(WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY)); + } + + @Test + @EnableFlags(FLAG_PREVENT_A11Y_NONTOOL_FROM_INJECTING_INTO_SENSITIVE_VIEWS) + public void testRegularEvent_afterGestureComplete_shouldPassToNext_withNoPolicyFlagChanges() { EventStreamTransformation next = attachMockNext(mMotionEventInjector); injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE); mMessageCapturingHandler.sendAllMessages(); // Send all motion events reset(next); mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0); verify(next).onMotionEvent(argThat(mIsClickDown), argThat(mIsClickDown), - eq(FLAG_INJECTED_FROM_ACCESSIBILITY)); + // The regular event passing through the filter should have no policy flag changes + eq(0)); } @Test @@ -275,7 +312,8 @@ public class MotionEventInjectorTest { mMessageCapturingHandler.sendOneMessage(); // Send a motion event verify(next).onMotionEvent( argThat(mIsLineStart), argThat(mIsLineStart), - eq(FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY)); + eq(WindowManagerPolicyConstants.FLAG_PASS_TO_USER + | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY)); } @Test @@ -307,10 +345,12 @@ public class MotionEventInjectorTest { mMessageCapturingHandler.sendOneMessage(); // Send a motion event verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), - eq(FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY)); + eq(WindowManagerPolicyConstants.FLAG_PASS_TO_USER + | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY)); verify(next).onMotionEvent( argThat(mIsLineStart), argThat(mIsLineStart), - eq(FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY)); + eq(WindowManagerPolicyConstants.FLAG_PASS_TO_USER + | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY)); } @Test @@ -731,8 +771,14 @@ public class MotionEventInjectorTest { private void injectEventsSync(List<GestureStep> gestureSteps, IAccessibilityServiceClient serviceInterface, int sequence) { + injectEventsSync(gestureSteps, serviceInterface, sequence, false); + } + + private void injectEventsSync(List<GestureStep> gestureSteps, + IAccessibilityServiceClient serviceInterface, int sequence, + boolean fromAccessibilityTool) { mMotionEventInjector.injectEvents(gestureSteps, serviceInterface, sequence, - Display.DEFAULT_DISPLAY); + Display.DEFAULT_DISPLAY, fromAccessibilityTool); // Dispatch the message sent by the injector. Our simple handler doesn't guarantee stuff // happens in order. mMessageCapturingHandler.sendLastMessage(); |