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