• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.MainThread;
23 import android.content.Context;
24 import android.graphics.Region;
25 import android.os.PowerManager;
26 import android.provider.Settings;
27 import android.util.Slog;
28 import android.util.SparseArray;
29 import android.util.SparseBooleanArray;
30 import android.view.Display;
31 import android.view.InputDevice;
32 import android.view.InputEvent;
33 import android.view.InputFilter;
34 import android.view.KeyEvent;
35 import android.view.MotionEvent;
36 import android.view.accessibility.AccessibilityEvent;
37 
38 import com.android.server.LocalServices;
39 import com.android.server.accessibility.gestures.TouchExplorer;
40 import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler;
41 import com.android.server.accessibility.magnification.MagnificationGestureHandler;
42 import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler;
43 import com.android.server.accessibility.magnification.WindowMagnificationPromptController;
44 import com.android.server.policy.WindowManagerPolicy;
45 
46 import java.util.ArrayList;
47 
48 /**
49  * This class is an input filter for implementing accessibility features such
50  * as display magnification and explore by touch.
51  *
52  * NOTE: This class has to be created and poked only from the main thread.
53  */
54 class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation {
55 
56     private static final String TAG = AccessibilityInputFilter.class.getSimpleName();
57 
58     private static final boolean DEBUG = false;
59 
60     /**
61      * Flag for enabling the screen magnification feature.
62      *
63      * @see #setUserAndEnabledFeatures(int, int)
64      */
65     static final int FLAG_FEATURE_SCREEN_MAGNIFIER = 0x00000001;
66 
67     /**
68      * Flag for enabling the touch exploration feature.
69      *
70      * @see #setUserAndEnabledFeatures(int, int)
71      */
72     static final int FLAG_FEATURE_TOUCH_EXPLORATION = 0x00000002;
73 
74     /**
75      * Flag for enabling the filtering key events feature.
76      *
77      * @see #setUserAndEnabledFeatures(int, int)
78      */
79     static final int FLAG_FEATURE_FILTER_KEY_EVENTS = 0x00000004;
80 
81     /**
82      * Flag for enabling "Automatically click on mouse stop" feature.
83      *
84      * @see #setUserAndEnabledFeatures(int, int)
85      */
86     static final int FLAG_FEATURE_AUTOCLICK = 0x00000008;
87 
88     /**
89      * Flag for enabling motion event injection.
90      *
91      * @see #setUserAndEnabledFeatures(int, int)
92      */
93     static final int FLAG_FEATURE_INJECT_MOTION_EVENTS = 0x00000010;
94 
95     /**
96      * Flag for enabling the feature to control the screen magnifier. If
97      * {@link #FLAG_FEATURE_SCREEN_MAGNIFIER} is set this flag is ignored
98      * as the screen magnifier feature performs a super set of the work
99      * performed by this feature.
100      *
101      * @see #setUserAndEnabledFeatures(int, int)
102      */
103     static final int FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER = 0x00000020;
104 
105     /**
106      * Flag for enabling the feature to trigger the screen magnifier
107      * from another on-device interaction.
108      */
109     static final int FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER = 0x00000040;
110 
111     /**
112      * Flag for dispatching double tap and double tap and hold to the service.
113      *
114      * @see #setUserAndEnabledFeatures(int, int)
115      */
116     static final int FLAG_SERVICE_HANDLES_DOUBLE_TAP = 0x00000080;
117 
118 /**
119      * Flag for enabling multi-finger gestures.
120      *
121      * @see #setUserAndEnabledFeatures(int, int)
122      */
123     static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000100;
124 
125     /**
126      * Flag for enabling two-finger passthrough when multi-finger gestures are enabled.
127      *
128      * @see #setUserAndEnabledFeatures(int, int)
129      */
130     static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x00000200;
131 
132     /**
133      * Flag for including motion events when dispatching a gesture.
134      *
135      * @see #setUserAndEnabledFeatures(int, int)
136      */
137     static final int FLAG_SEND_MOTION_EVENTS = 0x00000400;
138 
139     static final int FEATURES_AFFECTING_MOTION_EVENTS =
140             FLAG_FEATURE_INJECT_MOTION_EVENTS
141                     | FLAG_FEATURE_AUTOCLICK
142                     | FLAG_FEATURE_TOUCH_EXPLORATION
143                     | FLAG_FEATURE_SCREEN_MAGNIFIER
144                     | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER
145                     | FLAG_SERVICE_HANDLES_DOUBLE_TAP
146                     | FLAG_REQUEST_MULTI_FINGER_GESTURES
147                     | FLAG_REQUEST_2_FINGER_PASSTHROUGH;
148 
149     private final Context mContext;
150 
151     private final PowerManager mPm;
152 
153     private final AccessibilityManagerService mAms;
154 
155     private final SparseArray<EventStreamTransformation> mEventHandler;
156 
157     private final SparseArray<TouchExplorer> mTouchExplorer = new SparseArray<>(0);
158 
159     private final SparseArray<MagnificationGestureHandler> mMagnificationGestureHandler =
160             new SparseArray<>(0);
161 
162     private final SparseArray<MotionEventInjector> mMotionEventInjectors = new SparseArray<>(0);
163 
164     private AutoclickController mAutoclickController;
165 
166     private KeyboardInterceptor mKeyboardInterceptor;
167 
168     private boolean mInstalled;
169 
170     private int mUserId;
171 
172     private int mEnabledFeatures;
173 
174     private EventStreamState mMouseStreamState;
175 
176     private EventStreamState mTouchScreenStreamState;
177 
178     private EventStreamState mKeyboardStreamState;
179 
AccessibilityInputFilter(Context context, AccessibilityManagerService service)180     AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
181         this(context, service, new SparseArray<>(0));
182     }
183 
AccessibilityInputFilter(Context context, AccessibilityManagerService service, SparseArray<EventStreamTransformation> eventHandler)184     AccessibilityInputFilter(Context context, AccessibilityManagerService service,
185             SparseArray<EventStreamTransformation> eventHandler) {
186         super(context.getMainLooper());
187         mContext = context;
188         mAms = service;
189         mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
190         mEventHandler = eventHandler;
191     }
192 
193     @Override
onInstalled()194     public void onInstalled() {
195         if (DEBUG) {
196             Slog.d(TAG, "Accessibility input filter installed.");
197         }
198         mInstalled = true;
199         disableFeatures();
200         enableFeatures();
201         super.onInstalled();
202     }
203 
204     @Override
onUninstalled()205     public void onUninstalled() {
206         if (DEBUG) {
207             Slog.d(TAG, "Accessibility input filter uninstalled.");
208         }
209         mInstalled = false;
210         disableFeatures();
211         super.onUninstalled();
212     }
213 
onDisplayChanged()214     void onDisplayChanged() {
215         if (mInstalled) {
216             disableFeatures();
217             enableFeatures();
218         }
219     }
220 
221     @Override
onInputEvent(InputEvent event, int policyFlags)222     public void onInputEvent(InputEvent event, int policyFlags) {
223         if (DEBUG) {
224             Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
225                     + Integer.toHexString(policyFlags));
226         }
227 
228         if (mEventHandler.size() == 0) {
229             if (DEBUG) Slog.d(TAG, "No mEventHandler for event " + event);
230             super.onInputEvent(event, policyFlags);
231             return;
232         }
233 
234         EventStreamState state = getEventStreamState(event);
235         if (state == null) {
236             super.onInputEvent(event, policyFlags);
237             return;
238         }
239 
240         int eventSource = event.getSource();
241         if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
242             state.reset();
243             clearEventsForAllEventHandlers(eventSource);
244             super.onInputEvent(event, policyFlags);
245             return;
246         }
247 
248         if (state.updateInputSource(event.getSource())) {
249             clearEventsForAllEventHandlers(eventSource);
250         }
251 
252         if (!state.inputSourceValid()) {
253             super.onInputEvent(event, policyFlags);
254             return;
255         }
256 
257         if (event instanceof MotionEvent) {
258             if ((mEnabledFeatures & FEATURES_AFFECTING_MOTION_EVENTS) != 0) {
259                 MotionEvent motionEvent = (MotionEvent) event;
260                 processMotionEvent(state, motionEvent, policyFlags);
261                 return;
262             } else {
263                 super.onInputEvent(event, policyFlags);
264             }
265         } else if (event instanceof KeyEvent) {
266             KeyEvent keyEvent = (KeyEvent) event;
267             processKeyEvent(state, keyEvent, policyFlags);
268         }
269     }
270 
271     /**
272      * Gets current event stream state associated with an input event.
273      * @return The event stream state that should be used for the event. Null if the event should
274      *     not be handled by #AccessibilityInputFilter.
275      */
getEventStreamState(InputEvent event)276     private EventStreamState getEventStreamState(InputEvent event) {
277         if (event instanceof MotionEvent) {
278           if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
279               if (mTouchScreenStreamState == null) {
280                   mTouchScreenStreamState = new TouchScreenEventStreamState();
281               }
282               return mTouchScreenStreamState;
283           }
284           if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
285               if (mMouseStreamState == null) {
286                   mMouseStreamState = new MouseEventStreamState();
287               }
288               return mMouseStreamState;
289           }
290         } else if (event instanceof KeyEvent) {
291           if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
292               if (mKeyboardStreamState == null) {
293                   mKeyboardStreamState = new KeyboardEventStreamState();
294               }
295               return mKeyboardStreamState;
296           }
297         }
298         return null;
299     }
300 
clearEventsForAllEventHandlers(int eventSource)301     private void clearEventsForAllEventHandlers(int eventSource) {
302         for (int i = 0; i < mEventHandler.size(); i++) {
303             final EventStreamTransformation eventHandler = mEventHandler.valueAt(i);
304             if (eventHandler != null) {
305                 eventHandler.clearEvents(eventSource);
306             }
307         }
308     }
309 
processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags)310     private void processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags) {
311         if (!state.shouldProcessScroll() && event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
312             super.onInputEvent(event, policyFlags);
313             return;
314         }
315 
316         if (!state.shouldProcessMotionEvent(event)) {
317             return;
318         }
319 
320         handleMotionEvent(event, policyFlags);
321     }
322 
processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags)323     private void processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags) {
324         if (!state.shouldProcessKeyEvent(event)) {
325             super.onInputEvent(event, policyFlags);
326             return;
327         }
328         // Since the display id of KeyEvent always would be -1 and there is only one
329         // KeyboardInterceptor for all display, pass KeyEvent to the mEventHandler of
330         // DEFAULT_DISPLAY to handle.
331         mEventHandler.get(Display.DEFAULT_DISPLAY).onKeyEvent(event, policyFlags);
332     }
333 
handleMotionEvent(MotionEvent event, int policyFlags)334     private void handleMotionEvent(MotionEvent event, int policyFlags) {
335         if (DEBUG) {
336             Slog.i(TAG, "Handling motion event: " + event + ", policyFlags: " + policyFlags);
337         }
338         mPm.userActivity(event.getEventTime(), false);
339         MotionEvent transformedEvent = MotionEvent.obtain(event);
340         final int displayId = event.getDisplayId();
341         mEventHandler.get(isDisplayIdValid(displayId) ? displayId : Display.DEFAULT_DISPLAY)
342                 .onMotionEvent(transformedEvent, event, policyFlags);
343         transformedEvent.recycle();
344     }
345 
isDisplayIdValid(int displayId)346     private boolean isDisplayIdValid(int displayId) {
347         return mEventHandler.get(displayId) != null;
348     }
349 
350     @Override
onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent, int policyFlags)351     public void onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent,
352             int policyFlags) {
353         sendInputEvent(transformedEvent, policyFlags);
354     }
355 
356     @Override
onKeyEvent(KeyEvent event, int policyFlags)357     public void onKeyEvent(KeyEvent event, int policyFlags) {
358         sendInputEvent(event, policyFlags);
359     }
360 
361     @Override
onAccessibilityEvent(AccessibilityEvent event)362     public void onAccessibilityEvent(AccessibilityEvent event) {
363         // TODO Implement this to inject the accessibility event
364         //      into the accessibility manager service similarly
365         //      to how this is done for input events.
366     }
367 
368     @Override
setNext(EventStreamTransformation sink)369     public void setNext(EventStreamTransformation sink) {
370         /* do nothing */
371     }
372 
373     @Override
getNext()374     public EventStreamTransformation getNext() {
375         return null;
376     }
377 
378     @Override
clearEvents(int inputSource)379     public void clearEvents(int inputSource) {
380         /* do nothing */
381     }
382 
setUserAndEnabledFeatures(int userId, int enabledFeatures)383     void setUserAndEnabledFeatures(int userId, int enabledFeatures) {
384         if (DEBUG) {
385             Slog.i(TAG, "setUserAndEnabledFeatures(userId = " + userId + ", enabledFeatures = 0x"
386                     + Integer.toHexString(enabledFeatures) + ")");
387         }
388         if (mEnabledFeatures == enabledFeatures && mUserId == userId) {
389             return;
390         }
391         if (mInstalled) {
392             disableFeatures();
393         }
394         mUserId = userId;
395         mEnabledFeatures = enabledFeatures;
396         if (mInstalled) {
397             enableFeatures();
398         }
399     }
400 
notifyAccessibilityEvent(AccessibilityEvent event)401     void notifyAccessibilityEvent(AccessibilityEvent event) {
402         for (int i = 0; i < mEventHandler.size(); i++) {
403             final EventStreamTransformation eventHandler = mEventHandler.valueAt(i);
404             if (eventHandler != null) {
405                 eventHandler.onAccessibilityEvent(event);
406             }
407         }
408     }
409 
notifyAccessibilityButtonClicked(int displayId)410     void notifyAccessibilityButtonClicked(int displayId) {
411         if (mMagnificationGestureHandler.size() != 0) {
412             final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId);
413             if (handler != null) {
414                 handler.notifyShortcutTriggered();
415             }
416         }
417     }
418 
enableFeatures()419     private void enableFeatures() {
420         if (DEBUG) Slog.i(TAG, "enableFeatures()");
421 
422         resetStreamState();
423 
424         final ArrayList<Display> displaysList = mAms.getValidDisplayList();
425 
426         if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
427             mAutoclickController = new AutoclickController(mContext, mUserId);
428             addFirstEventHandlerForAllDisplays(displaysList, mAutoclickController);
429         }
430 
431         for (int i = displaysList.size() - 1; i >= 0; i--) {
432             final int displayId = displaysList.get(i).getDisplayId();
433             final Context displayContext = mContext.createDisplayContext(displaysList.get(i));
434 
435             if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
436                 TouchExplorer explorer = new TouchExplorer(displayContext, mAms);
437                 if ((mEnabledFeatures & FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0) {
438                     explorer.setServiceHandlesDoubleTap(true);
439                 }
440                 if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) {
441                     explorer.setMultiFingerGesturesEnabled(true);
442                 }
443                 if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) {
444                     explorer.setTwoFingerPassthroughEnabled(true);
445                 }
446                 if ((mEnabledFeatures & FLAG_SEND_MOTION_EVENTS) != 0) {
447                     explorer.setSendMotionEventsEnabled(true);
448                 }
449                 addFirstEventHandler(displayId, explorer);
450                 mTouchExplorer.put(displayId, explorer);
451             }
452 
453             if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
454                     || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
455                     || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
456                 final MagnificationGestureHandler magnificationGestureHandler =
457                         createMagnificationGestureHandler(displayId,
458                                 displayContext);
459                 addFirstEventHandler(displayId, magnificationGestureHandler);
460                 mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
461             }
462 
463             if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
464                 MotionEventInjector injector = new MotionEventInjector(
465                         mContext.getMainLooper());
466                 addFirstEventHandler(displayId, injector);
467                 mMotionEventInjectors.put(displayId, injector);
468             }
469         }
470 
471         if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
472             mAms.setMotionEventInjectors(mMotionEventInjectors);
473         }
474 
475         if ((mEnabledFeatures & FLAG_FEATURE_FILTER_KEY_EVENTS) != 0) {
476             mKeyboardInterceptor = new KeyboardInterceptor(mAms,
477                     LocalServices.getService(WindowManagerPolicy.class));
478             // Since the display id of KeyEvent always would be -1 and it would be dispatched to
479             // the display with input focus directly, we only need one KeyboardInterceptor for
480             // default display.
481             addFirstEventHandler(Display.DEFAULT_DISPLAY, mKeyboardInterceptor);
482         }
483     }
484 
485     /**
486      * Adds an event handler to the event handler chain for giving display. The handler is added at
487      * the beginning of the chain.
488      *
489      * @param displayId The logical display id.
490      * @param handler The handler to be added to the event handlers list.
491      */
addFirstEventHandler(int displayId, EventStreamTransformation handler)492     private void addFirstEventHandler(int displayId, EventStreamTransformation handler) {
493         EventStreamTransformation eventHandler = mEventHandler.get(displayId);
494         if (eventHandler != null) {
495             handler.setNext(eventHandler);
496         } else {
497             handler.setNext(this);
498         }
499         eventHandler = handler;
500         mEventHandler.put(displayId, eventHandler);
501     }
502 
503     /**
504      * Adds an event handler to the event handler chain for all displays. The handler is added at
505      * the beginning of the chain.
506      *
507      * @param displayList The list of displays
508      * @param handler The handler to be added to the event handlers list.
509      */
addFirstEventHandlerForAllDisplays(ArrayList<Display> displayList, EventStreamTransformation handler)510     private void addFirstEventHandlerForAllDisplays(ArrayList<Display> displayList,
511             EventStreamTransformation handler) {
512         for (int i = 0; i < displayList.size(); i++) {
513             final int displayId = displayList.get(i).getDisplayId();
514             addFirstEventHandler(displayId, handler);
515         }
516     }
517 
disableFeatures()518     private void disableFeatures() {
519         for (int i = mMotionEventInjectors.size() - 1; i >= 0; i--) {
520             final MotionEventInjector injector = mMotionEventInjectors.valueAt(i);
521             if (injector != null) {
522                 injector.onDestroy();
523             }
524         }
525         mAms.setMotionEventInjectors(null);
526         mMotionEventInjectors.clear();
527         if (mAutoclickController != null) {
528             mAutoclickController.onDestroy();
529             mAutoclickController = null;
530         }
531         for (int i = mTouchExplorer.size() - 1; i >= 0; i--) {
532             final TouchExplorer explorer = mTouchExplorer.valueAt(i);
533             if (explorer != null) {
534                 explorer.onDestroy();
535             }
536         }
537         mTouchExplorer.clear();
538         for (int i = mMagnificationGestureHandler.size() - 1; i >= 0; i--) {
539             final MagnificationGestureHandler handler = mMagnificationGestureHandler.valueAt(i);
540             if (handler != null) {
541                 handler.onDestroy();
542             }
543         }
544         mMagnificationGestureHandler.clear();
545         if (mKeyboardInterceptor != null) {
546             mKeyboardInterceptor.onDestroy();
547             mKeyboardInterceptor = null;
548         }
549 
550         mEventHandler.clear();
551         resetStreamState();
552     }
553 
createMagnificationGestureHandler( int displayId, Context displayContext)554     private MagnificationGestureHandler createMagnificationGestureHandler(
555             int displayId, Context displayContext) {
556         final boolean detectControlGestures = (mEnabledFeatures
557                 & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0;
558         final boolean triggerable = (mEnabledFeatures
559                 & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0;
560         MagnificationGestureHandler magnificationGestureHandler;
561         if (mAms.getMagnificationMode(displayId)
562                 == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
563             final Context uiContext = displayContext.createWindowContext(
564                     TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
565             magnificationGestureHandler = new WindowMagnificationGestureHandler(uiContext,
566                     mAms.getWindowMagnificationMgr(), mAms.getMagnificationController(),
567                     detectControlGestures, triggerable,
568                     displayId);
569         } else {
570             final Context uiContext = displayContext.createWindowContext(
571                     TYPE_MAGNIFICATION_OVERLAY, null /* options */);
572             magnificationGestureHandler = new FullScreenMagnificationGestureHandler(uiContext,
573                     mAms.getFullScreenMagnificationController(), mAms.getMagnificationController(),
574                     detectControlGestures, triggerable,
575                     new WindowMagnificationPromptController(displayContext, mUserId), displayId);
576         }
577         return magnificationGestureHandler;
578     }
579 
resetStreamState()580     void resetStreamState() {
581         if (mTouchScreenStreamState != null) {
582             mTouchScreenStreamState.reset();
583         }
584         if (mMouseStreamState != null) {
585             mMouseStreamState.reset();
586         }
587         if (mKeyboardStreamState != null) {
588             mKeyboardStreamState.reset();
589         }
590     }
591 
592     @Override
onDestroy()593     public void onDestroy() {
594         /* ignore */
595     }
596 
597     /**
598      * Called to refresh the magnification mode on the given display.
599      * It's responsible for changing {@link MagnificationGestureHandler} based on the current mode.
600      *
601      * @param display The logical display
602      */
603     @MainThread
refreshMagnificationMode(Display display)604     public void refreshMagnificationMode(Display display) {
605         final int displayId = display.getDisplayId();
606         final MagnificationGestureHandler magnificationGestureHandler =
607                 mMagnificationGestureHandler.get(displayId);
608         if (magnificationGestureHandler == null) {
609             return;
610         }
611         if (magnificationGestureHandler.getMode() == mAms.getMagnificationMode(displayId)) {
612             return;
613         }
614         magnificationGestureHandler.onDestroy();
615         final MagnificationGestureHandler currentMagnificationGestureHandler =
616                 createMagnificationGestureHandler(displayId,
617                         mContext.createDisplayContext(display));
618         switchEventStreamTransformation(displayId, magnificationGestureHandler,
619                 currentMagnificationGestureHandler);
620         mMagnificationGestureHandler.put(displayId, currentMagnificationGestureHandler);
621     }
622 
623     @MainThread
switchEventStreamTransformation(int displayId, EventStreamTransformation oldStreamTransformation, EventStreamTransformation currentStreamTransformation)624     private void switchEventStreamTransformation(int displayId,
625             EventStreamTransformation oldStreamTransformation,
626             EventStreamTransformation currentStreamTransformation) {
627         EventStreamTransformation eventStreamTransformation = mEventHandler.get(displayId);
628         if (eventStreamTransformation == null) {
629             return;
630         }
631         if (eventStreamTransformation == oldStreamTransformation) {
632             currentStreamTransformation.setNext(oldStreamTransformation.getNext());
633             mEventHandler.put(displayId, currentStreamTransformation);
634         } else {
635             while (eventStreamTransformation != null) {
636                 if (eventStreamTransformation.getNext() == oldStreamTransformation) {
637                     eventStreamTransformation.setNext(currentStreamTransformation);
638                     currentStreamTransformation.setNext(oldStreamTransformation.getNext());
639                     return;
640                 } else {
641                     eventStreamTransformation = eventStreamTransformation.getNext();
642                 }
643             }
644         }
645     }
646 
647     /**
648      * Keeps state of event streams observed for an input device with a certain source.
649      * Provides information about whether motion and key events should be processed by accessibility
650      * #EventStreamTransformations. Base implementation describes behaviour for event sources that
651      * whose events should not be handled by a11y event stream transformations.
652      */
653     private static class EventStreamState {
654         private int mSource;
655 
EventStreamState()656         EventStreamState() {
657             mSource = -1;
658         }
659 
660         /**
661          * Updates the input source of the device associated with the state. If the source changes,
662          * resets internal state.
663          *
664          * @param source Updated input source.
665          * @return Whether the input source has changed.
666          */
updateInputSource(int source)667         public boolean updateInputSource(int source) {
668             if (mSource == source) {
669                 return false;
670             }
671             // Reset clears internal state, so make sure it's called before |mSource| is updated.
672             reset();
673             mSource = source;
674             return true;
675         }
676 
677         /**
678          * @return Whether input source is valid.
679          */
inputSourceValid()680         public boolean inputSourceValid() {
681             return mSource >= 0;
682         }
683 
684         /**
685          * Resets the event stream state.
686          */
reset()687         public void reset() {
688             mSource = -1;
689         }
690 
691         /**
692          * @return Whether scroll events for device should be handled by event transformations.
693          */
shouldProcessScroll()694         public boolean shouldProcessScroll() {
695             return false;
696         }
697 
698         /**
699          * @param event An observed motion event.
700          * @return Whether the event should be handled by event transformations.
701          */
shouldProcessMotionEvent(MotionEvent event)702         public boolean shouldProcessMotionEvent(MotionEvent event) {
703             return false;
704         }
705 
706         /**
707          * @param event An observed key event.
708          * @return Whether the event should be handled by event transformations.
709          */
shouldProcessKeyEvent(KeyEvent event)710         public boolean shouldProcessKeyEvent(KeyEvent event) {
711             return false;
712         }
713     }
714 
715     /**
716      * Keeps state of stream of events from a mouse device.
717      */
718     private static class MouseEventStreamState extends EventStreamState {
719         private boolean mMotionSequenceStarted;
720 
MouseEventStreamState()721         public MouseEventStreamState() {
722             reset();
723         }
724 
725         @Override
reset()726         final public void reset() {
727             super.reset();
728             mMotionSequenceStarted = false;
729         }
730 
731         @Override
shouldProcessScroll()732         final public boolean shouldProcessScroll() {
733             return true;
734         }
735 
736         @Override
shouldProcessMotionEvent(MotionEvent event)737         final public boolean shouldProcessMotionEvent(MotionEvent event) {
738             if (mMotionSequenceStarted) {
739                 return true;
740             }
741             // Wait for down or move event to start processing mouse events.
742             int action = event.getActionMasked();
743             mMotionSequenceStarted =
744                     action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_HOVER_MOVE;
745             return mMotionSequenceStarted;
746         }
747     }
748 
749     /**
750      * Keeps state of stream of events from a touch screen device.
751      */
752     private static class TouchScreenEventStreamState extends EventStreamState {
753         private boolean mTouchSequenceStarted;
754         private boolean mHoverSequenceStarted;
755 
TouchScreenEventStreamState()756         public TouchScreenEventStreamState() {
757             reset();
758         }
759 
760         @Override
reset()761         final public void reset() {
762             super.reset();
763             mTouchSequenceStarted = false;
764             mHoverSequenceStarted = false;
765         }
766 
767         @Override
shouldProcessMotionEvent(MotionEvent event)768         final public boolean shouldProcessMotionEvent(MotionEvent event) {
769             // Wait for a down touch event to start processing.
770             if (event.isTouchEvent()) {
771                 if (mTouchSequenceStarted) {
772                     return true;
773                 }
774                 mTouchSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_DOWN;
775                 return mTouchSequenceStarted;
776             }
777 
778             // Wait for an enter hover event to start processing.
779             if (mHoverSequenceStarted) {
780                 return true;
781             }
782             mHoverSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER;
783             return mHoverSequenceStarted;
784         }
785     }
786 
787     /**
788      * Keeps state of streams of events from all keyboard devices.
789      */
790     private static class KeyboardEventStreamState extends EventStreamState {
791         private SparseBooleanArray mEventSequenceStartedMap = new SparseBooleanArray();
792 
KeyboardEventStreamState()793         public KeyboardEventStreamState() {
794             reset();
795         }
796 
797         @Override
reset()798         final public void reset() {
799             super.reset();
800             mEventSequenceStartedMap.clear();
801         }
802 
803         /*
804          * Key events from different devices may be interleaved. For example, the volume up and
805          * down keys can come from different input sources.
806          */
807         @Override
updateInputSource(int deviceId)808         public boolean updateInputSource(int deviceId) {
809             return false;
810         }
811 
812         // We manage all input source simultaneously; there is no concept of validity.
813         @Override
inputSourceValid()814         public boolean inputSourceValid() {
815             return true;
816         }
817 
818         @Override
shouldProcessKeyEvent(KeyEvent event)819         final public boolean shouldProcessKeyEvent(KeyEvent event) {
820             // For each keyboard device, wait for a down event from a device to start processing
821             int deviceId = event.getDeviceId();
822             if (mEventSequenceStartedMap.get(deviceId, false)) {
823                 return true;
824             }
825             boolean shouldProcess = event.getAction() == KeyEvent.ACTION_DOWN;
826             mEventSequenceStartedMap.put(deviceId, shouldProcess);
827             return shouldProcess;
828         }
829     }
830 
setGestureDetectionPassthroughRegion(int displayId, Region region)831     public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
832         if (region != null && mTouchExplorer.contains(displayId)) {
833             mTouchExplorer.get(displayId).setGestureDetectionPassthroughRegion(region);
834         }
835     }
836 
setTouchExplorationPassthroughRegion(int displayId, Region region)837     public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
838         if (region != null && mTouchExplorer.contains(displayId)) {
839             mTouchExplorer.get(displayId).setTouchExplorationPassthroughRegion(region);
840         }
841     }
842 }
843