• 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.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.getMagnificationController().getFullScreenMagnificationController(),
612                     mAms.getTraceManager(),
613                     mAms.getMagnificationController(), detectControlGestures, triggerable,
614                     new WindowMagnificationPromptController(displayContext, mUserId), displayId);
615         }
616         return magnificationGestureHandler;
617     }
618 
resetAllStreamState()619     void resetAllStreamState() {
620         final ArrayList<Display> displaysList = mAms.getValidDisplayList();
621 
622         for (int i = displaysList.size() - 1; i >= 0; i--) {
623             resetStreamStateForDisplay(displaysList.get(i).getDisplayId());
624         }
625 
626         if (mKeyboardStreamState != null) {
627             mKeyboardStreamState.reset();
628         }
629     }
630 
resetStreamStateForDisplay(int displayId)631     void resetStreamStateForDisplay(int displayId) {
632         final EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId);
633         if (touchScreenStreamState != null) {
634             touchScreenStreamState.reset();
635             mTouchScreenStreamStates.remove(displayId);
636         }
637 
638         final EventStreamState mouseStreamState = mMouseStreamStates.get(displayId);
639         if (mouseStreamState != null) {
640             mouseStreamState.reset();
641             mMouseStreamStates.remove(displayId);
642         }
643     }
644 
645     @Override
onDestroy()646     public void onDestroy() {
647         /* ignore */
648     }
649 
650     /**
651      * Called to refresh the magnification mode on the given display.
652      * It's responsible for changing {@link MagnificationGestureHandler} based on the current mode.
653      *
654      * @param display The logical display
655      */
656     @MainThread
refreshMagnificationMode(Display display)657     public void refreshMagnificationMode(Display display) {
658         final int displayId = display.getDisplayId();
659         final MagnificationGestureHandler magnificationGestureHandler =
660                 mMagnificationGestureHandler.get(displayId);
661         if (magnificationGestureHandler == null) {
662             return;
663         }
664         if (magnificationGestureHandler.getMode() == mAms.getMagnificationMode(displayId)) {
665             return;
666         }
667         magnificationGestureHandler.onDestroy();
668         final MagnificationGestureHandler currentMagnificationGestureHandler =
669                 createMagnificationGestureHandler(displayId,
670                         mContext.createDisplayContext(display));
671         switchEventStreamTransformation(displayId, magnificationGestureHandler,
672                 currentMagnificationGestureHandler);
673         mMagnificationGestureHandler.put(displayId, currentMagnificationGestureHandler);
674     }
675 
676     @MainThread
switchEventStreamTransformation(int displayId, EventStreamTransformation oldStreamTransformation, EventStreamTransformation currentStreamTransformation)677     private void switchEventStreamTransformation(int displayId,
678             EventStreamTransformation oldStreamTransformation,
679             EventStreamTransformation currentStreamTransformation) {
680         EventStreamTransformation eventStreamTransformation = mEventHandler.get(displayId);
681         if (eventStreamTransformation == null) {
682             return;
683         }
684         if (eventStreamTransformation == oldStreamTransformation) {
685             currentStreamTransformation.setNext(oldStreamTransformation.getNext());
686             mEventHandler.put(displayId, currentStreamTransformation);
687         } else {
688             while (eventStreamTransformation != null) {
689                 if (eventStreamTransformation.getNext() == oldStreamTransformation) {
690                     eventStreamTransformation.setNext(currentStreamTransformation);
691                     currentStreamTransformation.setNext(oldStreamTransformation.getNext());
692                     return;
693                 } else {
694                     eventStreamTransformation = eventStreamTransformation.getNext();
695                 }
696             }
697         }
698     }
699 
700     /**
701      * Keeps state of event streams observed for an input device with a certain source.
702      * Provides information about whether motion and key events should be processed by accessibility
703      * #EventStreamTransformations. Base implementation describes behaviour for event sources that
704      * whose events should not be handled by a11y event stream transformations.
705      */
706     private static class EventStreamState {
707         private int mSource;
708 
EventStreamState()709         EventStreamState() {
710             mSource = -1;
711         }
712 
713         /**
714          * Updates the input source of the device associated with the state. If the source changes,
715          * resets internal state.
716          *
717          * @param source Updated input source.
718          * @return Whether the input source has changed.
719          */
updateInputSource(int source)720         public boolean updateInputSource(int source) {
721             if (mSource == source) {
722                 return false;
723             }
724             // Reset clears internal state, so make sure it's called before |mSource| is updated.
725             reset();
726             mSource = source;
727             return true;
728         }
729 
730         /**
731          * @return Whether input source is valid.
732          */
inputSourceValid()733         public boolean inputSourceValid() {
734             return mSource >= 0;
735         }
736 
737         /**
738          * Resets the event stream state.
739          */
reset()740         public void reset() {
741             mSource = -1;
742         }
743 
744         /**
745          * @return Whether scroll events for device should be handled by event transformations.
746          */
shouldProcessScroll()747         public boolean shouldProcessScroll() {
748             return false;
749         }
750 
751         /**
752          * @param event An observed motion event.
753          * @return Whether the event should be handled by event transformations.
754          */
shouldProcessMotionEvent(MotionEvent event)755         public boolean shouldProcessMotionEvent(MotionEvent event) {
756             return false;
757         }
758 
759         /**
760          * @param event An observed key event.
761          * @return Whether the event should be handled by event transformations.
762          */
shouldProcessKeyEvent(KeyEvent event)763         public boolean shouldProcessKeyEvent(KeyEvent event) {
764             return false;
765         }
766     }
767 
768     /**
769      * Keeps state of stream of events from a mouse device.
770      */
771     private static class MouseEventStreamState extends EventStreamState {
772         private boolean mMotionSequenceStarted;
773 
MouseEventStreamState()774         public MouseEventStreamState() {
775             reset();
776         }
777 
778         @Override
reset()779         final public void reset() {
780             super.reset();
781             mMotionSequenceStarted = false;
782         }
783 
784         @Override
shouldProcessScroll()785         final public boolean shouldProcessScroll() {
786             return true;
787         }
788 
789         @Override
shouldProcessMotionEvent(MotionEvent event)790         final public boolean shouldProcessMotionEvent(MotionEvent event) {
791             if (mMotionSequenceStarted) {
792                 return true;
793             }
794             // Wait for down or move event to start processing mouse events.
795             int action = event.getActionMasked();
796             mMotionSequenceStarted =
797                     action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_HOVER_MOVE;
798             return mMotionSequenceStarted;
799         }
800     }
801 
802     /**
803      * Keeps state of stream of events from a touch screen device.
804      */
805     private static class TouchScreenEventStreamState extends EventStreamState {
806         private boolean mTouchSequenceStarted;
807         private boolean mHoverSequenceStarted;
808 
TouchScreenEventStreamState()809         public TouchScreenEventStreamState() {
810             reset();
811         }
812 
813         @Override
reset()814         final public void reset() {
815             super.reset();
816             mTouchSequenceStarted = false;
817             mHoverSequenceStarted = false;
818         }
819 
820         @Override
shouldProcessMotionEvent(MotionEvent event)821         final public boolean shouldProcessMotionEvent(MotionEvent event) {
822             // Wait for a down touch event to start processing.
823             if (event.isTouchEvent()) {
824                 if (mTouchSequenceStarted) {
825                     return true;
826                 }
827                 mTouchSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_DOWN;
828                 return mTouchSequenceStarted;
829             }
830 
831             // Wait for an enter hover event to start processing.
832             if (mHoverSequenceStarted) {
833                 return true;
834             }
835             mHoverSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER;
836             return mHoverSequenceStarted;
837         }
838     }
839 
840     /**
841      * Keeps state of streams of events from all keyboard devices.
842      */
843     private static class KeyboardEventStreamState extends EventStreamState {
844         private SparseBooleanArray mEventSequenceStartedMap = new SparseBooleanArray();
845 
KeyboardEventStreamState()846         public KeyboardEventStreamState() {
847             reset();
848         }
849 
850         @Override
reset()851         final public void reset() {
852             super.reset();
853             mEventSequenceStartedMap.clear();
854         }
855 
856         /*
857          * Key events from different devices may be interleaved. For example, the volume up and
858          * down keys can come from different input sources.
859          */
860         @Override
updateInputSource(int deviceId)861         public boolean updateInputSource(int deviceId) {
862             return false;
863         }
864 
865         // We manage all input source simultaneously; there is no concept of validity.
866         @Override
inputSourceValid()867         public boolean inputSourceValid() {
868             return true;
869         }
870 
871         @Override
shouldProcessKeyEvent(KeyEvent event)872         final public boolean shouldProcessKeyEvent(KeyEvent event) {
873             // For each keyboard device, wait for a down event from a device to start processing
874             int deviceId = event.getDeviceId();
875             if (mEventSequenceStartedMap.get(deviceId, false)) {
876                 return true;
877             }
878             boolean shouldProcess = event.getAction() == KeyEvent.ACTION_DOWN;
879             mEventSequenceStartedMap.put(deviceId, shouldProcess);
880             return shouldProcess;
881         }
882     }
883 
setGestureDetectionPassthroughRegion(int displayId, Region region)884     public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
885         if (region != null && mTouchExplorer.contains(displayId)) {
886             mTouchExplorer.get(displayId).setGestureDetectionPassthroughRegion(region);
887         }
888     }
889 
setTouchExplorationPassthroughRegion(int displayId, Region region)890     public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
891         if (region != null && mTouchExplorer.contains(displayId)) {
892             mTouchExplorer.get(displayId).setTouchExplorationPassthroughRegion(region);
893         }
894     }
895 
setServiceDetectsGesturesEnabled(int displayId, boolean mode)896     public void setServiceDetectsGesturesEnabled(int displayId, boolean mode) {
897         if (mTouchExplorer.contains(displayId)) {
898             mTouchExplorer.get(displayId).setServiceDetectsGestures(mode);
899         }
900     }
901 
requestTouchExploration(int displayId)902     public void requestTouchExploration(int displayId) {
903         if (mTouchExplorer.contains(displayId)) {
904             mTouchExplorer.get(displayId).requestTouchExploration();
905         }
906     }
907 
requestDragging(int displayId, int pointerId)908     public void requestDragging(int displayId, int pointerId) {
909         if (mTouchExplorer.contains(displayId)) {
910             mTouchExplorer.get(displayId).requestDragging(pointerId);
911         }
912     }
913 
requestDelegating(int displayId)914     public void requestDelegating(int displayId) {
915         if (mTouchExplorer.contains(displayId)) {
916             mTouchExplorer.get(displayId).requestDelegating();
917         }
918     }
919 
onDoubleTap(int displayId)920     public void onDoubleTap(int displayId) {
921         if (mTouchExplorer.contains(displayId)) {
922             mTouchExplorer.get(displayId).onDoubleTap();
923         }
924     }
925 
onDoubleTapAndHold(int displayId)926     public void onDoubleTapAndHold(int displayId) {
927         if (mTouchExplorer.contains(displayId)) {
928             mTouchExplorer.get(displayId).onDoubleTapAndHold();
929         }
930     }
931 
932     /**
933      * Dumps all {@link AccessibilityInputFilter}s here.
934      */
dump(FileDescriptor fd, final PrintWriter pw, String[] args)935     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
936         if (mEventHandler == null) {
937             return;
938         }
939         pw.append("A11yInputFilter Info : ");
940         pw.println();
941 
942         final ArrayList<Display> displaysList = mAms.getValidDisplayList();
943         for (int i = 0; i < displaysList.size(); i++) {
944             final int displayId = displaysList.get(i).getDisplayId();
945             EventStreamTransformation next = mEventHandler.get(displayId);
946             if (next != null) {
947                 pw.append("Enabled features of Display [");
948                 pw.append(Integer.toString(displayId));
949                 pw.append("] = ");
950 
951                 final StringJoiner joiner = new StringJoiner(",", "[", "]");
952 
953                 while (next != null) {
954                     if (next instanceof MagnificationGestureHandler) {
955                         joiner.add("MagnificationGesture");
956                     } else if (next instanceof KeyboardInterceptor) {
957                         joiner.add("KeyboardInterceptor");
958                     } else if (next instanceof TouchExplorer) {
959                         joiner.add("TouchExplorer");
960                     } else if (next instanceof AutoclickController) {
961                         joiner.add("AutoclickController");
962                     } else if (next instanceof MotionEventInjector) {
963                         joiner.add("MotionEventInjector");
964                     }
965                     next = next.getNext();
966                 }
967                 pw.append(joiner.toString());
968             }
969             pw.println();
970         }
971     }
972 }
973