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