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     static final int FEATURES_AFFECTING_MOTION_EVENTS =
145             FLAG_FEATURE_INJECT_MOTION_EVENTS
146                     | FLAG_FEATURE_AUTOCLICK
147                     | FLAG_FEATURE_TOUCH_EXPLORATION
148                     | FLAG_FEATURE_SCREEN_MAGNIFIER
149                     | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER
150                     | FLAG_SERVICE_HANDLES_DOUBLE_TAP
151                     | FLAG_REQUEST_MULTI_FINGER_GESTURES
152                     | FLAG_REQUEST_2_FINGER_PASSTHROUGH;
153 
154     private final Context mContext;
155 
156     private final PowerManager mPm;
157 
158     private final AccessibilityManagerService mAms;
159 
160     private final SparseArray<EventStreamTransformation> mEventHandler;
161 
162     private final SparseArray<TouchExplorer> mTouchExplorer = new SparseArray<>(0);
163 
164     private final SparseArray<MagnificationGestureHandler> mMagnificationGestureHandler =
165             new SparseArray<>(0);
166 
167     private final SparseArray<MotionEventInjector> mMotionEventInjectors = new SparseArray<>(0);
168 
169     private AutoclickController mAutoclickController;
170 
171     private KeyboardInterceptor mKeyboardInterceptor;
172 
173     private boolean mInstalled;
174 
175     private int mUserId;
176 
177     private int mEnabledFeatures;
178 
179     private final SparseArray<EventStreamState> mMouseStreamStates = new SparseArray<>(0);
180 
181     private final SparseArray<EventStreamState> mTouchScreenStreamStates = new SparseArray<>(0);
182 
183     private EventStreamState mKeyboardStreamState;
184 
AccessibilityInputFilter(Context context, AccessibilityManagerService service)185     AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
186         this(context, service, new SparseArray<>(0));
187     }
188 
AccessibilityInputFilter(Context context, AccessibilityManagerService service, SparseArray<EventStreamTransformation> eventHandler)189     AccessibilityInputFilter(Context context, AccessibilityManagerService service,
190             SparseArray<EventStreamTransformation> eventHandler) {
191         super(context.getMainLooper());
192         mContext = context;
193         mAms = service;
194         mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
195         mEventHandler = eventHandler;
196     }
197 
198     @Override
onInstalled()199     public void onInstalled() {
200         if (DEBUG) {
201             Slog.d(TAG, "Accessibility input filter installed.");
202         }
203         mInstalled = true;
204         disableFeatures();
205         enableFeatures();
206         super.onInstalled();
207     }
208 
209     @Override
onUninstalled()210     public void onUninstalled() {
211         if (DEBUG) {
212             Slog.d(TAG, "Accessibility input filter uninstalled.");
213         }
214         mInstalled = false;
215         disableFeatures();
216         super.onUninstalled();
217     }
218 
onDisplayAdded(@onNull Display display)219     void onDisplayAdded(@NonNull Display display) {
220         if (mInstalled) {
221             resetStreamStateForDisplay(display.getDisplayId());
222             enableFeaturesForDisplay(display);
223         }
224     }
225 
onDisplayRemoved(int displayId)226     void onDisplayRemoved(int displayId) {
227         if (mInstalled) {
228             disableFeaturesForDisplay(displayId);
229             resetStreamStateForDisplay(displayId);
230         }
231     }
232 
233     @Override
onInputEvent(InputEvent event, int policyFlags)234     public void onInputEvent(InputEvent event, int policyFlags) {
235         if (DEBUG) {
236             Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
237                     + Integer.toHexString(policyFlags));
238         }
239         if (mAms.getTraceManager().isA11yTracingEnabledForTypes(
240                 AccessibilityTrace.FLAGS_INPUT_FILTER)) {
241             mAms.getTraceManager().logTrace(TAG + ".onInputEvent",
242                     AccessibilityTrace.FLAGS_INPUT_FILTER,
243                     "event=" + event + ";policyFlags=" + policyFlags);
244         }
245         if (mEventHandler.size() == 0) {
246             if (DEBUG) Slog.d(TAG, "No mEventHandler for event " + event);
247             super.onInputEvent(event, policyFlags);
248             return;
249         }
250 
251         EventStreamState state = getEventStreamState(event);
252         if (state == null) {
253             super.onInputEvent(event, policyFlags);
254             return;
255         }
256 
257         final int eventSource = event.getSource();
258         final int displayId = event.getDisplayId();
259         if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
260             state.reset();
261             clearEventStreamHandler(displayId, eventSource);
262             super.onInputEvent(event, policyFlags);
263             return;
264         }
265 
266         if (state.updateInputSource(event.getSource())) {
267             clearEventStreamHandler(displayId, eventSource);
268         }
269 
270         if (!state.inputSourceValid()) {
271             super.onInputEvent(event, policyFlags);
272             return;
273         }
274 
275         if (event instanceof MotionEvent) {
276             if ((mEnabledFeatures & FEATURES_AFFECTING_MOTION_EVENTS) != 0) {
277                 MotionEvent motionEvent = (MotionEvent) event;
278                 processMotionEvent(state, motionEvent, policyFlags);
279                 return;
280             } else {
281                 super.onInputEvent(event, policyFlags);
282             }
283         } else if (event instanceof KeyEvent) {
284             KeyEvent keyEvent = (KeyEvent) event;
285             processKeyEvent(state, keyEvent, policyFlags);
286         }
287     }
288 
289     /**
290      * Gets current event stream state associated with an input event.
291      * @return The event stream state that should be used for the event. Null if the event should
292      *     not be handled by #AccessibilityInputFilter.
293      */
getEventStreamState(InputEvent event)294     private EventStreamState getEventStreamState(InputEvent event) {
295         if (event instanceof MotionEvent) {
296             final int displayId = event.getDisplayId();
297 
298             if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
299                 EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId);
300                 if (touchScreenStreamState == null) {
301                     touchScreenStreamState = new TouchScreenEventStreamState();
302                     mTouchScreenStreamStates.put(displayId, touchScreenStreamState);
303                 }
304                 return touchScreenStreamState;
305             }
306             if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
307                 EventStreamState mouseStreamState = mMouseStreamStates.get(displayId);
308                 if (mouseStreamState == null) {
309                     mouseStreamState = new MouseEventStreamState();
310                     mMouseStreamStates.put(displayId, mouseStreamState);
311                 }
312                 return mouseStreamState;
313             }
314         } else if (event instanceof KeyEvent) {
315             if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
316                 if (mKeyboardStreamState == null) {
317                     mKeyboardStreamState = new KeyboardEventStreamState();
318                 }
319                 return mKeyboardStreamState;
320             }
321         }
322         return null;
323     }
324 
clearEventStreamHandler(int displayId, int eventSource)325     private void clearEventStreamHandler(int displayId, int eventSource) {
326         final EventStreamTransformation eventHandler = mEventHandler.get(displayId);
327         if (eventHandler != null) {
328             eventHandler.clearEvents(eventSource);
329         }
330     }
331 
processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags)332     private void processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags) {
333         if (!state.shouldProcessScroll() && event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
334             super.onInputEvent(event, policyFlags);
335             return;
336         }
337 
338         if (!state.shouldProcessMotionEvent(event)) {
339             return;
340         }
341 
342         handleMotionEvent(event, policyFlags);
343     }
344 
processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags)345     private void processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags) {
346         if (!state.shouldProcessKeyEvent(event)) {
347             super.onInputEvent(event, policyFlags);
348             return;
349         }
350         // Since the display id of KeyEvent always would be -1 and there is only one
351         // KeyboardInterceptor for all display, pass KeyEvent to the mEventHandler of
352         // DEFAULT_DISPLAY to handle.
353         mEventHandler.get(Display.DEFAULT_DISPLAY).onKeyEvent(event, policyFlags);
354     }
355 
handleMotionEvent(MotionEvent event, int policyFlags)356     private void handleMotionEvent(MotionEvent event, int policyFlags) {
357         if (DEBUG) {
358             Slog.i(TAG, "Handling motion event: " + event + ", policyFlags: " + policyFlags);
359         }
360         mPm.userActivity(event.getEventTime(), false);
361         MotionEvent transformedEvent = MotionEvent.obtain(event);
362         final int displayId = event.getDisplayId();
363         mEventHandler.get(isDisplayIdValid(displayId) ? displayId : Display.DEFAULT_DISPLAY)
364                 .onMotionEvent(transformedEvent, event, policyFlags);
365         transformedEvent.recycle();
366     }
367 
isDisplayIdValid(int displayId)368     private boolean isDisplayIdValid(int displayId) {
369         return mEventHandler.get(displayId) != null;
370     }
371 
372     @Override
onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent, int policyFlags)373     public void onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent,
374             int policyFlags) {
375         sendInputEvent(transformedEvent, policyFlags);
376     }
377 
378     @Override
onKeyEvent(KeyEvent event, int policyFlags)379     public void onKeyEvent(KeyEvent event, int policyFlags) {
380         sendInputEvent(event, policyFlags);
381     }
382 
383     @Override
onAccessibilityEvent(AccessibilityEvent event)384     public void onAccessibilityEvent(AccessibilityEvent event) {
385         // TODO Implement this to inject the accessibility event
386         //      into the accessibility manager service similarly
387         //      to how this is done for input events.
388     }
389 
390     @Override
setNext(EventStreamTransformation sink)391     public void setNext(EventStreamTransformation sink) {
392         /* do nothing */
393     }
394 
395     @Override
getNext()396     public EventStreamTransformation getNext() {
397         return null;
398     }
399 
400     @Override
clearEvents(int inputSource)401     public void clearEvents(int inputSource) {
402         /* do nothing */
403     }
404 
setUserAndEnabledFeatures(int userId, int enabledFeatures)405     void setUserAndEnabledFeatures(int userId, int enabledFeatures) {
406         if (DEBUG) {
407             Slog.i(TAG, "setUserAndEnabledFeatures(userId = " + userId + ", enabledFeatures = 0x"
408                     + Integer.toHexString(enabledFeatures) + ")");
409         }
410         if (mEnabledFeatures == enabledFeatures && mUserId == userId) {
411             return;
412         }
413         if (mInstalled) {
414             disableFeatures();
415         }
416         mUserId = userId;
417         mEnabledFeatures = enabledFeatures;
418         if (mInstalled) {
419             enableFeatures();
420         }
421     }
422 
notifyAccessibilityEvent(AccessibilityEvent event)423     void notifyAccessibilityEvent(AccessibilityEvent event) {
424         for (int i = 0; i < mEventHandler.size(); i++) {
425             final EventStreamTransformation eventHandler = mEventHandler.valueAt(i);
426             if (eventHandler != null) {
427                 eventHandler.onAccessibilityEvent(event);
428             }
429         }
430     }
431 
notifyAccessibilityButtonClicked(int displayId)432     void notifyAccessibilityButtonClicked(int displayId) {
433         if (mMagnificationGestureHandler.size() != 0) {
434             final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId);
435             if (handler != null) {
436                 handler.notifyShortcutTriggered();
437             }
438         }
439     }
440 
enableFeatures()441     private void enableFeatures() {
442         if (DEBUG) Slog.i(TAG, "enableFeatures()");
443 
444         resetAllStreamState();
445 
446         final ArrayList<Display> displaysList = mAms.getValidDisplayList();
447 
448         for (int i = displaysList.size() - 1; i >= 0; i--) {
449             enableFeaturesForDisplay(displaysList.get(i));
450         }
451         enableDisplayIndependentFeatures();
452     }
453 
enableFeaturesForDisplay(Display display)454     private void enableFeaturesForDisplay(Display display) {
455         if (DEBUG) {
456             Slog.i(TAG, "enableFeaturesForDisplay() : display Id = " + display.getDisplayId());
457         }
458 
459         final Context displayContext = mContext.createDisplayContext(display);
460         final int displayId = display.getDisplayId();
461 
462         if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
463             if (mAutoclickController == null) {
464                 mAutoclickController = new AutoclickController(
465                         mContext, mUserId, mAms.getTraceManager());
466             }
467             addFirstEventHandler(displayId, mAutoclickController);
468         }
469 
470         if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
471             TouchExplorer explorer = new TouchExplorer(displayContext, mAms);
472             if ((mEnabledFeatures & FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0) {
473                 explorer.setServiceHandlesDoubleTap(true);
474             }
475             if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) {
476                 explorer.setMultiFingerGesturesEnabled(true);
477             }
478             if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) {
479                 explorer.setTwoFingerPassthroughEnabled(true);
480             }
481             if ((mEnabledFeatures & FLAG_SEND_MOTION_EVENTS) != 0) {
482                 explorer.setSendMotionEventsEnabled(true);
483             }
484             addFirstEventHandler(displayId, explorer);
485             mTouchExplorer.put(displayId, explorer);
486         }
487 
488         if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
489                 || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
490                 || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
491             final MagnificationGestureHandler magnificationGestureHandler =
492                     createMagnificationGestureHandler(displayId,
493                             displayContext);
494             addFirstEventHandler(displayId, magnificationGestureHandler);
495             mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
496         }
497 
498         if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
499             MotionEventInjector injector = new MotionEventInjector(
500                     mContext.getMainLooper(), mAms.getTraceManager());
501             addFirstEventHandler(displayId, injector);
502             mMotionEventInjectors.put(displayId, injector);
503         }
504     }
505 
enableDisplayIndependentFeatures()506     private void enableDisplayIndependentFeatures() {
507         if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
508             mAms.setMotionEventInjectors(mMotionEventInjectors);
509         }
510 
511         if ((mEnabledFeatures & FLAG_FEATURE_FILTER_KEY_EVENTS) != 0) {
512             mKeyboardInterceptor = new KeyboardInterceptor(mAms,
513                     LocalServices.getService(WindowManagerPolicy.class));
514             // Since the display id of KeyEvent always would be -1 and it would be dispatched to
515             // the display with input focus directly, we only need one KeyboardInterceptor for
516             // default display.
517             addFirstEventHandler(Display.DEFAULT_DISPLAY, mKeyboardInterceptor);
518         }
519     }
520 
521     /**
522      * Adds an event handler to the event handler chain for giving display. The handler is added at
523      * the beginning of the chain.
524      *
525      * @param displayId The logical display id.
526      * @param handler The handler to be added to the event handlers list.
527      */
addFirstEventHandler(int displayId, EventStreamTransformation handler)528     private void addFirstEventHandler(int displayId, EventStreamTransformation handler) {
529         EventStreamTransformation eventHandler = mEventHandler.get(displayId);
530         if (eventHandler != null) {
531             handler.setNext(eventHandler);
532         } else {
533             handler.setNext(this);
534         }
535         eventHandler = handler;
536         mEventHandler.put(displayId, eventHandler);
537     }
538 
disableFeatures()539     private void disableFeatures() {
540         final ArrayList<Display> displaysList = mAms.getValidDisplayList();
541 
542         for (int i = displaysList.size() - 1; i >= 0; i--) {
543             disableFeaturesForDisplay(displaysList.get(i).getDisplayId());
544         }
545         mAms.setMotionEventInjectors(null);
546         disableDisplayIndependentFeatures();
547 
548         resetAllStreamState();
549     }
550 
disableFeaturesForDisplay(int displayId)551     private void disableFeaturesForDisplay(int displayId) {
552         if (DEBUG) {
553             Slog.i(TAG, "disableFeaturesForDisplay() : display Id = " + displayId);
554         }
555 
556         final MotionEventInjector injector = mMotionEventInjectors.get(displayId);
557         if (injector != null) {
558             injector.onDestroy();
559             mMotionEventInjectors.remove(displayId);
560         }
561 
562         final TouchExplorer explorer = mTouchExplorer.get(displayId);
563         if (explorer != null) {
564             explorer.onDestroy();
565             mTouchExplorer.remove(displayId);
566         }
567 
568         final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId);
569         if (handler != null) {
570             handler.onDestroy();
571             mMagnificationGestureHandler.remove(displayId);
572         }
573 
574         final EventStreamTransformation eventStreamTransformation = mEventHandler.get(displayId);
575         if (eventStreamTransformation != null) {
576             mEventHandler.remove(displayId);
577         }
578     }
579 
disableDisplayIndependentFeatures()580     private void disableDisplayIndependentFeatures() {
581         if (mAutoclickController != null) {
582             mAutoclickController.onDestroy();
583             mAutoclickController = null;
584         }
585 
586         if (mKeyboardInterceptor != null) {
587             mKeyboardInterceptor.onDestroy();
588             mKeyboardInterceptor = null;
589         }
590     }
591 
createMagnificationGestureHandler( int displayId, Context displayContext)592     private MagnificationGestureHandler createMagnificationGestureHandler(
593             int displayId, Context displayContext) {
594         final boolean detectControlGestures = (mEnabledFeatures
595                 & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0;
596         final boolean triggerable = (mEnabledFeatures
597                 & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0;
598         MagnificationGestureHandler magnificationGestureHandler;
599         if (mAms.getMagnificationMode(displayId)
600                 == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
601             final Context uiContext = displayContext.createWindowContext(
602                     TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
603             magnificationGestureHandler = new WindowMagnificationGestureHandler(uiContext,
604                     mAms.getWindowMagnificationMgr(), mAms.getTraceManager(),
605                     mAms.getMagnificationController(), detectControlGestures, triggerable,
606                     displayId);
607         } else {
608             final Context uiContext = displayContext.createWindowContext(
609                     TYPE_MAGNIFICATION_OVERLAY, null /* options */);
610             magnificationGestureHandler = new FullScreenMagnificationGestureHandler(uiContext,
611                     mAms.getFullScreenMagnificationController(), mAms.getTraceManager(),
612                     mAms.getMagnificationController(), detectControlGestures, triggerable,
613                     new WindowMagnificationPromptController(displayContext, mUserId), displayId);
614         }
615         return magnificationGestureHandler;
616     }
617 
resetAllStreamState()618     void resetAllStreamState() {
619         final ArrayList<Display> displaysList = mAms.getValidDisplayList();
620 
621         for (int i = displaysList.size() - 1; i >= 0; i--) {
622             resetStreamStateForDisplay(displaysList.get(i).getDisplayId());
623         }
624 
625         if (mKeyboardStreamState != null) {
626             mKeyboardStreamState.reset();
627         }
628     }
629 
resetStreamStateForDisplay(int displayId)630     void resetStreamStateForDisplay(int displayId) {
631         final EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId);
632         if (touchScreenStreamState != null) {
633             touchScreenStreamState.reset();
634             mTouchScreenStreamStates.remove(displayId);
635         }
636 
637         final EventStreamState mouseStreamState = mMouseStreamStates.get(displayId);
638         if (mouseStreamState != null) {
639             mouseStreamState.reset();
640             mMouseStreamStates.remove(displayId);
641         }
642     }
643 
644     @Override
onDestroy()645     public void onDestroy() {
646         /* ignore */
647     }
648 
649     /**
650      * Called to refresh the magnification mode on the given display.
651      * It's responsible for changing {@link MagnificationGestureHandler} based on the current mode.
652      *
653      * @param display The logical display
654      */
655     @MainThread
refreshMagnificationMode(Display display)656     public void refreshMagnificationMode(Display display) {
657         final int displayId = display.getDisplayId();
658         final MagnificationGestureHandler magnificationGestureHandler =
659                 mMagnificationGestureHandler.get(displayId);
660         if (magnificationGestureHandler == null) {
661             return;
662         }
663         if (magnificationGestureHandler.getMode() == mAms.getMagnificationMode(displayId)) {
664             return;
665         }
666         magnificationGestureHandler.onDestroy();
667         final MagnificationGestureHandler currentMagnificationGestureHandler =
668                 createMagnificationGestureHandler(displayId,
669                         mContext.createDisplayContext(display));
670         switchEventStreamTransformation(displayId, magnificationGestureHandler,
671                 currentMagnificationGestureHandler);
672         mMagnificationGestureHandler.put(displayId, currentMagnificationGestureHandler);
673     }
674 
675     @MainThread
switchEventStreamTransformation(int displayId, EventStreamTransformation oldStreamTransformation, EventStreamTransformation currentStreamTransformation)676     private void switchEventStreamTransformation(int displayId,
677             EventStreamTransformation oldStreamTransformation,
678             EventStreamTransformation currentStreamTransformation) {
679         EventStreamTransformation eventStreamTransformation = mEventHandler.get(displayId);
680         if (eventStreamTransformation == null) {
681             return;
682         }
683         if (eventStreamTransformation == oldStreamTransformation) {
684             currentStreamTransformation.setNext(oldStreamTransformation.getNext());
685             mEventHandler.put(displayId, currentStreamTransformation);
686         } else {
687             while (eventStreamTransformation != null) {
688                 if (eventStreamTransformation.getNext() == oldStreamTransformation) {
689                     eventStreamTransformation.setNext(currentStreamTransformation);
690                     currentStreamTransformation.setNext(oldStreamTransformation.getNext());
691                     return;
692                 } else {
693                     eventStreamTransformation = eventStreamTransformation.getNext();
694                 }
695             }
696         }
697     }
698 
699     /**
700      * Keeps state of event streams observed for an input device with a certain source.
701      * Provides information about whether motion and key events should be processed by accessibility
702      * #EventStreamTransformations. Base implementation describes behaviour for event sources that
703      * whose events should not be handled by a11y event stream transformations.
704      */
705     private static class EventStreamState {
706         private int mSource;
707 
EventStreamState()708         EventStreamState() {
709             mSource = -1;
710         }
711 
712         /**
713          * Updates the input source of the device associated with the state. If the source changes,
714          * resets internal state.
715          *
716          * @param source Updated input source.
717          * @return Whether the input source has changed.
718          */
updateInputSource(int source)719         public boolean updateInputSource(int source) {
720             if (mSource == source) {
721                 return false;
722             }
723             // Reset clears internal state, so make sure it's called before |mSource| is updated.
724             reset();
725             mSource = source;
726             return true;
727         }
728 
729         /**
730          * @return Whether input source is valid.
731          */
inputSourceValid()732         public boolean inputSourceValid() {
733             return mSource >= 0;
734         }
735 
736         /**
737          * Resets the event stream state.
738          */
reset()739         public void reset() {
740             mSource = -1;
741         }
742 
743         /**
744          * @return Whether scroll events for device should be handled by event transformations.
745          */
shouldProcessScroll()746         public boolean shouldProcessScroll() {
747             return false;
748         }
749 
750         /**
751          * @param event An observed motion event.
752          * @return Whether the event should be handled by event transformations.
753          */
shouldProcessMotionEvent(MotionEvent event)754         public boolean shouldProcessMotionEvent(MotionEvent event) {
755             return false;
756         }
757 
758         /**
759          * @param event An observed key event.
760          * @return Whether the event should be handled by event transformations.
761          */
shouldProcessKeyEvent(KeyEvent event)762         public boolean shouldProcessKeyEvent(KeyEvent event) {
763             return false;
764         }
765     }
766 
767     /**
768      * Keeps state of stream of events from a mouse device.
769      */
770     private static class MouseEventStreamState extends EventStreamState {
771         private boolean mMotionSequenceStarted;
772 
MouseEventStreamState()773         public MouseEventStreamState() {
774             reset();
775         }
776 
777         @Override
reset()778         final public void reset() {
779             super.reset();
780             mMotionSequenceStarted = false;
781         }
782 
783         @Override
shouldProcessScroll()784         final public boolean shouldProcessScroll() {
785             return true;
786         }
787 
788         @Override
shouldProcessMotionEvent(MotionEvent event)789         final public boolean shouldProcessMotionEvent(MotionEvent event) {
790             if (mMotionSequenceStarted) {
791                 return true;
792             }
793             // Wait for down or move event to start processing mouse events.
794             int action = event.getActionMasked();
795             mMotionSequenceStarted =
796                     action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_HOVER_MOVE;
797             return mMotionSequenceStarted;
798         }
799     }
800 
801     /**
802      * Keeps state of stream of events from a touch screen device.
803      */
804     private static class TouchScreenEventStreamState extends EventStreamState {
805         private boolean mTouchSequenceStarted;
806         private boolean mHoverSequenceStarted;
807 
TouchScreenEventStreamState()808         public TouchScreenEventStreamState() {
809             reset();
810         }
811 
812         @Override
reset()813         final public void reset() {
814             super.reset();
815             mTouchSequenceStarted = false;
816             mHoverSequenceStarted = false;
817         }
818 
819         @Override
shouldProcessMotionEvent(MotionEvent event)820         final public boolean shouldProcessMotionEvent(MotionEvent event) {
821             // Wait for a down touch event to start processing.
822             if (event.isTouchEvent()) {
823                 if (mTouchSequenceStarted) {
824                     return true;
825                 }
826                 mTouchSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_DOWN;
827                 return mTouchSequenceStarted;
828             }
829 
830             // Wait for an enter hover event to start processing.
831             if (mHoverSequenceStarted) {
832                 return true;
833             }
834             mHoverSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER;
835             return mHoverSequenceStarted;
836         }
837     }
838 
839     /**
840      * Keeps state of streams of events from all keyboard devices.
841      */
842     private static class KeyboardEventStreamState extends EventStreamState {
843         private SparseBooleanArray mEventSequenceStartedMap = new SparseBooleanArray();
844 
KeyboardEventStreamState()845         public KeyboardEventStreamState() {
846             reset();
847         }
848 
849         @Override
reset()850         final public void reset() {
851             super.reset();
852             mEventSequenceStartedMap.clear();
853         }
854 
855         /*
856          * Key events from different devices may be interleaved. For example, the volume up and
857          * down keys can come from different input sources.
858          */
859         @Override
updateInputSource(int deviceId)860         public boolean updateInputSource(int deviceId) {
861             return false;
862         }
863 
864         // We manage all input source simultaneously; there is no concept of validity.
865         @Override
inputSourceValid()866         public boolean inputSourceValid() {
867             return true;
868         }
869 
870         @Override
shouldProcessKeyEvent(KeyEvent event)871         final public boolean shouldProcessKeyEvent(KeyEvent event) {
872             // For each keyboard device, wait for a down event from a device to start processing
873             int deviceId = event.getDeviceId();
874             if (mEventSequenceStartedMap.get(deviceId, false)) {
875                 return true;
876             }
877             boolean shouldProcess = event.getAction() == KeyEvent.ACTION_DOWN;
878             mEventSequenceStartedMap.put(deviceId, shouldProcess);
879             return shouldProcess;
880         }
881     }
882 
setGestureDetectionPassthroughRegion(int displayId, Region region)883     public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
884         if (region != null && mTouchExplorer.contains(displayId)) {
885             mTouchExplorer.get(displayId).setGestureDetectionPassthroughRegion(region);
886         }
887     }
888 
setTouchExplorationPassthroughRegion(int displayId, Region region)889     public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
890         if (region != null && mTouchExplorer.contains(displayId)) {
891             mTouchExplorer.get(displayId).setTouchExplorationPassthroughRegion(region);
892         }
893     }
894 
895     /**
896      * Dumps all {@link AccessibilityInputFilter}s here.
897      */
dump(FileDescriptor fd, final PrintWriter pw, String[] args)898     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
899         if (mEventHandler == null) {
900             return;
901         }
902         pw.append("A11yInputFilter Info : ");
903         pw.println();
904 
905         final ArrayList<Display> displaysList = mAms.getValidDisplayList();
906         for (int i = 0; i < displaysList.size(); i++) {
907             final int displayId = displaysList.get(i).getDisplayId();
908             EventStreamTransformation next = mEventHandler.get(displayId);
909             if (next != null) {
910                 pw.append("Enabled features of Display [");
911                 pw.append(Integer.toString(displayId));
912                 pw.append("] = ");
913 
914                 final StringJoiner joiner = new StringJoiner(",", "[", "]");
915 
916                 while (next != null) {
917                     if (next instanceof MagnificationGestureHandler) {
918                         joiner.add("MagnificationGesture");
919                     } else if (next instanceof KeyboardInterceptor) {
920                         joiner.add("KeyboardInterceptor");
921                     } else if (next instanceof TouchExplorer) {
922                         joiner.add("TouchExplorer");
923                     } else if (next instanceof AutoclickController) {
924                         joiner.add("AutoclickController");
925                     } else if (next instanceof MotionEventInjector) {
926                         joiner.add("MotionEventInjector");
927                     }
928                     next = next.getNext();
929                 }
930                 pw.append(joiner.toString());
931             }
932             pw.println();
933         }
934     }
935 }
936