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 android.content.Context; 20 import android.os.Handler; 21 import android.os.PowerManager; 22 import android.util.DebugUtils; 23 import android.util.ExceptionUtils; 24 import android.util.Log; 25 import android.util.Pools.SimplePool; 26 import android.util.Slog; 27 import android.util.SparseBooleanArray; 28 import android.view.Choreographer; 29 import android.view.InputDevice; 30 import android.view.InputEvent; 31 import android.view.InputFilter; 32 import android.view.KeyEvent; 33 import android.view.MotionEvent; 34 import android.view.accessibility.AccessibilityEvent; 35 36 import com.android.internal.util.BitUtils; 37 import com.android.server.LocalServices; 38 import com.android.server.policy.WindowManagerPolicy; 39 40 /** 41 * This class is an input filter for implementing accessibility features such 42 * as display magnification and explore by touch. 43 * 44 * NOTE: This class has to be created and poked only from the main thread. 45 */ 46 class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation { 47 48 private static final String TAG = AccessibilityInputFilter.class.getSimpleName(); 49 50 private static final boolean DEBUG = false; 51 52 /** 53 * Flag for enabling the screen magnification feature. 54 * 55 * @see #setUserAndEnabledFeatures(int, int) 56 */ 57 static final int FLAG_FEATURE_SCREEN_MAGNIFIER = 0x00000001; 58 59 /** 60 * Flag for enabling the touch exploration feature. 61 * 62 * @see #setUserAndEnabledFeatures(int, int) 63 */ 64 static final int FLAG_FEATURE_TOUCH_EXPLORATION = 0x00000002; 65 66 /** 67 * Flag for enabling the filtering key events feature. 68 * 69 * @see #setUserAndEnabledFeatures(int, int) 70 */ 71 static final int FLAG_FEATURE_FILTER_KEY_EVENTS = 0x00000004; 72 73 /** 74 * Flag for enabling "Automatically click on mouse stop" feature. 75 * 76 * @see #setUserAndEnabledFeatures(int, int) 77 */ 78 static final int FLAG_FEATURE_AUTOCLICK = 0x00000008; 79 80 /** 81 * Flag for enabling motion event injection. 82 * 83 * @see #setUserAndEnabledFeatures(int, int) 84 */ 85 static final int FLAG_FEATURE_INJECT_MOTION_EVENTS = 0x00000010; 86 87 /** 88 * Flag for enabling the feature to control the screen magnifier. If 89 * {@link #FLAG_FEATURE_SCREEN_MAGNIFIER} is set this flag is ignored 90 * as the screen magnifier feature performs a super set of the work 91 * performed by this feature. 92 * 93 * @see #setUserAndEnabledFeatures(int, int) 94 */ 95 static final int FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER = 0x00000020; 96 97 /** 98 * Flag for enabling the feature to trigger the screen magnifier 99 * from another on-device interaction. 100 */ 101 static final int FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER = 0x00000040; 102 103 static final int FEATURES_AFFECTING_MOTION_EVENTS = FLAG_FEATURE_INJECT_MOTION_EVENTS 104 | FLAG_FEATURE_AUTOCLICK | FLAG_FEATURE_TOUCH_EXPLORATION 105 | FLAG_FEATURE_SCREEN_MAGNIFIER | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER; 106 107 private final Runnable mProcessBatchedEventsRunnable = new Runnable() { 108 @Override 109 public void run() { 110 final long frameTimeNanos = mChoreographer.getFrameTimeNanos(); 111 if (DEBUG) { 112 Slog.i(TAG, "Begin batch processing for frame: " + frameTimeNanos); 113 } 114 processBatchedEvents(frameTimeNanos); 115 if (DEBUG) { 116 Slog.i(TAG, "End batch processing."); 117 } 118 if (mEventQueue != null) { 119 scheduleProcessBatchedEvents(); 120 } 121 } 122 }; 123 124 private final Context mContext; 125 126 private final PowerManager mPm; 127 128 private final AccessibilityManagerService mAms; 129 130 private final Choreographer mChoreographer; 131 132 private boolean mInstalled; 133 134 private int mUserId; 135 136 private int mEnabledFeatures; 137 138 private TouchExplorer mTouchExplorer; 139 140 private MagnificationGestureHandler mMagnificationGestureHandler; 141 142 private MotionEventInjector mMotionEventInjector; 143 144 private AutoclickController mAutoclickController; 145 146 private KeyboardInterceptor mKeyboardInterceptor; 147 148 private EventStreamTransformation mEventHandler; 149 150 private MotionEventHolder mEventQueue; 151 152 private EventStreamState mMouseStreamState; 153 154 private EventStreamState mTouchScreenStreamState; 155 156 private EventStreamState mKeyboardStreamState; 157 158 AccessibilityInputFilter(Context context, AccessibilityManagerService service) { 159 super(context.getMainLooper()); 160 mContext = context; 161 mAms = service; 162 mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 163 mChoreographer = Choreographer.getInstance(); 164 } 165 166 @Override 167 public void onInstalled() { 168 if (DEBUG) { 169 Slog.d(TAG, "Accessibility input filter installed."); 170 } 171 mInstalled = true; 172 disableFeatures(); 173 enableFeatures(); 174 super.onInstalled(); 175 } 176 177 @Override 178 public void onUninstalled() { 179 if (DEBUG) { 180 Slog.d(TAG, "Accessibility input filter uninstalled."); 181 } 182 mInstalled = false; 183 disableFeatures(); 184 super.onUninstalled(); 185 } 186 187 @Override 188 public void onInputEvent(InputEvent event, int policyFlags) { 189 if (DEBUG) { 190 Slog.d(TAG, "Received event: " + event + ", policyFlags=0x" 191 + Integer.toHexString(policyFlags)); 192 } 193 194 if (mEventHandler == null) { 195 if (DEBUG) Slog.d(TAG, "mEventHandler == null for event " + event); 196 super.onInputEvent(event, policyFlags); 197 return; 198 } 199 200 EventStreamState state = getEventStreamState(event); 201 if (state == null) { 202 super.onInputEvent(event, policyFlags); 203 return; 204 } 205 206 int eventSource = event.getSource(); 207 if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) { 208 state.reset(); 209 mEventHandler.clearEvents(eventSource); 210 super.onInputEvent(event, policyFlags); 211 return; 212 } 213 214 if (state.updateDeviceId(event.getDeviceId())) { 215 mEventHandler.clearEvents(eventSource); 216 } 217 218 if (!state.deviceIdValid()) { 219 super.onInputEvent(event, policyFlags); 220 return; 221 } 222 223 if (event instanceof MotionEvent) { 224 if ((mEnabledFeatures & FEATURES_AFFECTING_MOTION_EVENTS) != 0) { 225 MotionEvent motionEvent = (MotionEvent) event; 226 processMotionEvent(state, motionEvent, policyFlags); 227 return; 228 } else { 229 super.onInputEvent(event, policyFlags); 230 } 231 } else if (event instanceof KeyEvent) { 232 KeyEvent keyEvent = (KeyEvent) event; 233 processKeyEvent(state, keyEvent, policyFlags); 234 } 235 } 236 237 /** 238 * Gets current event stream state associated with an input event. 239 * @return The event stream state that should be used for the event. Null if the event should 240 * not be handled by #AccessibilityInputFilter. 241 */ 242 private EventStreamState getEventStreamState(InputEvent event) { 243 if (event instanceof MotionEvent) { 244 if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) { 245 if (mTouchScreenStreamState == null) { 246 mTouchScreenStreamState = new TouchScreenEventStreamState(); 247 } 248 return mTouchScreenStreamState; 249 } 250 if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { 251 if (mMouseStreamState == null) { 252 mMouseStreamState = new MouseEventStreamState(); 253 } 254 return mMouseStreamState; 255 } 256 } else if (event instanceof KeyEvent) { 257 if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) { 258 if (mKeyboardStreamState == null) { 259 mKeyboardStreamState = new KeyboardEventStreamState(); 260 } 261 return mKeyboardStreamState; 262 } 263 } 264 return null; 265 } 266 267 private void processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags) { 268 if (!state.shouldProcessScroll() && event.getActionMasked() == MotionEvent.ACTION_SCROLL) { 269 super.onInputEvent(event, policyFlags); 270 return; 271 } 272 273 if (!state.shouldProcessMotionEvent(event)) { 274 return; 275 } 276 277 batchMotionEvent(event, policyFlags); 278 } 279 280 private void processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags) { 281 if (!state.shouldProcessKeyEvent(event)) { 282 super.onInputEvent(event, policyFlags); 283 return; 284 } 285 mEventHandler.onKeyEvent(event, policyFlags); 286 } 287 288 private void scheduleProcessBatchedEvents() { 289 mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, 290 mProcessBatchedEventsRunnable, null); 291 } 292 293 private void batchMotionEvent(MotionEvent event, int policyFlags) { 294 if (DEBUG) { 295 Slog.i(TAG, "Batching event: " + event + ", policyFlags: " + policyFlags); 296 } 297 if (mEventQueue == null) { 298 mEventQueue = MotionEventHolder.obtain(event, policyFlags); 299 scheduleProcessBatchedEvents(); 300 return; 301 } 302 if (mEventQueue.event.addBatch(event)) { 303 return; 304 } 305 MotionEventHolder holder = MotionEventHolder.obtain(event, policyFlags); 306 holder.next = mEventQueue; 307 mEventQueue.previous = holder; 308 mEventQueue = holder; 309 } 310 311 private void processBatchedEvents(long frameNanos) { 312 MotionEventHolder current = mEventQueue; 313 if (current == null) { 314 return; 315 } 316 while (current.next != null) { 317 current = current.next; 318 } 319 while (true) { 320 if (current == null) { 321 mEventQueue = null; 322 break; 323 } 324 if (current.event.getEventTimeNano() >= frameNanos) { 325 // Finished with this choreographer frame. Do the rest on the next one. 326 current.next = null; 327 break; 328 } 329 handleMotionEvent(current.event, current.policyFlags); 330 MotionEventHolder prior = current; 331 current = current.previous; 332 prior.recycle(); 333 } 334 } 335 336 private void handleMotionEvent(MotionEvent event, int policyFlags) { 337 if (DEBUG) { 338 Slog.i(TAG, "Handling batched event: " + event + ", policyFlags: " + policyFlags); 339 } 340 // Since we do batch processing it is possible that by the time the 341 // next batch is processed the event handle had been set to null. 342 if (mEventHandler != null) { 343 mPm.userActivity(event.getEventTime(), false); 344 MotionEvent transformedEvent = MotionEvent.obtain(event); 345 mEventHandler.onMotionEvent(transformedEvent, event, policyFlags); 346 transformedEvent.recycle(); 347 } else { 348 if (DEBUG) Slog.d(TAG, "mEventHandler == null for " + event); 349 } 350 } 351 352 @Override 353 public void onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent, 354 int policyFlags) { 355 sendInputEvent(transformedEvent, policyFlags); 356 } 357 358 @Override 359 public void onKeyEvent(KeyEvent event, int policyFlags) { 360 sendInputEvent(event, policyFlags); 361 } 362 363 @Override 364 public void onAccessibilityEvent(AccessibilityEvent event) { 365 // TODO Implement this to inject the accessibility event 366 // into the accessibility manager service similarly 367 // to how this is done for input events. 368 } 369 370 @Override 371 public void setNext(EventStreamTransformation sink) { 372 /* do nothing */ 373 } 374 375 @Override 376 public EventStreamTransformation getNext() { 377 return null; 378 } 379 380 @Override 381 public void clearEvents(int inputSource) { 382 /* do nothing */ 383 } 384 385 void setUserAndEnabledFeatures(int userId, int enabledFeatures) { 386 if (DEBUG) { 387 Slog.i(TAG, "setUserAndEnabledFeatures(userId = " + userId + ", enabledFeatures = 0x" 388 + Integer.toHexString(enabledFeatures) + ")"); 389 } 390 if (mEnabledFeatures == enabledFeatures && mUserId == userId) { 391 return; 392 } 393 if (mInstalled) { 394 disableFeatures(); 395 } 396 mUserId = userId; 397 mEnabledFeatures = enabledFeatures; 398 if (mInstalled) { 399 enableFeatures(); 400 } 401 } 402 403 void notifyAccessibilityEvent(AccessibilityEvent event) { 404 if (mEventHandler != null) { 405 mEventHandler.onAccessibilityEvent(event); 406 } 407 } 408 409 void notifyAccessibilityButtonClicked() { 410 if (mMagnificationGestureHandler != null) { 411 mMagnificationGestureHandler.notifyShortcutTriggered(); 412 } 413 } 414 415 private void enableFeatures() { 416 if (DEBUG) Slog.i(TAG, "enableFeatures()"); 417 418 resetStreamState(); 419 420 if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) { 421 mAutoclickController = new AutoclickController(mContext, mUserId); 422 addFirstEventHandler(mAutoclickController); 423 } 424 425 if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) { 426 mTouchExplorer = new TouchExplorer(mContext, mAms); 427 addFirstEventHandler(mTouchExplorer); 428 } 429 430 if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0 431 || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) 432 || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) { 433 final boolean detectControlGestures = (mEnabledFeatures 434 & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0; 435 final boolean triggerable = (mEnabledFeatures 436 & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0; 437 mMagnificationGestureHandler = new MagnificationGestureHandler( 438 mContext, mAms.getMagnificationController(), 439 detectControlGestures, triggerable); 440 addFirstEventHandler(mMagnificationGestureHandler); 441 } 442 443 if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) { 444 mMotionEventInjector = new MotionEventInjector(mContext.getMainLooper()); 445 addFirstEventHandler(mMotionEventInjector); 446 mAms.setMotionEventInjector(mMotionEventInjector); 447 } 448 449 if ((mEnabledFeatures & FLAG_FEATURE_FILTER_KEY_EVENTS) != 0) { 450 mKeyboardInterceptor = new KeyboardInterceptor(mAms, 451 LocalServices.getService(WindowManagerPolicy.class)); 452 addFirstEventHandler(mKeyboardInterceptor); 453 } 454 } 455 456 /** 457 * Adds an event handler to the event handler chain. The handler is added at the beginning of 458 * the chain. 459 * 460 * @param handler The handler to be added to the event handlers list. 461 */ 462 private void addFirstEventHandler(EventStreamTransformation handler) { 463 if (mEventHandler != null) { 464 handler.setNext(mEventHandler); 465 } else { 466 handler.setNext(this); 467 } 468 mEventHandler = handler; 469 } 470 471 private void disableFeatures() { 472 // Give the features a chance to process any batched events so we'll keep a consistent 473 // event stream 474 processBatchedEvents(Long.MAX_VALUE); 475 if (mMotionEventInjector != null) { 476 mAms.setMotionEventInjector(null); 477 mMotionEventInjector.onDestroy(); 478 mMotionEventInjector = null; 479 } 480 if (mAutoclickController != null) { 481 mAutoclickController.onDestroy(); 482 mAutoclickController = null; 483 } 484 if (mTouchExplorer != null) { 485 mTouchExplorer.onDestroy(); 486 mTouchExplorer = null; 487 } 488 if (mMagnificationGestureHandler != null) { 489 mMagnificationGestureHandler.onDestroy(); 490 mMagnificationGestureHandler = null; 491 } 492 if (mKeyboardInterceptor != null) { 493 mKeyboardInterceptor.onDestroy(); 494 mKeyboardInterceptor = null; 495 } 496 497 mEventHandler = null; 498 resetStreamState(); 499 } 500 501 void resetStreamState() { 502 if (mTouchScreenStreamState != null) { 503 mTouchScreenStreamState.reset(); 504 } 505 if (mMouseStreamState != null) { 506 mMouseStreamState.reset(); 507 } 508 if (mKeyboardStreamState != null) { 509 mKeyboardStreamState.reset(); 510 } 511 } 512 513 @Override 514 public void onDestroy() { 515 /* ignore */ 516 } 517 518 private static class MotionEventHolder { 519 private static final int MAX_POOL_SIZE = 32; 520 private static final SimplePool<MotionEventHolder> sPool = 521 new SimplePool<MotionEventHolder>(MAX_POOL_SIZE); 522 523 public int policyFlags; 524 public MotionEvent event; 525 public MotionEventHolder next; 526 public MotionEventHolder previous; 527 528 public static MotionEventHolder obtain(MotionEvent event, int policyFlags) { 529 MotionEventHolder holder = sPool.acquire(); 530 if (holder == null) { 531 holder = new MotionEventHolder(); 532 } 533 holder.event = MotionEvent.obtain(event); 534 holder.policyFlags = policyFlags; 535 return holder; 536 } 537 538 public void recycle() { 539 event.recycle(); 540 event = null; 541 policyFlags = 0; 542 next = null; 543 previous = null; 544 sPool.release(this); 545 } 546 } 547 548 /** 549 * Keeps state of event streams observed for an input device with a certain source. 550 * Provides information about whether motion and key events should be processed by accessibility 551 * #EventStreamTransformations. Base implementation describes behaviour for event sources that 552 * whose events should not be handled by a11y event stream transformations. 553 */ 554 private static class EventStreamState { 555 private int mDeviceId; 556 557 EventStreamState() { 558 mDeviceId = -1; 559 } 560 561 /** 562 * Updates the ID of the device associated with the state. If the ID changes, resets 563 * internal state. 564 * 565 * @param deviceId Updated input device ID. 566 * @return Whether the device ID has changed. 567 */ 568 public boolean updateDeviceId(int deviceId) { 569 if (mDeviceId == deviceId) { 570 return false; 571 } 572 // Reset clears internal state, so make sure it's called before |mDeviceId| is updated. 573 reset(); 574 mDeviceId = deviceId; 575 return true; 576 } 577 578 /** 579 * @return Whether device ID is valid. 580 */ 581 public boolean deviceIdValid() { 582 return mDeviceId >= 0; 583 } 584 585 /** 586 * Resets the event stream state. 587 */ 588 public void reset() { 589 mDeviceId = -1; 590 } 591 592 /** 593 * @return Whether scroll events for device should be handled by event transformations. 594 */ 595 public boolean shouldProcessScroll() { 596 return false; 597 } 598 599 /** 600 * @param event An observed motion event. 601 * @return Whether the event should be handled by event transformations. 602 */ 603 public boolean shouldProcessMotionEvent(MotionEvent event) { 604 return false; 605 } 606 607 /** 608 * @param event An observed key event. 609 * @return Whether the event should be handled by event transformations. 610 */ 611 public boolean shouldProcessKeyEvent(KeyEvent event) { 612 return false; 613 } 614 } 615 616 /** 617 * Keeps state of stream of events from a mouse device. 618 */ 619 private static class MouseEventStreamState extends EventStreamState { 620 private boolean mMotionSequenceStarted; 621 622 public MouseEventStreamState() { 623 reset(); 624 } 625 626 @Override 627 final public void reset() { 628 super.reset(); 629 mMotionSequenceStarted = false; 630 } 631 632 @Override 633 final public boolean shouldProcessScroll() { 634 return true; 635 } 636 637 @Override 638 final public boolean shouldProcessMotionEvent(MotionEvent event) { 639 if (mMotionSequenceStarted) { 640 return true; 641 } 642 // Wait for down or move event to start processing mouse events. 643 int action = event.getActionMasked(); 644 mMotionSequenceStarted = 645 action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_HOVER_MOVE; 646 return mMotionSequenceStarted; 647 } 648 } 649 650 /** 651 * Keeps state of stream of events from a touch screen device. 652 */ 653 private static class TouchScreenEventStreamState extends EventStreamState { 654 private boolean mTouchSequenceStarted; 655 private boolean mHoverSequenceStarted; 656 657 public TouchScreenEventStreamState() { 658 reset(); 659 } 660 661 @Override 662 final public void reset() { 663 super.reset(); 664 mTouchSequenceStarted = false; 665 mHoverSequenceStarted = false; 666 } 667 668 @Override 669 final public boolean shouldProcessMotionEvent(MotionEvent event) { 670 // Wait for a down touch event to start processing. 671 if (event.isTouchEvent()) { 672 if (mTouchSequenceStarted) { 673 return true; 674 } 675 mTouchSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_DOWN; 676 return mTouchSequenceStarted; 677 } 678 679 // Wait for an enter hover event to start processing. 680 if (mHoverSequenceStarted) { 681 return true; 682 } 683 mHoverSequenceStarted = event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER; 684 return mHoverSequenceStarted; 685 } 686 } 687 688 /** 689 * Keeps state of streams of events from all keyboard devices. 690 */ 691 private static class KeyboardEventStreamState extends EventStreamState { 692 private SparseBooleanArray mEventSequenceStartedMap = new SparseBooleanArray(); 693 694 public KeyboardEventStreamState() { 695 reset(); 696 } 697 698 @Override 699 final public void reset() { 700 super.reset(); 701 mEventSequenceStartedMap.clear(); 702 } 703 704 /* 705 * Key events from different devices may be interleaved. For example, the volume up and 706 * down keys can come from different device IDs. 707 */ 708 @Override 709 public boolean updateDeviceId(int deviceId) { 710 return false; 711 } 712 713 // We manage all device ids simultaneously; there is no concept of validity. 714 @Override 715 public boolean deviceIdValid() { 716 return true; 717 } 718 719 720 @Override 721 final public boolean shouldProcessKeyEvent(KeyEvent event) { 722 // For each keyboard device, wait for a down event from a device to start processing 723 int deviceId = event.getDeviceId(); 724 if (mEventSequenceStartedMap.get(deviceId, false)) { 725 return true; 726 } 727 boolean shouldProcess = event.getAction() == KeyEvent.ACTION_DOWN; 728 mEventSequenceStartedMap.put(deviceId, shouldProcess); 729 return shouldProcess; 730 } 731 } 732 } 733