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