1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.accessibility;
18 
19 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
20 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
21 
22 import android.accessibilityservice.AccessibilityTrace;
23 import android.annotation.MainThread;
24 import android.annotation.NonNull;
25 import android.content.Context;
26 import android.graphics.Region;
27 import android.os.PowerManager;
28 import android.provider.Settings;
29 import android.util.Slog;
30 import android.util.SparseArray;
31 import android.util.SparseBooleanArray;
32 import android.view.Display;
33 import android.view.InputDevice;
34 import android.view.InputEvent;
35 import android.view.InputFilter;
36 import android.view.KeyEvent;
37 import android.view.MotionEvent;
38 import android.view.accessibility.AccessibilityEvent;
39 
40 import com.android.server.LocalServices;
41 import com.android.server.accessibility.gestures.TouchExplorer;
42 import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler;
43 import com.android.server.accessibility.magnification.MagnificationGestureHandler;
44 import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler;
45 import com.android.server.accessibility.magnification.WindowMagnificationPromptController;
46 import com.android.server.policy.WindowManagerPolicy;
47 
48 import java.io.FileDescriptor;
49 import java.io.PrintWriter;
50 import java.util.ArrayList;
51 import java.util.StringJoiner;
52 
53 /**
54  * This class is an input filter for implementing accessibility features such
55  * as display magnification and explore by touch.
56  *
57  * NOTE: This class has to be created and poked only from the main thread.
58  */
59 class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation {
60 
61     private static final String TAG = AccessibilityInputFilter.class.getSimpleName();
62 
63     private static final boolean DEBUG = false;
64 
65     /**
66      * Flag for enabling the screen magnification feature.
67      *
68      * @see #setUserAndEnabledFeatures(int, int)
69      */
70     static final int FLAG_FEATURE_SCREEN_MAGNIFIER = 0x00000001;
71 
72     /**
73      * Flag for enabling the touch exploration feature.
74      *
75      * @see #setUserAndEnabledFeatures(int, int)
76      */
77     static final int FLAG_FEATURE_TOUCH_EXPLORATION = 0x00000002;
78 
79     /**
80      * Flag for enabling the filtering key events feature.
81      *
82      * @see #setUserAndEnabledFeatures(int, int)
83      */
84     static final int FLAG_FEATURE_FILTER_KEY_EVENTS = 0x00000004;
85 
86     /**
87      * Flag for enabling "Automatically click on mouse stop" feature.
88      *
89      * @see #setUserAndEnabledFeatures(int, int)
90      */
91     static final int FLAG_FEATURE_AUTOCLICK = 0x00000008;
92 
93     /**
94      * Flag for enabling motion event injection.
95      *
96      * @see #setUserAndEnabledFeatures(int, int)
97      */
98     static final int FLAG_FEATURE_INJECT_MOTION_EVENTS = 0x00000010;
99 
100     /**
101      * Flag for enabling the feature to control the screen magnifier. If
102      * {@link #FLAG_FEATURE_SCREEN_MAGNIFIER} is set this flag is ignored
103      * as the screen magnifier feature performs a super set of the work
104      * performed by this feature.
105      *
106      * @see #setUserAndEnabledFeatures(int, int)
107      */
108     static final int FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER = 0x00000020;
109 
110     /**
111      * Flag for enabling the feature to trigger the screen magnifier
112      * from another on-device interaction.
113      */
114     static final int FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER = 0x00000040;
115 
116     /**
117      * Flag for dispatching double tap and double tap and hold to the service.
118      *
119      * @see #setUserAndEnabledFeatures(int, int)
120      */
121     static final int FLAG_SERVICE_HANDLES_DOUBLE_TAP = 0x00000080;
122 
123 /**
124      * Flag for enabling multi-finger gestures.
125      *
126      * @see #setUserAndEnabledFeatures(int, int)
127      */
128     static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000100;
129 
130     /**
131      * Flag for enabling two-finger passthrough when multi-finger gestures are enabled.
132      *
133      * @see #setUserAndEnabledFeatures(int, int)
134      */
135     static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x00000200;
136 
137     /**
138      * Flag for including motion events when dispatching a gesture.
139      *
140      * @see #setUserAndEnabledFeatures(int, int)
141      */
142     static final int FLAG_SEND_MOTION_EVENTS = 0x00000400;
143 
144     /** Flag for intercepting generic motion events. */
145     static final int FLAG_FEATURE_INTERCEPT_GENERIC_MOTION_EVENTS = 0x00000800;
146 
147     static final int FEATURES_AFFECTING_MOTION_EVENTS =
148             FLAG_FEATURE_INJECT_MOTION_EVENTS
149                     | FLAG_FEATURE_AUTOCLICK
150                     | FLAG_FEATURE_TOUCH_EXPLORATION
151                     | FLAG_FEATURE_SCREEN_MAGNIFIER
152                     | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER
153                     | FLAG_SERVICE_HANDLES_DOUBLE_TAP
154                     | FLAG_REQUEST_MULTI_FINGER_GESTURES
155                     | FLAG_REQUEST_2_FINGER_PASSTHROUGH
156                     | FLAG_FEATURE_INTERCEPT_GENERIC_MOTION_EVENTS;
157 
158     private final Context mContext;
159 
160     private final PowerManager mPm;
161 
162     private final AccessibilityManagerService mAms;
163 
164     private final SparseArray<EventStreamTransformation> mEventHandler;
165 
166     private final SparseArray<TouchExplorer> mTouchExplorer = new SparseArray<>(0);
167 
168     private final SparseArray<MagnificationGestureHandler> mMagnificationGestureHandler =
169             new SparseArray<>(0);
170 
171     private final SparseArray<MotionEventInjector> mMotionEventInjectors = new SparseArray<>(0);
172 
173     private AutoclickController mAutoclickController;
174 
175     private KeyboardInterceptor mKeyboardInterceptor;
176 
177     private boolean mInstalled;
178 
179     private int mUserId;
180 
181     private int mEnabledFeatures;
182 
183     // Display-specific features
184     private SparseArray<Boolean> mServiceDetectsGestures = new SparseArray<>();
185     private final SparseArray<EventStreamState> mMouseStreamStates = new SparseArray<>(0);
186 
187     private final SparseArray<EventStreamState> mTouchScreenStreamStates = new SparseArray<>(0);
188 
189     // State tracking for generic MotionEvents is display-agnostic so we only need one.
190     private GenericMotionEventStreamState mGenericMotionEventStreamState;
191     private int mCombinedGenericMotionEventSources = 0;
192 
193     private EventStreamState mKeyboardStreamState;
194 
AccessibilityInputFilter(Context context, AccessibilityManagerService service)195     AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
196         this(context, service, new SparseArray<>(0));
197     }
198 
AccessibilityInputFilter(Context context, AccessibilityManagerService service, SparseArray<EventStreamTransformation> eventHandler)199     AccessibilityInputFilter(Context context, AccessibilityManagerService service,
200             SparseArray<EventStreamTransformation> eventHandler) {
201         super(context.getMainLooper());
202         mContext = context;
203         mAms = service;
204         mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
205         mEventHandler = eventHandler;
206     }
207 
208     @Override
onInstalled()209     public void onInstalled() {
210         if (DEBUG) {
211             Slog.d(TAG, "Accessibility input filter installed.");
212         }
213         mInstalled = true;
214         disableFeatures();
215         enableFeatures();
216         mAms.onInputFilterInstalled(true);
217         super.onInstalled();
218     }
219 
220     @Override
onUninstalled()221     public void onUninstalled() {
222         if (DEBUG) {
223             Slog.d(TAG, "Accessibility input filter uninstalled.");
224         }
225         mInstalled = false;
226         disableFeatures();
227         mAms.onInputFilterInstalled(false);
228         super.onUninstalled();
229     }
230 
onDisplayAdded(@onNull Display display)231     void onDisplayAdded(@NonNull Display display) {
232         enableFeaturesForDisplayIfInstalled(display);
233 
234     }
235 
onDisplayRemoved(int displayId)236     void onDisplayRemoved(int displayId) {
237         disableFeaturesForDisplayIfInstalled(displayId);
238     }
239 
240     @Override
onInputEvent(InputEvent event, int policyFlags)241     public void onInputEvent(InputEvent event, int policyFlags) {
242         if (DEBUG) {
243             Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
244                     + Integer.toHexString(policyFlags));
245         }
246         if (mAms.getTraceManager().isA11yTracingEnabledForTypes(
247                 AccessibilityTrace.FLAGS_INPUT_FILTER)) {
248             mAms.getTraceManager().logTrace(TAG + ".onInputEvent",
249                     AccessibilityTrace.FLAGS_INPUT_FILTER,
250                     "event=" + event + ";policyFlags=" + policyFlags);
251         }
252         if (mEventHandler.size() == 0) {
253             if (DEBUG) Slog.d(TAG, "No mEventHandler for event " + event);
254             super.onInputEvent(event, policyFlags);
255             return;
256         }
257 
258         EventStreamState state = getEventStreamState(event);
259         if (state == null) {
260             super.onInputEvent(event, policyFlags);
261             return;
262         }
263 
264         final int eventSource = event.getSource();
265         final int displayId = event.getDisplayId();
266         if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
267             state.reset();
268             clearEventStreamHandler(displayId, eventSource);
269             super.onInputEvent(event, policyFlags);
270             return;
271         }
272 
273         if (state.updateInputSource(event.getSource())) {
274             clearEventStreamHandler(displayId, eventSource);
275         }
276 
277         if (!state.inputSourceValid()) {
278             super.onInputEvent(event, policyFlags);
279             return;
280         }
281 
282         if (event instanceof MotionEvent) {
283             if ((mEnabledFeatures & FEATURES_AFFECTING_MOTION_EVENTS) != 0) {
284                 MotionEvent motionEvent = (MotionEvent) event;
285                 processMotionEvent(state, motionEvent, policyFlags);
286                 return;
287             } else {
288                 super.onInputEvent(event, policyFlags);
289             }
290         } else if (event instanceof KeyEvent) {
291             KeyEvent keyEvent = (KeyEvent) event;
292             processKeyEvent(state, keyEvent, policyFlags);
293         }
294     }
295 
296     /**
297      * Gets current event stream state associated with an input event.
298      * @return The event stream state that should be used for the event. Null if the event should
299      *     not be handled by #AccessibilityInputFilter.
300      */
getEventStreamState(InputEvent event)301     private EventStreamState getEventStreamState(InputEvent event) {
302         if (event instanceof MotionEvent) {
303             final int displayId = event.getDisplayId();
304             if (mGenericMotionEventStreamState == null) {
305                 mGenericMotionEventStreamState = new GenericMotionEventStreamState();
306             }
307 
308             if (mGenericMotionEventStreamState.shouldProcessMotionEvent((MotionEvent) event)) {
309                 return mGenericMotionEventStreamState;
310             }
311             if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
312                 EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId);
313                 if (touchScreenStreamState == null) {
314                     touchScreenStreamState = new TouchScreenEventStreamState();
315                     mTouchScreenStreamStates.put(displayId, touchScreenStreamState);
316                 }
317                 return touchScreenStreamState;
318             }
319             if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
320                 EventStreamState mouseStreamState = mMouseStreamStates.get(displayId);
321                 if (mouseStreamState == null) {
322                     mouseStreamState = new MouseEventStreamState();
323                     mMouseStreamStates.put(displayId, mouseStreamState);
324                 }
325                 return mouseStreamState;
326             }
327         } else if (event instanceof KeyEvent) {
328             if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
329                 if (mKeyboardStreamState == null) {
330                     mKeyboardStreamState = new KeyboardEventStreamState();
331                 }
332                 return mKeyboardStreamState;
333             }
334         }
335         return null;
336     }
337 
clearEventStreamHandler(int displayId, int eventSource)338     private void clearEventStreamHandler(int displayId, int eventSource) {
339         final EventStreamTransformation eventHandler = mEventHandler.get(displayId);
340         if (eventHandler != null) {
341             eventHandler.clearEvents(eventSource);
342         }
343     }
344 
processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags)345     private void processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags) {
346         if (!state.shouldProcessScroll() && event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
347             super.onInputEvent(event, policyFlags);
348             return;
349         }
350 
351         if (!state.shouldProcessMotionEvent(event)) {
352             return;
353         }
354 
355         handleMotionEvent(event, policyFlags);
356     }
357 
processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags)358     private void processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags) {
359         if (!state.shouldProcessKeyEvent(event)) {
360             super.onInputEvent(event, policyFlags);
361             return;
362         }
363         // Since the display id of KeyEvent always would be -1 and there is only one
364         // KeyboardInterceptor for all display, pass KeyEvent to the mEventHandler of
365         // DEFAULT_DISPLAY to handle.
366         mEventHandler.get(Display.DEFAULT_DISPLAY).onKeyEvent(event, policyFlags);
367     }
368 
handleMotionEvent(MotionEvent event, int policyFlags)369     private void handleMotionEvent(MotionEvent event, int policyFlags) {
370         if (DEBUG) {
371             Slog.i(TAG, "Handling motion event: " + event + ", policyFlags: " + policyFlags);
372         }
373         mPm.userActivity(event.getEventTime(), false);
374         MotionEvent transformedEvent = MotionEvent.obtain(event);
375         final int displayId = event.getDisplayId();
376         EventStreamTransformation eventStreamTransformation = mEventHandler.get(
377                 isDisplayIdValid(displayId) ? displayId : Display.DEFAULT_DISPLAY);
378         if (eventStreamTransformation != null) {
379             eventStreamTransformation.onMotionEvent(transformedEvent, event, policyFlags);
380         }
381         transformedEvent.recycle();
382     }
383 
isDisplayIdValid(int displayId)384     private boolean isDisplayIdValid(int displayId) {
385         return mEventHandler.get(displayId) != null;
386     }
387 
388     @Override
onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent, int policyFlags)389     public void onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent,
390             int policyFlags) {
391         if (!mInstalled) {
392             Slog.w(TAG, "onMotionEvent called before input filter installed!");
393             return;
394         }
395         sendInputEvent(transformedEvent, policyFlags);
396     }
397 
398     @Override
onKeyEvent(KeyEvent event, int policyFlags)399     public void onKeyEvent(KeyEvent event, int policyFlags) {
400         if (!mInstalled) {
401             Slog.w(TAG, "onKeyEvent called before input filter installed!");
402             return;
403         }
404         sendInputEvent(event, policyFlags);
405     }
406 
407     @Override
onAccessibilityEvent(AccessibilityEvent event)408     public void onAccessibilityEvent(AccessibilityEvent event) {
409         // TODO Implement this to inject the accessibility event
410         //      into the accessibility manager service similarly
411         //      to how this is done for input events.
412     }
413 
414     @Override
setNext(EventStreamTransformation sink)415     public void setNext(EventStreamTransformation sink) {
416         /* do nothing */
417     }
418 
419     @Override
getNext()420     public EventStreamTransformation getNext() {
421         return null;
422     }
423 
424     @Override
clearEvents(int inputSource)425     public void clearEvents(int inputSource) {
426         /* do nothing */
427     }
428 
setUserAndEnabledFeatures(int userId, int enabledFeatures)429     void setUserAndEnabledFeatures(int userId, int enabledFeatures) {
430         if (DEBUG) {
431             Slog.i(TAG, "setUserAndEnabledFeatures(userId = " + userId + ", enabledFeatures = 0x"
432                     + Integer.toHexString(enabledFeatures) + ")");
433         }
434         if (mEnabledFeatures == enabledFeatures && mUserId == userId) {
435             return;
436         }
437         if (mInstalled) {
438             disableFeatures();
439         }
440         mUserId = userId;
441         mEnabledFeatures = enabledFeatures;
442         if (mInstalled) {
443             enableFeatures();
444         }
445     }
446 
notifyAccessibilityEvent(AccessibilityEvent event)447     void notifyAccessibilityEvent(AccessibilityEvent event) {
448         for (int i = 0; i < mEventHandler.size(); i++) {
449             final EventStreamTransformation eventHandler = mEventHandler.valueAt(i);
450             if (eventHandler != null) {
451                 eventHandler.onAccessibilityEvent(event);
452             }
453         }
454     }
455 
notifyAccessibilityButtonClicked(int displayId)456     void notifyAccessibilityButtonClicked(int displayId) {
457         if (mMagnificationGestureHandler.size() != 0) {
458             final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId);
459             if (handler != null) {
460                 handler.notifyShortcutTriggered();
461             }
462         }
463     }
464 
enableFeatures()465     private void enableFeatures() {
466         if (DEBUG) Slog.i(TAG, "enableFeatures()");
467 
468         resetAllStreamState();
469 
470         final ArrayList<Display> displaysList = mAms.getValidDisplayList();
471 
472         for (int i = displaysList.size() - 1; i >= 0; i--) {
473             enableFeaturesForDisplay(displaysList.get(i));
474         }
475         enableDisplayIndependentFeatures();
476     }
477 
enableFeaturesForDisplay(Display display)478     private void enableFeaturesForDisplay(Display display) {
479         if (DEBUG) {
480             Slog.i(TAG, "enableFeaturesForDisplay() : display Id = " + display.getDisplayId());
481         }
482 
483         final Context displayContext = mContext.createDisplayContext(display);
484         final int displayId = display.getDisplayId();
485         if (mAms.isDisplayProxyed(displayId)) {
486             return;
487         }
488         if (!mServiceDetectsGestures.contains(displayId)) {
489             mServiceDetectsGestures.put(displayId, false);
490         }
491         if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
492             if (mAutoclickController == null) {
493                 mAutoclickController = new AutoclickController(
494                         mContext, mUserId, mAms.getTraceManager());
495             }
496             addFirstEventHandler(displayId, mAutoclickController);
497         }
498 
499         if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
500             TouchExplorer explorer = new TouchExplorer(displayContext, mAms);
501             if ((mEnabledFeatures & FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0) {
502                 explorer.setServiceHandlesDoubleTap(true);
503             }
504             if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) {
505                 explorer.setMultiFingerGesturesEnabled(true);
506             }
507             if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) {
508                 explorer.setTwoFingerPassthroughEnabled(true);
509             }
510             if ((mEnabledFeatures & FLAG_SEND_MOTION_EVENTS) != 0) {
511                 explorer.setSendMotionEventsEnabled(true);
512             }
513             explorer.setServiceDetectsGestures(mServiceDetectsGestures.get(displayId));
514             addFirstEventHandler(displayId, explorer);
515             mTouchExplorer.put(displayId, explorer);
516         }
517 
518         if ((mEnabledFeatures & FLAG_FEATURE_INTERCEPT_GENERIC_MOTION_EVENTS) != 0) {
519             addFirstEventHandler(displayId, new BaseEventStreamTransformation() {
520                 @Override
521                 public void onMotionEvent(MotionEvent event, MotionEvent rawEvent,
522                         int policyFlags) {
523                     if (!anyServiceWantsGenericMotionEvent(rawEvent)
524                             || !mAms.sendMotionEventToListeningServices(rawEvent)) {
525                         super.onMotionEvent(event, rawEvent, policyFlags);
526                     }
527                 }
528             });
529         }
530 
531         if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
532                 || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
533                 || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
534             final MagnificationGestureHandler magnificationGestureHandler =
535                     createMagnificationGestureHandler(displayId,
536                             displayContext);
537             addFirstEventHandler(displayId, magnificationGestureHandler);
538             mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
539         }
540 
541         if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
542             MotionEventInjector injector = new MotionEventInjector(
543                     mContext.getMainLooper(), mAms.getTraceManager());
544             addFirstEventHandler(displayId, injector);
545             mMotionEventInjectors.put(displayId, injector);
546         }
547     }
548 
enableDisplayIndependentFeatures()549     private void enableDisplayIndependentFeatures() {
550         if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
551             mAms.setMotionEventInjectors(mMotionEventInjectors);
552         }
553 
554         if ((mEnabledFeatures & FLAG_FEATURE_FILTER_KEY_EVENTS) != 0) {
555             mKeyboardInterceptor = new KeyboardInterceptor(mAms,
556                     LocalServices.getService(WindowManagerPolicy.class));
557             // Since the display id of KeyEvent always would be -1 and it would be dispatched to
558             // the display with input focus directly, we only need one KeyboardInterceptor for
559             // default display.
560             addFirstEventHandler(Display.DEFAULT_DISPLAY, mKeyboardInterceptor);
561         }
562     }
563 
564     /**
565      * Adds an event handler to the event handler chain for giving display. The handler is added at
566      * the beginning of the chain.
567      *
568      * @param displayId The logical display id.
569      * @param handler The handler to be added to the event handlers list.
570      */
addFirstEventHandler(int displayId, EventStreamTransformation handler)571     private void addFirstEventHandler(int displayId, EventStreamTransformation handler) {
572         EventStreamTransformation eventHandler = mEventHandler.get(displayId);
573         if (eventHandler != null) {
574             handler.setNext(eventHandler);
575         } else {
576             handler.setNext(this);
577         }
578         eventHandler = handler;
579         mEventHandler.put(displayId, eventHandler);
580     }
581 
disableFeatures()582     private void disableFeatures() {
583         final ArrayList<Display> displaysList = mAms.getValidDisplayList();
584 
585         for (int i = displaysList.size() - 1; i >= 0; i--) {
586             disableFeaturesForDisplay(displaysList.get(i).getDisplayId());
587         }
588         mAms.setMotionEventInjectors(null);
589         disableDisplayIndependentFeatures();
590 
591         resetAllStreamState();
592     }
593 
disableFeaturesForDisplay(int displayId)594     private void disableFeaturesForDisplay(int displayId) {
595         if (DEBUG) {
596             Slog.i(TAG, "disableFeaturesForDisplay() : display Id = " + displayId);
597         }
598 
599         final MotionEventInjector injector = mMotionEventInjectors.get(displayId);
600         if (injector != null) {
601             injector.onDestroy();
602             mMotionEventInjectors.remove(displayId);
603         }
604 
605         final TouchExplorer explorer = mTouchExplorer.get(displayId);
606         if (explorer != null) {
607             explorer.onDestroy();
608             mTouchExplorer.remove(displayId);
609         }
610 
611         final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId);
612         if (handler != null) {
613             handler.onDestroy();
614             mMagnificationGestureHandler.remove(displayId);
615         }
616 
617         final EventStreamTransformation eventStreamTransformation = mEventHandler.get(displayId);
618         if (eventStreamTransformation != null) {
619             mEventHandler.remove(displayId);
620         }
621     }
enableFeaturesForDisplayIfInstalled(Display display)622     void enableFeaturesForDisplayIfInstalled(Display display) {
623         if (mInstalled) {
624             resetStreamStateForDisplay(display.getDisplayId());
625             enableFeaturesForDisplay(display);
626         }
627     }
disableFeaturesForDisplayIfInstalled(int displayId)628     void disableFeaturesForDisplayIfInstalled(int displayId) {
629         if (mInstalled) {
630             disableFeaturesForDisplay(displayId);
631             resetStreamStateForDisplay(displayId);
632         }
633     }
634 
disableDisplayIndependentFeatures()635     private void disableDisplayIndependentFeatures() {
636         if (mAutoclickController != null) {
637             mAutoclickController.onDestroy();
638             mAutoclickController = null;
639         }
640 
641         if (mKeyboardInterceptor != null) {
642             mKeyboardInterceptor.onDestroy();
643             mKeyboardInterceptor = null;
644         }
645     }
646 
createMagnificationGestureHandler( int displayId, Context displayContext)647     private MagnificationGestureHandler createMagnificationGestureHandler(
648             int displayId, Context displayContext) {
649         final boolean detectControlGestures = (mEnabledFeatures
650                 & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0;
651         final boolean triggerable = (mEnabledFeatures
652                 & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0;
653         MagnificationGestureHandler magnificationGestureHandler;
654         if (mAms.getMagnificationMode(displayId)
655                 == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
656             final Context uiContext = displayContext.createWindowContext(
657                     TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
658             magnificationGestureHandler = new WindowMagnificationGestureHandler(uiContext,
659                     mAms.getWindowMagnificationMgr(), mAms.getTraceManager(),
660                     mAms.getMagnificationController(), detectControlGestures, triggerable,
661                     displayId);
662         } else {
663             final Context uiContext = displayContext.createWindowContext(
664                     TYPE_MAGNIFICATION_OVERLAY, null /* options */);
665             magnificationGestureHandler = new FullScreenMagnificationGestureHandler(uiContext,
666                     mAms.getMagnificationController().getFullScreenMagnificationController(),
667                     mAms.getTraceManager(),
668                     mAms.getMagnificationController(), detectControlGestures, triggerable,
669                     new WindowMagnificationPromptController(displayContext, mUserId), displayId);
670         }
671         return magnificationGestureHandler;
672     }
673 
resetAllStreamState()674     void resetAllStreamState() {
675         final ArrayList<Display> displaysList = mAms.getValidDisplayList();
676 
677         for (int i = displaysList.size() - 1; i >= 0; i--) {
678             resetStreamStateForDisplay(displaysList.get(i).getDisplayId());
679         }
680 
681         if (mKeyboardStreamState != null) {
682             mKeyboardStreamState.reset();
683         }
684     }
685 
resetStreamStateForDisplay(int displayId)686     void resetStreamStateForDisplay(int displayId) {
687         final EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId);
688         if (touchScreenStreamState != null) {
689             touchScreenStreamState.reset();
690             mTouchScreenStreamStates.remove(displayId);
691         }
692 
693         final EventStreamState mouseStreamState = mMouseStreamStates.get(displayId);
694         if (mouseStreamState != null) {
695             mouseStreamState.reset();
696             mMouseStreamStates.remove(displayId);
697         }
698     }
699 
700     @Override
onDestroy()701     public void onDestroy() {
702         /* ignore */
703     }
704 
705     /**
706      * Called to refresh the magnification mode on the given display.
707      * It's responsible for changing {@link MagnificationGestureHandler} based on the current mode.
708      *
709      * @param display The logical display
710      */
711     @MainThread
refreshMagnificationMode(Display display)712     public void refreshMagnificationMode(Display display) {
713         final int displayId = display.getDisplayId();
714         final MagnificationGestureHandler magnificationGestureHandler =
715                 mMagnificationGestureHandler.get(displayId);
716         if (magnificationGestureHandler == null) {
717             return;
718         }
719         if (magnificationGestureHandler.getMode() == mAms.getMagnificationMode(displayId)) {
720             return;
721         }
722         magnificationGestureHandler.onDestroy();
723         final MagnificationGestureHandler currentMagnificationGestureHandler =
724                 createMagnificationGestureHandler(displayId,
725                         mContext.createDisplayContext(display));
726         switchEventStreamTransformation(displayId, magnificationGestureHandler,
727                 currentMagnificationGestureHandler);
728         mMagnificationGestureHandler.put(displayId, currentMagnificationGestureHandler);
729     }
730 
731     @MainThread
switchEventStreamTransformation(int displayId, EventStreamTransformation oldStreamTransformation, EventStreamTransformation currentStreamTransformation)732     private void switchEventStreamTransformation(int displayId,
733             EventStreamTransformation oldStreamTransformation,
734             EventStreamTransformation currentStreamTransformation) {
735         EventStreamTransformation eventStreamTransformation = mEventHandler.get(displayId);
736         if (eventStreamTransformation == null) {
737             return;
738         }
739         if (eventStreamTransformation == oldStreamTransformation) {
740             currentStreamTransformation.setNext(oldStreamTransformation.getNext());
741             mEventHandler.put(displayId, currentStreamTransformation);
742         } else {
743             while (eventStreamTransformation != null) {
744                 if (eventStreamTransformation.getNext() == oldStreamTransformation) {
745                     eventStreamTransformation.setNext(currentStreamTransformation);
746                     currentStreamTransformation.setNext(oldStreamTransformation.getNext());
747                     return;
748                 } else {
749                     eventStreamTransformation = eventStreamTransformation.getNext();
750                 }
751             }
752         }
753     }
754 
755     /**
756      * Keeps state of event streams observed for an input device with a certain source.
757      * Provides information about whether motion and key events should be processed by accessibility
758      * #EventStreamTransformations. Base implementation describes behaviour for event sources that
759      * whose events should not be handled by a11y event stream transformations.
760      */
761     private static class EventStreamState {
762         private int mSource;
763 
EventStreamState()764         EventStreamState() {
765             mSource = -1;
766         }
767 
768         /**
769          * Updates the input source of the device associated with the state. If the source changes,
770          * resets internal state.
771          *
772          * @param source Updated input source.
773          * @return Whether the input source has changed.
774          */
updateInputSource(int source)775         public boolean updateInputSource(int source) {
776             if (mSource == source) {
777                 return false;
778             }
779             // Reset clears internal state, so make sure it's called before |mSource| is updated.
780             reset();
781             mSource = source;
782             return true;
783         }
784 
785         /**
786          * @return Whether input source is valid.
787          */
inputSourceValid()788         public boolean inputSourceValid() {
789             return mSource >= 0;
790         }
791 
792         /**
793          * Resets the event stream state.
794          */
reset()795         public void reset() {
796             mSource = -1;
797         }
798 
799         /**
800          * @return Whether scroll events for device should be handled by event transformations.
801          */
shouldProcessScroll()802         public boolean shouldProcessScroll() {
803             return false;
804         }
805 
806         /**
807          * @param event An observed motion event.
808          * @return Whether the event should be handled by event transformations.
809          */
shouldProcessMotionEvent(MotionEvent event)810         public boolean shouldProcessMotionEvent(MotionEvent event) {
811             return false;
812         }
813 
814         /**
815          * @param event An observed key event.
816          * @return Whether the event should be handled by event transformations.
817          */
shouldProcessKeyEvent(KeyEvent event)818         public boolean shouldProcessKeyEvent(KeyEvent event) {
819             return false;
820         }
821     }
822 
823     /**
824      * Keeps state of stream of events from a mouse device.
825      */
826     private static class MouseEventStreamState extends EventStreamState {
827         private boolean mMotionSequenceStarted;
828 
MouseEventStreamState()829         public MouseEventStreamState() {
830             reset();
831         }
832 
833         @Override
reset()834         final public void reset() {
835             super.reset();
836             mMotionSequenceStarted = false;
837         }
838 
839         @Override
shouldProcessScroll()840         final public boolean shouldProcessScroll() {
841             return true;
842         }
843 
844         @Override
shouldProcessMotionEvent(MotionEvent event)845         final public boolean shouldProcessMotionEvent(MotionEvent event) {
846             if (mMotionSequenceStarted) {
847                 return true;
848             }
849             // Wait for down or move event to start processing mouse events.
850             int action = event.getActionMasked();
851             mMotionSequenceStarted =
852                     action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_HOVER_MOVE;
853             return mMotionSequenceStarted;
854         }
855     }
856 
857     /**
858      * Keeps state of stream of events from a touch screen device.
859      */
860     private static class TouchScreenEventStreamState extends EventStreamState {
861         private boolean mTouchSequenceStarted;
862         private boolean mHoverSequenceStarted;
863 
TouchScreenEventStreamState()864         public TouchScreenEventStreamState() {
865             reset();
866         }
867 
868         @Override
reset()869         final public void reset() {
870             super.reset();
871             mTouchSequenceStarted = false;
872             mHoverSequenceStarted = false;
873         }
874 
875         @Override
shouldProcessMotionEvent(MotionEvent event)876         final public boolean shouldProcessMotionEvent(MotionEvent event) {
877             // Wait for a down touch event to start processing.
878             if (event.isTouchEvent()) {
879                 if (mTouchSequenceStarted) {
880                     return true;
881                 }
882                 mTouchSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_DOWN;
883                 return mTouchSequenceStarted;
884             }
885 
886             // Wait for an enter hover event to start processing.
887             if (mHoverSequenceStarted) {
888                 return true;
889             }
890             mHoverSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER;
891             return mHoverSequenceStarted;
892         }
893     }
894 
895     private class GenericMotionEventStreamState extends EventStreamState {
896         @Override
shouldProcessMotionEvent(MotionEvent event)897         public boolean shouldProcessMotionEvent(MotionEvent event) {
898             return anyServiceWantsGenericMotionEvent(event);
899         }
900         @Override
shouldProcessScroll()901         public boolean shouldProcessScroll() {
902             return true;
903         }
904     }
905 
anyServiceWantsGenericMotionEvent(MotionEvent event)906     private boolean anyServiceWantsGenericMotionEvent(MotionEvent event) {
907         // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing
908         // touch exploration.
909         if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)
910                 && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
911             return false;
912         }
913         final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK;
914         return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0;
915     }
916 
setCombinedGenericMotionEventSources(int sources)917     public void setCombinedGenericMotionEventSources(int sources) {
918         mCombinedGenericMotionEventSources = sources;
919     }
920 
921     /**
922      * Keeps state of streams of events from all keyboard devices.
923      */
924     private static class KeyboardEventStreamState extends EventStreamState {
925         private SparseBooleanArray mEventSequenceStartedMap = new SparseBooleanArray();
926 
KeyboardEventStreamState()927         public KeyboardEventStreamState() {
928             reset();
929         }
930 
931         @Override
reset()932         final public void reset() {
933             super.reset();
934             mEventSequenceStartedMap.clear();
935         }
936 
937         /*
938          * Key events from different devices may be interleaved. For example, the volume up and
939          * down keys can come from different input sources.
940          */
941         @Override
updateInputSource(int deviceId)942         public boolean updateInputSource(int deviceId) {
943             return false;
944         }
945 
946         // We manage all input source simultaneously; there is no concept of validity.
947         @Override
inputSourceValid()948         public boolean inputSourceValid() {
949             return true;
950         }
951 
952         @Override
shouldProcessKeyEvent(KeyEvent event)953         final public boolean shouldProcessKeyEvent(KeyEvent event) {
954             // For each keyboard device, wait for a down event from a device to start processing
955             int deviceId = event.getDeviceId();
956             if (mEventSequenceStartedMap.get(deviceId, false)) {
957                 return true;
958             }
959             boolean shouldProcess = event.getAction() == KeyEvent.ACTION_DOWN;
960             mEventSequenceStartedMap.put(deviceId, shouldProcess);
961             return shouldProcess;
962         }
963     }
964 
setGestureDetectionPassthroughRegion(int displayId, Region region)965     public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
966         if (region != null && mTouchExplorer.contains(displayId)) {
967             mTouchExplorer.get(displayId).setGestureDetectionPassthroughRegion(region);
968         }
969     }
970 
setTouchExplorationPassthroughRegion(int displayId, Region region)971     public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
972         if (region != null && mTouchExplorer.contains(displayId)) {
973             mTouchExplorer.get(displayId).setTouchExplorationPassthroughRegion(region);
974         }
975     }
976 
setServiceDetectsGesturesEnabled(int displayId, boolean mode)977     public void setServiceDetectsGesturesEnabled(int displayId, boolean mode) {
978         if (mTouchExplorer.contains(displayId)) {
979             mTouchExplorer.get(displayId).setServiceDetectsGestures(mode);
980         }
981         mServiceDetectsGestures.put(displayId, mode);
982     }
983 
resetServiceDetectsGestures()984     public void resetServiceDetectsGestures() {
985         mServiceDetectsGestures.clear();
986     }
987 
requestTouchExploration(int displayId)988     public void requestTouchExploration(int displayId) {
989         if (mTouchExplorer.contains(displayId)) {
990             mTouchExplorer.get(displayId).requestTouchExploration();
991         }
992     }
993 
requestDragging(int displayId, int pointerId)994     public void requestDragging(int displayId, int pointerId) {
995         if (mTouchExplorer.contains(displayId)) {
996             mTouchExplorer.get(displayId).requestDragging(pointerId);
997         }
998     }
999 
requestDelegating(int displayId)1000     public void requestDelegating(int displayId) {
1001         if (mTouchExplorer.contains(displayId)) {
1002             mTouchExplorer.get(displayId).requestDelegating();
1003         }
1004     }
1005 
onDoubleTap(int displayId)1006     public void onDoubleTap(int displayId) {
1007         if (mTouchExplorer.contains(displayId)) {
1008             mTouchExplorer.get(displayId).onDoubleTap();
1009         }
1010     }
1011 
onDoubleTapAndHold(int displayId)1012     public void onDoubleTapAndHold(int displayId) {
1013         if (mTouchExplorer.contains(displayId)) {
1014             mTouchExplorer.get(displayId).onDoubleTapAndHold();
1015         }
1016     }
1017 
1018     /**
1019      * Dumps all {@link AccessibilityInputFilter}s here.
1020      */
dump(FileDescriptor fd, final PrintWriter pw, String[] args)1021     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
1022         if (mEventHandler == null) {
1023             return;
1024         }
1025         pw.append("A11yInputFilter Info : ");
1026         pw.println();
1027 
1028         final ArrayList<Display> displaysList = mAms.getValidDisplayList();
1029         for (int i = 0; i < displaysList.size(); i++) {
1030             final int displayId = displaysList.get(i).getDisplayId();
1031             EventStreamTransformation next = mEventHandler.get(displayId);
1032             if (next != null) {
1033                 pw.append("Enabled features of Display [");
1034                 pw.append(Integer.toString(displayId));
1035                 pw.append("] = ");
1036 
1037                 final StringJoiner joiner = new StringJoiner(",", "[", "]");
1038 
1039                 while (next != null) {
1040                     if (next instanceof MagnificationGestureHandler) {
1041                         joiner.add("MagnificationGesture");
1042                     } else if (next instanceof KeyboardInterceptor) {
1043                         joiner.add("KeyboardInterceptor");
1044                     } else if (next instanceof TouchExplorer) {
1045                         joiner.add("TouchExplorer");
1046                     } else if (next instanceof AutoclickController) {
1047                         joiner.add("AutoclickController");
1048                     } else if (next instanceof MotionEventInjector) {
1049                         joiner.add("MotionEventInjector");
1050                     }
1051                     next = next.getNext();
1052                 }
1053                 pw.append(joiner.toString());
1054             }
1055             pw.println();
1056         }
1057     }
1058 }
1059