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.InputDevice.SOURCE_CLASS_POINTER; 20 import static android.view.MotionEvent.ACTION_SCROLL; 21 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; 22 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; 23 24 import android.accessibilityservice.AccessibilityTrace; 25 import android.annotation.MainThread; 26 import android.annotation.NonNull; 27 import android.content.Context; 28 import android.graphics.Region; 29 import android.os.PowerManager; 30 import android.os.SystemClock; 31 import android.provider.Settings; 32 import android.util.Slog; 33 import android.util.SparseArray; 34 import android.util.SparseBooleanArray; 35 import android.view.Display; 36 import android.view.InputDevice; 37 import android.view.InputEvent; 38 import android.view.InputFilter; 39 import android.view.KeyEvent; 40 import android.view.MotionEvent; 41 import android.view.MotionEvent.PointerCoords; 42 import android.view.MotionEvent.PointerProperties; 43 import android.view.accessibility.AccessibilityEvent; 44 45 import com.android.server.LocalServices; 46 import com.android.server.accessibility.gestures.TouchExplorer; 47 import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler; 48 import com.android.server.accessibility.magnification.FullScreenMagnificationVibrationHelper; 49 import com.android.server.accessibility.magnification.MagnificationGestureHandler; 50 import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler; 51 import com.android.server.accessibility.magnification.WindowMagnificationPromptController; 52 import com.android.server.policy.WindowManagerPolicy; 53 54 import java.io.FileDescriptor; 55 import java.io.PrintWriter; 56 import java.util.ArrayList; 57 import java.util.StringJoiner; 58 59 /** 60 * This class is an input filter for implementing accessibility features such 61 * as display magnification and explore by touch. 62 * 63 * NOTE: This class has to be created and poked only from the main thread. 64 */ 65 @SuppressWarnings("MissingPermissionAnnotation") 66 class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation { 67 68 private static final String TAG = AccessibilityInputFilter.class.getSimpleName(); 69 70 private static final boolean DEBUG = false; 71 72 /** 73 * Flag for enabling the screen magnification feature. 74 * 75 * @see #setUserAndEnabledFeatures(int, int) 76 */ 77 static final int FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP = 0x00000001; 78 79 /** 80 * Flag for enabling the touch exploration feature. 81 * 82 * @see #setUserAndEnabledFeatures(int, int) 83 */ 84 static final int FLAG_FEATURE_TOUCH_EXPLORATION = 0x00000002; 85 86 /** 87 * Flag for enabling the filtering key events feature. 88 * 89 * @see #setUserAndEnabledFeatures(int, int) 90 */ 91 static final int FLAG_FEATURE_FILTER_KEY_EVENTS = 0x00000004; 92 93 /** 94 * Flag for enabling "Automatically click on mouse stop" feature. 95 * 96 * @see #setUserAndEnabledFeatures(int, int) 97 */ 98 static final int FLAG_FEATURE_AUTOCLICK = 0x00000008; 99 100 /** 101 * Flag for enabling motion event injection. 102 * 103 * @see #setUserAndEnabledFeatures(int, int) 104 */ 105 static final int FLAG_FEATURE_INJECT_MOTION_EVENTS = 0x00000010; 106 107 /** 108 * Flag for enabling the feature to control the screen magnifier. If 109 * {@link #FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP} is set this flag is ignored 110 * as the screen magnifier feature performs a super set of the work 111 * performed by this feature. 112 * 113 * @see #setUserAndEnabledFeatures(int, int) 114 */ 115 static final int FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER = 0x00000020; 116 117 /** 118 * Flag for enabling the feature to trigger the screen magnifier 119 * from another on-device interaction. 120 */ 121 static final int FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER = 0x00000040; 122 123 /** 124 * Flag for dispatching double tap and double tap and hold to the service. 125 * 126 * @see #setUserAndEnabledFeatures(int, int) 127 */ 128 static final int FLAG_SERVICE_HANDLES_DOUBLE_TAP = 0x00000080; 129 130 /** 131 * Flag for enabling multi-finger gestures. 132 * 133 * @see #setUserAndEnabledFeatures(int, int) 134 */ 135 static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000100; 136 137 /** 138 * Flag for enabling two-finger passthrough when multi-finger gestures are enabled. 139 * 140 * @see #setUserAndEnabledFeatures(int, int) 141 */ 142 static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x00000200; 143 144 /** 145 * Flag for including motion events when dispatching a gesture. 146 * 147 * @see #setUserAndEnabledFeatures(int, int) 148 */ 149 static final int FLAG_SEND_MOTION_EVENTS = 0x00000400; 150 151 /** Flag for intercepting generic motion events. */ 152 static final int FLAG_FEATURE_INTERCEPT_GENERIC_MOTION_EVENTS = 0x00000800; 153 154 /** 155 * Flag for enabling the two-finger triple-tap magnification feature. 156 * 157 * @see #setUserAndEnabledFeatures(int, int) 158 */ 159 static final int FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP = 0x00001000; 160 161 static final int FEATURES_AFFECTING_MOTION_EVENTS = 162 FLAG_FEATURE_INJECT_MOTION_EVENTS 163 | FLAG_FEATURE_AUTOCLICK 164 | FLAG_FEATURE_TOUCH_EXPLORATION 165 | FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP 166 | FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP 167 | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER 168 | FLAG_SERVICE_HANDLES_DOUBLE_TAP 169 | FLAG_REQUEST_MULTI_FINGER_GESTURES 170 | FLAG_REQUEST_2_FINGER_PASSTHROUGH 171 | FLAG_FEATURE_INTERCEPT_GENERIC_MOTION_EVENTS; 172 173 private final Context mContext; 174 175 private final PowerManager mPm; 176 177 private final AccessibilityManagerService mAms; 178 179 private final SparseArray<EventStreamTransformation> mEventHandler; 180 181 private final SparseArray<TouchExplorer> mTouchExplorer = new SparseArray<>(0); 182 183 private final SparseArray<MagnificationGestureHandler> mMagnificationGestureHandler = 184 new SparseArray<>(0); 185 186 private final SparseArray<MotionEventInjector> mMotionEventInjectors = new SparseArray<>(0); 187 188 private AutoclickController mAutoclickController; 189 190 private KeyboardInterceptor mKeyboardInterceptor; 191 192 private boolean mInstalled; 193 194 private int mUserId; 195 196 private int mEnabledFeatures; 197 198 // Display-specific features 199 private SparseArray<Boolean> mServiceDetectsGestures = new SparseArray<>(); 200 private final SparseArray<EventStreamState> mMouseStreamStates = new SparseArray<>(0); 201 202 private final SparseArray<EventStreamState> mTouchScreenStreamStates = new SparseArray<>(0); 203 204 // State tracking for generic MotionEvents is display-agnostic so we only need one. 205 private GenericMotionEventStreamState mGenericMotionEventStreamState; 206 private int mCombinedGenericMotionEventSources = 0; 207 private int mCombinedMotionEventObservedSources = 0; 208 209 private EventStreamState mKeyboardStreamState; 210 211 /** 212 * The last MotionEvent emitted from the input device that's currently active. This is used to 213 * keep track of which input device is currently active, and also to generate the cancel event 214 * if a new device becomes active. 215 */ 216 private MotionEvent mLastActiveDeviceMotionEvent = null; 217 cancelMotion(MotionEvent event)218 private static MotionEvent cancelMotion(MotionEvent event) { 219 if (event.getActionMasked() == MotionEvent.ACTION_CANCEL 220 || event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT 221 || event.getActionMasked() == MotionEvent.ACTION_UP) { 222 throw new IllegalArgumentException("Can't cancel " + event); 223 } 224 final int action; 225 if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER 226 || event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) { 227 action = MotionEvent.ACTION_HOVER_EXIT; 228 } else { 229 action = MotionEvent.ACTION_CANCEL; 230 } 231 232 final int pointerCount; 233 if (event.getActionMasked() == MotionEvent.ACTION_POINTER_UP) { 234 pointerCount = event.getPointerCount() - 1; 235 } else { 236 pointerCount = event.getPointerCount(); 237 } 238 final PointerProperties[] properties = new PointerProperties[pointerCount]; 239 final PointerCoords[] coords = new PointerCoords[pointerCount]; 240 int newPointerIndex = 0; 241 for (int i = 0; i < event.getPointerCount(); i++) { 242 if (event.getActionMasked() == MotionEvent.ACTION_POINTER_UP) { 243 if (event.getActionIndex() == i) { 244 // Skip the pointer that's going away 245 continue; 246 } 247 } 248 final PointerCoords c = new PointerCoords(); 249 c.x = event.getX(i); 250 c.y = event.getY(i); 251 coords[newPointerIndex] = c; 252 final PointerProperties p = new PointerProperties(); 253 p.id = event.getPointerId(i); 254 p.toolType = event.getToolType(i); 255 properties[newPointerIndex] = p; 256 newPointerIndex++; 257 } 258 259 return MotionEvent.obtain(event.getDownTime(), SystemClock.uptimeMillis(), action, 260 pointerCount, properties, coords, 261 event.getMetaState(), event.getButtonState(), 262 event.getXPrecision(), event.getYPrecision(), event.getDeviceId(), 263 event.getEdgeFlags(), event.getSource(), event.getDisplayId(), event.getFlags(), 264 event.getClassification()); 265 } 266 AccessibilityInputFilter(Context context, AccessibilityManagerService service)267 AccessibilityInputFilter(Context context, AccessibilityManagerService service) { 268 this(context, service, new SparseArray<>(0)); 269 } 270 AccessibilityInputFilter(Context context, AccessibilityManagerService service, SparseArray<EventStreamTransformation> eventHandler)271 AccessibilityInputFilter(Context context, AccessibilityManagerService service, 272 SparseArray<EventStreamTransformation> eventHandler) { 273 super(context.getMainLooper()); 274 mContext = context; 275 mAms = service; 276 mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 277 mEventHandler = eventHandler; 278 } 279 280 @Override onInstalled()281 public void onInstalled() { 282 if (DEBUG) { 283 Slog.d(TAG, "Accessibility input filter installed."); 284 } 285 mInstalled = true; 286 disableFeatures(); 287 enableFeatures(); 288 mAms.onInputFilterInstalled(true); 289 super.onInstalled(); 290 } 291 292 @Override onUninstalled()293 public void onUninstalled() { 294 if (DEBUG) { 295 Slog.d(TAG, "Accessibility input filter uninstalled."); 296 } 297 mInstalled = false; 298 disableFeatures(); 299 mAms.onInputFilterInstalled(false); 300 super.onUninstalled(); 301 } 302 onDisplayAdded(@onNull Display display)303 void onDisplayAdded(@NonNull Display display) { 304 enableFeaturesForDisplayIfInstalled(display); 305 306 } 307 onDisplayRemoved(int displayId)308 void onDisplayRemoved(int displayId) { 309 disableFeaturesForDisplayIfInstalled(displayId); 310 } 311 312 @Override onInputEvent(InputEvent event, int policyFlags)313 public void onInputEvent(InputEvent event, int policyFlags) { 314 if (DEBUG) { 315 Slog.d(TAG, "Received event: " + event + ", policyFlags=0x" 316 + Integer.toHexString(policyFlags)); 317 } 318 if (mAms.getTraceManager().isA11yTracingEnabledForTypes( 319 AccessibilityTrace.FLAGS_INPUT_FILTER)) { 320 mAms.getTraceManager().logTrace(TAG + ".onInputEvent", 321 AccessibilityTrace.FLAGS_INPUT_FILTER, 322 "event=" + event + ";policyFlags=" + policyFlags); 323 } 324 if (Flags.handleMultiDeviceInput()) { 325 if (!shouldProcessMultiDeviceEvent(event, policyFlags)) { 326 // We are only allowing a single device to be active at a time. 327 return; 328 } 329 } 330 331 onInputEventInternal(event, policyFlags); 332 } 333 onInputEventInternal(InputEvent event, int policyFlags)334 private void onInputEventInternal(InputEvent event, int policyFlags) { 335 if (mEventHandler.size() == 0) { 336 if (DEBUG) Slog.d(TAG, "No mEventHandler for event " + event); 337 super.onInputEvent(event, policyFlags); 338 return; 339 } 340 341 EventStreamState state = getEventStreamState(event); 342 if (state == null) { 343 super.onInputEvent(event, policyFlags); 344 return; 345 } 346 347 final int eventSource = event.getSource(); 348 final int displayId = event.getDisplayId(); 349 if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) { 350 if (!Flags.doNotResetKeyEventState()) { 351 state.reset(); 352 clearEventStreamHandler(displayId, eventSource); 353 } 354 if (DEBUG) { 355 Slog.d(TAG, "Not processing event " + event); 356 } 357 super.onInputEvent(event, policyFlags); 358 return; 359 } 360 361 if (state.updateInputSource(event.getSource())) { 362 clearEventStreamHandler(displayId, eventSource); 363 } 364 365 if (!state.inputSourceValid()) { 366 super.onInputEvent(event, policyFlags); 367 return; 368 } 369 370 if (event instanceof MotionEvent) { 371 if ((mEnabledFeatures & FEATURES_AFFECTING_MOTION_EVENTS) != 0) { 372 MotionEvent motionEvent = (MotionEvent) event; 373 processMotionEvent(state, motionEvent, policyFlags); 374 return; 375 } else { 376 super.onInputEvent(event, policyFlags); 377 } 378 } else if (event instanceof KeyEvent) { 379 KeyEvent keyEvent = (KeyEvent) event; 380 processKeyEvent(state, keyEvent, policyFlags); 381 } 382 } 383 384 /** 385 * Gets current event stream state associated with an input event. 386 * @return The event stream state that should be used for the event. Null if the event should 387 * not be handled by #AccessibilityInputFilter. 388 */ getEventStreamState(InputEvent event)389 private EventStreamState getEventStreamState(InputEvent event) { 390 if (event instanceof MotionEvent) { 391 final int displayId = event.getDisplayId(); 392 if (mGenericMotionEventStreamState == null) { 393 mGenericMotionEventStreamState = new GenericMotionEventStreamState(); 394 } 395 396 if (mGenericMotionEventStreamState.shouldProcessMotionEvent((MotionEvent) event)) { 397 return mGenericMotionEventStreamState; 398 } 399 if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) { 400 EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId); 401 if (touchScreenStreamState == null) { 402 touchScreenStreamState = new TouchScreenEventStreamState(); 403 mTouchScreenStreamStates.put(displayId, touchScreenStreamState); 404 } 405 return touchScreenStreamState; 406 } 407 if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { 408 EventStreamState mouseStreamState = mMouseStreamStates.get(displayId); 409 if (mouseStreamState == null) { 410 mouseStreamState = new MouseEventStreamState(); 411 mMouseStreamStates.put(displayId, mouseStreamState); 412 } 413 return mouseStreamState; 414 } 415 } else if (event instanceof KeyEvent) { 416 if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) { 417 if (mKeyboardStreamState == null) { 418 mKeyboardStreamState = new KeyboardEventStreamState(); 419 } 420 return mKeyboardStreamState; 421 } 422 } 423 return null; 424 } 425 clearEventStreamHandler(int displayId, int eventSource)426 private void clearEventStreamHandler(int displayId, int eventSource) { 427 final EventStreamTransformation eventHandler = mEventHandler.get(displayId); 428 if (eventHandler != null) { 429 eventHandler.clearEvents(eventSource); 430 } 431 } 432 shouldProcessMultiDeviceEvent(InputEvent event, int policyFlags)433 boolean shouldProcessMultiDeviceEvent(InputEvent event, int policyFlags) { 434 if (event instanceof MotionEvent motion) { 435 if (!motion.isFromSource(SOURCE_CLASS_POINTER) || motion.getAction() == ACTION_SCROLL) { 436 // Non-pointer events are focus-dispatched and don't require special logic. 437 // Scroll events are stand-alone and therefore can be considered to not be part of 438 // a stream. 439 return true; 440 } 441 // Only allow 1 device to be sending motion events at a time 442 // If the event is from an active device, let it through. 443 // If the event is not from an active device, only let it through if it starts a new 444 // gesture like ACTION_DOWN or ACTION_HOVER_ENTER 445 final boolean eventIsFromCurrentDevice = mLastActiveDeviceMotionEvent != null 446 && mLastActiveDeviceMotionEvent.getDeviceId() == motion.getDeviceId(); 447 final int actionMasked = motion.getActionMasked(); 448 switch (actionMasked) { 449 case MotionEvent.ACTION_DOWN: 450 case MotionEvent.ACTION_HOVER_ENTER: 451 case MotionEvent.ACTION_HOVER_MOVE: { 452 if (mLastActiveDeviceMotionEvent != null 453 && mLastActiveDeviceMotionEvent.getDeviceId() != motion.getDeviceId()) { 454 // This is a new gesture from a new device. Cancel the existing state 455 // and let this through 456 MotionEvent canceled = cancelMotion(mLastActiveDeviceMotionEvent); 457 onInputEventInternal(canceled, policyFlags); 458 } 459 mLastActiveDeviceMotionEvent = MotionEvent.obtain(motion); 460 return true; 461 } 462 case MotionEvent.ACTION_MOVE: 463 case MotionEvent.ACTION_POINTER_DOWN: 464 case MotionEvent.ACTION_POINTER_UP: { 465 if (eventIsFromCurrentDevice) { 466 mLastActiveDeviceMotionEvent = MotionEvent.obtain(motion); 467 return true; 468 } else { 469 return false; 470 } 471 } 472 case MotionEvent.ACTION_UP: 473 case MotionEvent.ACTION_CANCEL: 474 case MotionEvent.ACTION_HOVER_EXIT: { 475 if (eventIsFromCurrentDevice) { 476 // This is the last event of the gesture from this device. 477 mLastActiveDeviceMotionEvent = null; 478 return true; 479 } else { 480 // Event is from another device 481 return false; 482 } 483 } 484 default: { 485 if (mLastActiveDeviceMotionEvent != null 486 && event.getDeviceId() != mLastActiveDeviceMotionEvent.getDeviceId()) { 487 // This is an event from another device, ignore it. 488 return false; 489 } 490 } 491 } 492 } 493 return true; 494 } 495 processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags)496 private void processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags) { 497 if (!state.shouldProcessScroll() && event.getActionMasked() == ACTION_SCROLL) { 498 super.onInputEvent(event, policyFlags); 499 return; 500 } 501 502 if (!state.shouldProcessMotionEvent(event)) { 503 return; 504 } 505 506 handleMotionEvent(event, policyFlags); 507 } 508 processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags)509 private void processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags) { 510 if (!state.shouldProcessKeyEvent(event)) { 511 if (DEBUG) { 512 Slog.d(TAG, "processKeyEvent: not processing: " + event); 513 } 514 super.onInputEvent(event, policyFlags); 515 return; 516 } 517 if (DEBUG) { 518 Slog.d(TAG, "processKeyEvent: " + event); 519 } 520 // Since the display id of KeyEvent always would be -1 and there is only one 521 // KeyboardInterceptor for all display, pass KeyEvent to the mEventHandler of 522 // DEFAULT_DISPLAY to handle. 523 mEventHandler.get(Display.DEFAULT_DISPLAY).onKeyEvent(event, policyFlags); 524 } 525 handleMotionEvent(MotionEvent event, int policyFlags)526 private void handleMotionEvent(MotionEvent event, int policyFlags) { 527 if (DEBUG) { 528 Slog.i(TAG, "Handling motion event: " + event + ", policyFlags: " + policyFlags); 529 } 530 mPm.userActivity(event.getEventTime(), false); 531 MotionEvent transformedEvent = MotionEvent.obtain(event); 532 final int displayId = event.getDisplayId(); 533 EventStreamTransformation eventStreamTransformation = mEventHandler.get( 534 isDisplayIdValid(displayId) ? displayId : Display.DEFAULT_DISPLAY); 535 if (eventStreamTransformation != null) { 536 eventStreamTransformation.onMotionEvent(transformedEvent, event, policyFlags); 537 } 538 transformedEvent.recycle(); 539 } 540 isDisplayIdValid(int displayId)541 private boolean isDisplayIdValid(int displayId) { 542 return mEventHandler.get(displayId) != null; 543 } 544 545 @Override onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent, int policyFlags)546 public void onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent, 547 int policyFlags) { 548 if (!mInstalled) { 549 Slog.w(TAG, "onMotionEvent called before input filter installed!"); 550 return; 551 } 552 sendInputEvent(transformedEvent, policyFlags); 553 } 554 555 @Override onKeyEvent(KeyEvent event, int policyFlags)556 public void onKeyEvent(KeyEvent event, int policyFlags) { 557 if (!mInstalled) { 558 Slog.w(TAG, "onKeyEvent called before input filter installed!"); 559 return; 560 } 561 sendInputEvent(event, policyFlags); 562 } 563 564 @Override onAccessibilityEvent(AccessibilityEvent event)565 public void onAccessibilityEvent(AccessibilityEvent event) { 566 // TODO Implement this to inject the accessibility event 567 // into the accessibility manager service similarly 568 // to how this is done for input events. 569 } 570 571 @Override setNext(EventStreamTransformation sink)572 public void setNext(EventStreamTransformation sink) { 573 /* do nothing */ 574 } 575 576 @Override getNext()577 public EventStreamTransformation getNext() { 578 return null; 579 } 580 581 @Override clearEvents(int inputSource)582 public void clearEvents(int inputSource) { 583 /* do nothing */ 584 } 585 setUserAndEnabledFeatures(int userId, int enabledFeatures)586 void setUserAndEnabledFeatures(int userId, int enabledFeatures) { 587 if (DEBUG) { 588 Slog.i(TAG, "setUserAndEnabledFeatures(userId = " + userId + ", enabledFeatures = 0x" 589 + Integer.toHexString(enabledFeatures) + ")"); 590 } 591 if (mEnabledFeatures == enabledFeatures && mUserId == userId) { 592 return; 593 } 594 if (mInstalled) { 595 disableFeatures(); 596 } 597 mUserId = userId; 598 mEnabledFeatures = enabledFeatures; 599 if (mInstalled) { 600 enableFeatures(); 601 } 602 } 603 notifyAccessibilityEvent(AccessibilityEvent event)604 void notifyAccessibilityEvent(AccessibilityEvent event) { 605 for (int i = 0; i < mEventHandler.size(); i++) { 606 final EventStreamTransformation eventHandler = mEventHandler.valueAt(i); 607 if (eventHandler != null) { 608 eventHandler.onAccessibilityEvent(event); 609 } 610 } 611 } 612 notifyAccessibilityButtonClicked(int displayId)613 void notifyAccessibilityButtonClicked(int displayId) { 614 if (mMagnificationGestureHandler.size() != 0) { 615 final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId); 616 if (handler != null) { 617 handler.notifyShortcutTriggered(); 618 } 619 } 620 } 621 enableFeatures()622 private void enableFeatures() { 623 if (DEBUG) Slog.i(TAG, "enableFeatures()"); 624 625 resetAllStreamState(); 626 627 final ArrayList<Display> displaysList = mAms.getValidDisplayList(); 628 629 for (int i = displaysList.size() - 1; i >= 0; i--) { 630 enableFeaturesForDisplay(displaysList.get(i)); 631 } 632 enableDisplayIndependentFeatures(); 633 } 634 enableFeaturesForDisplay(Display display)635 private void enableFeaturesForDisplay(Display display) { 636 if (DEBUG) { 637 Slog.i(TAG, "enableFeaturesForDisplay() : display Id = " + display.getDisplayId()); 638 } 639 640 final Context displayContext = mContext.createDisplayContext(display); 641 final int displayId = display.getDisplayId(); 642 if (mAms.isDisplayProxyed(displayId)) { 643 return; 644 } 645 if (!mServiceDetectsGestures.contains(displayId)) { 646 mServiceDetectsGestures.put(displayId, false); 647 } 648 if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) { 649 if (mAutoclickController == null) { 650 mAutoclickController = new AutoclickController( 651 mContext, mUserId, mAms.getTraceManager()); 652 } 653 addFirstEventHandler(displayId, mAutoclickController); 654 } 655 656 if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) { 657 TouchExplorer explorer = new TouchExplorer(displayContext, mAms); 658 if ((mEnabledFeatures & FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0) { 659 explorer.setServiceHandlesDoubleTap(true); 660 } 661 if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) { 662 explorer.setMultiFingerGesturesEnabled(true); 663 } 664 if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) { 665 explorer.setTwoFingerPassthroughEnabled(true); 666 } 667 if ((mEnabledFeatures & FLAG_SEND_MOTION_EVENTS) != 0) { 668 explorer.setSendMotionEventsEnabled(true); 669 } 670 explorer.setServiceDetectsGestures(mServiceDetectsGestures.get(displayId)); 671 addFirstEventHandler(displayId, explorer); 672 mTouchExplorer.put(displayId, explorer); 673 } 674 675 if ((mEnabledFeatures & FLAG_FEATURE_INTERCEPT_GENERIC_MOTION_EVENTS) != 0) { 676 addFirstEventHandler( 677 displayId, 678 new BaseEventStreamTransformation() { 679 @Override 680 public void onMotionEvent( 681 MotionEvent event, MotionEvent rawEvent, int policyFlags) { 682 boolean passAlongEvent = true; 683 if (anyServiceWantsGenericMotionEvent(event)) { 684 // Some service wants this event, so try to deliver it to at least 685 // one service. 686 if (mAms.sendMotionEventToListeningServices(event)) { 687 // A service accepted this event, so prevent it from passing 688 // down the stream by default. 689 passAlongEvent = false; 690 } 691 // However, if a service is observing these events instead of 692 // consuming them then ensure 693 // it is always passed along to the next stage of the event stream. 694 if (anyServiceWantsToObserveMotionEvent(event)) { 695 passAlongEvent = true; 696 } 697 } 698 if (passAlongEvent) { 699 super.onMotionEvent(event, rawEvent, policyFlags); 700 } 701 } 702 }); 703 } 704 705 if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0 706 || ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP) != 0) 707 || ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP) != 0) 708 || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) { 709 final MagnificationGestureHandler magnificationGestureHandler = 710 createMagnificationGestureHandler(displayId, displayContext); 711 addFirstEventHandler(displayId, magnificationGestureHandler); 712 mMagnificationGestureHandler.put(displayId, magnificationGestureHandler); 713 } 714 715 if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) { 716 MotionEventInjector injector = 717 new MotionEventInjector(mContext.getMainLooper(), mAms.getTraceManager()); 718 addFirstEventHandler(displayId, injector); 719 mMotionEventInjectors.put(displayId, injector); 720 } 721 } 722 enableDisplayIndependentFeatures()723 private void enableDisplayIndependentFeatures() { 724 if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) { 725 mAms.setMotionEventInjectors(mMotionEventInjectors); 726 } 727 728 if ((mEnabledFeatures & FLAG_FEATURE_FILTER_KEY_EVENTS) != 0) { 729 mKeyboardInterceptor = new KeyboardInterceptor(mAms, 730 LocalServices.getService(WindowManagerPolicy.class)); 731 // Since the display id of KeyEvent always would be -1 and it would be dispatched to 732 // the display with input focus directly, we only need one KeyboardInterceptor for 733 // default display. 734 addFirstEventHandler(Display.DEFAULT_DISPLAY, mKeyboardInterceptor); 735 } 736 } 737 738 /** 739 * Adds an event handler to the event handler chain for giving display. The handler is added at 740 * the beginning of the chain. 741 * 742 * @param displayId The logical display id. 743 * @param handler The handler to be added to the event handlers list. 744 */ addFirstEventHandler(int displayId, EventStreamTransformation handler)745 private void addFirstEventHandler(int displayId, EventStreamTransformation handler) { 746 EventStreamTransformation eventHandler = mEventHandler.get(displayId); 747 if (eventHandler != null) { 748 handler.setNext(eventHandler); 749 } else { 750 handler.setNext(this); 751 } 752 eventHandler = handler; 753 mEventHandler.put(displayId, eventHandler); 754 } 755 disableFeatures()756 private void disableFeatures() { 757 final ArrayList<Display> displaysList = mAms.getValidDisplayList(); 758 759 for (int i = displaysList.size() - 1; i >= 0; i--) { 760 disableFeaturesForDisplay(displaysList.get(i).getDisplayId()); 761 } 762 mAms.setMotionEventInjectors(null); 763 disableDisplayIndependentFeatures(); 764 765 resetAllStreamState(); 766 } 767 disableFeaturesForDisplay(int displayId)768 private void disableFeaturesForDisplay(int displayId) { 769 if (DEBUG) { 770 Slog.i(TAG, "disableFeaturesForDisplay() : display Id = " + displayId); 771 } 772 773 final MotionEventInjector injector = mMotionEventInjectors.get(displayId); 774 if (injector != null) { 775 injector.onDestroy(); 776 mMotionEventInjectors.remove(displayId); 777 } 778 779 final TouchExplorer explorer = mTouchExplorer.get(displayId); 780 if (explorer != null) { 781 explorer.onDestroy(); 782 mTouchExplorer.remove(displayId); 783 } 784 785 final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId); 786 if (handler != null) { 787 handler.onDestroy(); 788 mMagnificationGestureHandler.remove(displayId); 789 } 790 791 final EventStreamTransformation eventStreamTransformation = mEventHandler.get(displayId); 792 if (eventStreamTransformation != null) { 793 mEventHandler.remove(displayId); 794 } 795 } enableFeaturesForDisplayIfInstalled(Display display)796 void enableFeaturesForDisplayIfInstalled(Display display) { 797 if (mInstalled) { 798 resetStreamStateForDisplay(display.getDisplayId()); 799 enableFeaturesForDisplay(display); 800 } 801 } disableFeaturesForDisplayIfInstalled(int displayId)802 void disableFeaturesForDisplayIfInstalled(int displayId) { 803 if (mInstalled) { 804 disableFeaturesForDisplay(displayId); 805 resetStreamStateForDisplay(displayId); 806 } 807 } 808 disableDisplayIndependentFeatures()809 private void disableDisplayIndependentFeatures() { 810 if (mAutoclickController != null) { 811 mAutoclickController.onDestroy(); 812 mAutoclickController = null; 813 } 814 815 if (mKeyboardInterceptor != null) { 816 mKeyboardInterceptor.onDestroy(); 817 mKeyboardInterceptor = null; 818 } 819 } 820 createMagnificationGestureHandler( int displayId, Context displayContext)821 private MagnificationGestureHandler createMagnificationGestureHandler( 822 int displayId, Context displayContext) { 823 final boolean detectControlGestures = (mEnabledFeatures 824 & FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP) != 0; 825 final boolean detectTwoFingerTripleTap = (mEnabledFeatures 826 & FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP) != 0; 827 final boolean triggerable = (mEnabledFeatures 828 & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0; 829 MagnificationGestureHandler magnificationGestureHandler; 830 if (mAms.getMagnificationMode(displayId) 831 == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) { 832 final Context uiContext = displayContext.createWindowContext( 833 TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */); 834 magnificationGestureHandler = new WindowMagnificationGestureHandler(uiContext, 835 mAms.getMagnificationConnectionManager(), mAms.getTraceManager(), 836 mAms.getMagnificationController(), 837 detectControlGestures, 838 detectTwoFingerTripleTap, 839 triggerable, displayId); 840 } else { 841 final Context uiContext = displayContext.createWindowContext( 842 TYPE_MAGNIFICATION_OVERLAY, null /* options */); 843 FullScreenMagnificationVibrationHelper fullScreenMagnificationVibrationHelper = 844 new FullScreenMagnificationVibrationHelper(uiContext); 845 magnificationGestureHandler = new FullScreenMagnificationGestureHandler(uiContext, 846 mAms.getMagnificationController().getFullScreenMagnificationController(), 847 mAms.getTraceManager(), 848 mAms.getMagnificationController(), 849 detectControlGestures, 850 detectTwoFingerTripleTap, 851 triggerable, 852 new WindowMagnificationPromptController(displayContext, mUserId), displayId, 853 fullScreenMagnificationVibrationHelper); 854 } 855 return magnificationGestureHandler; 856 } 857 resetAllStreamState()858 void resetAllStreamState() { 859 final ArrayList<Display> displaysList = mAms.getValidDisplayList(); 860 861 for (int i = displaysList.size() - 1; i >= 0; i--) { 862 resetStreamStateForDisplay(displaysList.get(i).getDisplayId()); 863 } 864 865 if (mKeyboardStreamState != null) { 866 mKeyboardStreamState.reset(); 867 } 868 } 869 resetStreamStateForDisplay(int displayId)870 void resetStreamStateForDisplay(int displayId) { 871 final EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId); 872 if (touchScreenStreamState != null) { 873 touchScreenStreamState.reset(); 874 mTouchScreenStreamStates.remove(displayId); 875 } 876 877 final EventStreamState mouseStreamState = mMouseStreamStates.get(displayId); 878 if (mouseStreamState != null) { 879 mouseStreamState.reset(); 880 mMouseStreamStates.remove(displayId); 881 } 882 } 883 884 @Override onDestroy()885 public void onDestroy() { 886 /* ignore */ 887 } 888 889 /** 890 * Called to refresh the magnification mode on the given display. 891 * It's responsible for changing {@link MagnificationGestureHandler} based on the current mode. 892 * 893 * @param display The logical display 894 */ 895 @MainThread refreshMagnificationMode(Display display)896 public void refreshMagnificationMode(Display display) { 897 final int displayId = display.getDisplayId(); 898 final MagnificationGestureHandler magnificationGestureHandler = 899 mMagnificationGestureHandler.get(displayId); 900 if (magnificationGestureHandler == null) { 901 return; 902 } 903 if (magnificationGestureHandler.getMode() == mAms.getMagnificationMode(displayId)) { 904 return; 905 } 906 magnificationGestureHandler.onDestroy(); 907 final MagnificationGestureHandler currentMagnificationGestureHandler = 908 createMagnificationGestureHandler(displayId, 909 mContext.createDisplayContext(display)); 910 switchEventStreamTransformation(displayId, magnificationGestureHandler, 911 currentMagnificationGestureHandler); 912 mMagnificationGestureHandler.put(displayId, currentMagnificationGestureHandler); 913 } 914 915 @MainThread switchEventStreamTransformation(int displayId, EventStreamTransformation oldStreamTransformation, EventStreamTransformation currentStreamTransformation)916 private void switchEventStreamTransformation(int displayId, 917 EventStreamTransformation oldStreamTransformation, 918 EventStreamTransformation currentStreamTransformation) { 919 EventStreamTransformation eventStreamTransformation = mEventHandler.get(displayId); 920 if (eventStreamTransformation == null) { 921 return; 922 } 923 if (eventStreamTransformation == oldStreamTransformation) { 924 currentStreamTransformation.setNext(oldStreamTransformation.getNext()); 925 mEventHandler.put(displayId, currentStreamTransformation); 926 } else { 927 while (eventStreamTransformation != null) { 928 if (eventStreamTransformation.getNext() == oldStreamTransformation) { 929 eventStreamTransformation.setNext(currentStreamTransformation); 930 currentStreamTransformation.setNext(oldStreamTransformation.getNext()); 931 return; 932 } else { 933 eventStreamTransformation = eventStreamTransformation.getNext(); 934 } 935 } 936 } 937 } 938 939 /** 940 * Keeps state of event streams observed for an input device with a certain source. 941 * Provides information about whether motion and key events should be processed by accessibility 942 * #EventStreamTransformations. Base implementation describes behaviour for event sources that 943 * whose events should not be handled by a11y event stream transformations. 944 */ 945 private static class EventStreamState { 946 private int mSource; 947 EventStreamState()948 EventStreamState() { 949 mSource = -1; 950 } 951 952 /** 953 * Updates the input source of the device associated with the state. If the source changes, 954 * resets internal state. 955 * 956 * @param source Updated input source. 957 * @return Whether the input source has changed. 958 */ updateInputSource(int source)959 public boolean updateInputSource(int source) { 960 if (mSource == source) { 961 return false; 962 } 963 // Reset clears internal state, so make sure it's called before |mSource| is updated. 964 reset(); 965 mSource = source; 966 return true; 967 } 968 969 /** 970 * @return Whether input source is valid. 971 */ inputSourceValid()972 public boolean inputSourceValid() { 973 return mSource >= 0; 974 } 975 976 /** 977 * Resets the event stream state. 978 */ reset()979 public void reset() { 980 mSource = -1; 981 } 982 983 /** 984 * @return Whether scroll events for device should be handled by event transformations. 985 */ shouldProcessScroll()986 public boolean shouldProcessScroll() { 987 return false; 988 } 989 990 /** 991 * @param event An observed motion event. 992 * @return Whether the event should be handled by event transformations. 993 */ shouldProcessMotionEvent(MotionEvent event)994 public boolean shouldProcessMotionEvent(MotionEvent event) { 995 return false; 996 } 997 998 /** 999 * @param event An observed key event. 1000 * @return Whether the event should be handled by event transformations. 1001 */ shouldProcessKeyEvent(KeyEvent event)1002 public boolean shouldProcessKeyEvent(KeyEvent event) { 1003 return false; 1004 } 1005 } 1006 1007 /** 1008 * Keeps state of stream of events from a mouse device. 1009 */ 1010 private static class MouseEventStreamState extends EventStreamState { 1011 private boolean mMotionSequenceStarted; 1012 MouseEventStreamState()1013 public MouseEventStreamState() { 1014 reset(); 1015 } 1016 1017 @Override reset()1018 final public void reset() { 1019 super.reset(); 1020 mMotionSequenceStarted = false; 1021 } 1022 1023 @Override shouldProcessScroll()1024 final public boolean shouldProcessScroll() { 1025 return true; 1026 } 1027 1028 @Override shouldProcessMotionEvent(MotionEvent event)1029 final public boolean shouldProcessMotionEvent(MotionEvent event) { 1030 if (mMotionSequenceStarted) { 1031 return true; 1032 } 1033 // Wait for down or move event to start processing mouse events. 1034 int action = event.getActionMasked(); 1035 mMotionSequenceStarted = 1036 action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_HOVER_MOVE; 1037 return mMotionSequenceStarted; 1038 } 1039 } 1040 1041 /** 1042 * Keeps state of stream of events from a touch screen device. 1043 */ 1044 private static class TouchScreenEventStreamState extends EventStreamState { 1045 private boolean mTouchSequenceStarted; 1046 private boolean mHoverSequenceStarted; 1047 TouchScreenEventStreamState()1048 public TouchScreenEventStreamState() { 1049 reset(); 1050 } 1051 1052 @Override reset()1053 final public void reset() { 1054 super.reset(); 1055 mTouchSequenceStarted = false; 1056 mHoverSequenceStarted = false; 1057 } 1058 1059 @Override shouldProcessMotionEvent(MotionEvent event)1060 final public boolean shouldProcessMotionEvent(MotionEvent event) { 1061 // Wait for a down touch event to start processing. 1062 if (event.isTouchEvent()) { 1063 if (mTouchSequenceStarted) { 1064 return true; 1065 } 1066 mTouchSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_DOWN; 1067 return mTouchSequenceStarted; 1068 } 1069 1070 // Wait for an enter hover event to start processing. 1071 if (mHoverSequenceStarted) { 1072 return true; 1073 } 1074 mHoverSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER; 1075 return mHoverSequenceStarted; 1076 } 1077 } 1078 1079 private class GenericMotionEventStreamState extends EventStreamState { 1080 @Override shouldProcessMotionEvent(MotionEvent event)1081 public boolean shouldProcessMotionEvent(MotionEvent event) { 1082 return anyServiceWantsGenericMotionEvent(event); 1083 } 1084 @Override shouldProcessScroll()1085 public boolean shouldProcessScroll() { 1086 return true; 1087 } 1088 } 1089 anyServiceWantsToObserveMotionEvent(MotionEvent event)1090 private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) { 1091 // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing 1092 // touch exploration. 1093 if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN) 1094 && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) { 1095 return false; 1096 } 1097 final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; 1098 return (mCombinedGenericMotionEventSources 1099 & mCombinedMotionEventObservedSources 1100 & eventSourceWithoutClass) 1101 != 0; 1102 } 1103 anyServiceWantsGenericMotionEvent(MotionEvent event)1104 private boolean anyServiceWantsGenericMotionEvent(MotionEvent event) { 1105 // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing 1106 // touch exploration. 1107 if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN) 1108 && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) { 1109 return false; 1110 } 1111 final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; 1112 return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0; 1113 } 1114 setCombinedGenericMotionEventSources(int sources)1115 public void setCombinedGenericMotionEventSources(int sources) { 1116 mCombinedGenericMotionEventSources = sources; 1117 } 1118 setCombinedMotionEventObservedSources(int sources)1119 public void setCombinedMotionEventObservedSources(int sources) { 1120 mCombinedMotionEventObservedSources = sources; 1121 } 1122 1123 /** 1124 * Keeps state of streams of events from all keyboard devices. 1125 */ 1126 private static class KeyboardEventStreamState extends EventStreamState { 1127 private SparseBooleanArray mEventSequenceStartedMap = new SparseBooleanArray(); 1128 KeyboardEventStreamState()1129 public KeyboardEventStreamState() { 1130 reset(); 1131 } 1132 1133 @Override reset()1134 final public void reset() { 1135 super.reset(); 1136 mEventSequenceStartedMap.clear(); 1137 } 1138 1139 /* 1140 * Key events from different devices may be interleaved. For example, the volume up and 1141 * down keys can come from different input sources. 1142 */ 1143 @Override updateInputSource(int deviceId)1144 public boolean updateInputSource(int deviceId) { 1145 return false; 1146 } 1147 1148 // We manage all input source simultaneously; there is no concept of validity. 1149 @Override inputSourceValid()1150 public boolean inputSourceValid() { 1151 return true; 1152 } 1153 1154 @Override shouldProcessKeyEvent(KeyEvent event)1155 final public boolean shouldProcessKeyEvent(KeyEvent event) { 1156 // For each keyboard device, wait for a down event from a device to start processing 1157 int deviceId = event.getDeviceId(); 1158 if (mEventSequenceStartedMap.get(deviceId, false)) { 1159 return true; 1160 } 1161 boolean shouldProcess = event.getAction() == KeyEvent.ACTION_DOWN; 1162 mEventSequenceStartedMap.put(deviceId, shouldProcess); 1163 return shouldProcess; 1164 } 1165 } 1166 setGestureDetectionPassthroughRegion(int displayId, Region region)1167 public void setGestureDetectionPassthroughRegion(int displayId, Region region) { 1168 if (region != null && mTouchExplorer.contains(displayId)) { 1169 mTouchExplorer.get(displayId).setGestureDetectionPassthroughRegion(region); 1170 } 1171 } 1172 setTouchExplorationPassthroughRegion(int displayId, Region region)1173 public void setTouchExplorationPassthroughRegion(int displayId, Region region) { 1174 if (region != null && mTouchExplorer.contains(displayId)) { 1175 mTouchExplorer.get(displayId).setTouchExplorationPassthroughRegion(region); 1176 } 1177 } 1178 setServiceDetectsGesturesEnabled(int displayId, boolean mode)1179 public void setServiceDetectsGesturesEnabled(int displayId, boolean mode) { 1180 if (mTouchExplorer.contains(displayId)) { 1181 mTouchExplorer.get(displayId).setServiceDetectsGestures(mode); 1182 } 1183 mServiceDetectsGestures.put(displayId, mode); 1184 } 1185 resetServiceDetectsGestures()1186 public void resetServiceDetectsGestures() { 1187 mServiceDetectsGestures.clear(); 1188 } 1189 requestTouchExploration(int displayId)1190 public void requestTouchExploration(int displayId) { 1191 if (mTouchExplorer.contains(displayId)) { 1192 mTouchExplorer.get(displayId).requestTouchExploration(); 1193 } 1194 } 1195 requestDragging(int displayId, int pointerId)1196 public void requestDragging(int displayId, int pointerId) { 1197 if (mTouchExplorer.contains(displayId)) { 1198 mTouchExplorer.get(displayId).requestDragging(pointerId); 1199 } 1200 } 1201 requestDelegating(int displayId)1202 public void requestDelegating(int displayId) { 1203 if (mTouchExplorer.contains(displayId)) { 1204 mTouchExplorer.get(displayId).requestDelegating(); 1205 } 1206 } 1207 onDoubleTap(int displayId)1208 public void onDoubleTap(int displayId) { 1209 if (mTouchExplorer.contains(displayId)) { 1210 mTouchExplorer.get(displayId).onDoubleTap(); 1211 } 1212 } 1213 onDoubleTapAndHold(int displayId)1214 public void onDoubleTapAndHold(int displayId) { 1215 if (mTouchExplorer.contains(displayId)) { 1216 mTouchExplorer.get(displayId).onDoubleTapAndHold(); 1217 } 1218 } 1219 1220 /** 1221 * Dumps all {@link AccessibilityInputFilter}s here. 1222 */ dump(FileDescriptor fd, final PrintWriter pw, String[] args)1223 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { 1224 if (mEventHandler == null) { 1225 return; 1226 } 1227 pw.append("A11yInputFilter Info : "); 1228 pw.println(); 1229 1230 final ArrayList<Display> displaysList = mAms.getValidDisplayList(); 1231 for (int i = 0; i < displaysList.size(); i++) { 1232 final int displayId = displaysList.get(i).getDisplayId(); 1233 EventStreamTransformation next = mEventHandler.get(displayId); 1234 if (next != null) { 1235 pw.append("Enabled features of Display ["); 1236 pw.append(Integer.toString(displayId)); 1237 pw.append("] = "); 1238 1239 final StringJoiner joiner = new StringJoiner(",", "[", "]"); 1240 1241 while (next != null) { 1242 if (next instanceof MagnificationGestureHandler) { 1243 joiner.add("MagnificationGesture"); 1244 } else if (next instanceof KeyboardInterceptor) { 1245 joiner.add("KeyboardInterceptor"); 1246 } else if (next instanceof TouchExplorer) { 1247 joiner.add("TouchExplorer"); 1248 } else if (next instanceof AutoclickController) { 1249 joiner.add("AutoclickController"); 1250 } else if (next instanceof MotionEventInjector) { 1251 joiner.add("MotionEventInjector"); 1252 } 1253 next = next.getNext(); 1254 } 1255 pw.append(joiner.toString()); 1256 } 1257 pw.println(); 1258 } 1259 } 1260 } 1261