1 /* 2 ** Copyright 2017, 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.accessibilityservice.AccessibilityServiceInfo.DEFAULT; 20 import static android.view.Display.DEFAULT_DISPLAY; 21 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; 22 23 import android.accessibilityservice.AccessibilityServiceInfo; 24 import android.accessibilityservice.IAccessibilityServiceClient; 25 import android.accessibilityservice.IAccessibilityServiceConnection; 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.app.PendingIntent; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.ServiceConnection; 33 import android.content.pm.PackageManager; 34 import android.content.pm.ParceledListSlice; 35 import android.graphics.Region; 36 import android.os.Binder; 37 import android.os.Build; 38 import android.os.Bundle; 39 import android.os.Handler; 40 import android.os.IBinder; 41 import android.os.Looper; 42 import android.os.Message; 43 import android.os.RemoteException; 44 import android.util.Slog; 45 import android.util.SparseArray; 46 import android.view.KeyEvent; 47 import android.view.MagnificationSpec; 48 import android.view.View; 49 import android.view.accessibility.AccessibilityCache; 50 import android.view.accessibility.AccessibilityEvent; 51 import android.view.accessibility.AccessibilityNodeInfo; 52 import android.view.accessibility.AccessibilityWindowInfo; 53 import android.view.accessibility.IAccessibilityInteractionConnection; 54 import android.view.accessibility.IAccessibilityInteractionConnectionCallback; 55 56 import com.android.internal.os.SomeArgs; 57 import com.android.internal.util.DumpUtils; 58 import com.android.server.accessibility.AccessibilityManagerService.RemoteAccessibilityConnection; 59 import com.android.server.accessibility.AccessibilityManagerService.SecurityPolicy; 60 import com.android.server.wm.WindowManagerInternal; 61 62 import java.io.FileDescriptor; 63 import java.io.PrintWriter; 64 import java.util.ArrayList; 65 import java.util.Arrays; 66 import java.util.HashSet; 67 import java.util.List; 68 import java.util.Set; 69 70 /** 71 * This class represents an accessibility client - either an AccessibilityService or a UiAutomation. 72 * It is responsible for behavior common to both types of clients. 73 */ 74 abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServiceConnection.Stub 75 implements ServiceConnection, IBinder.DeathRecipient, KeyEventDispatcher.KeyEventFilter, 76 FingerprintGestureDispatcher.FingerprintGestureClient { 77 private static final boolean DEBUG = false; 78 private static final String LOG_TAG = "AbstractAccessibilityServiceConnection"; 79 80 protected final Context mContext; 81 protected final SystemSupport mSystemSupport; 82 private final WindowManagerInternal mWindowManagerService; 83 private final GlobalActionPerformer mGlobalActionPerformer; 84 85 // Handler for scheduling method invocations on the main thread. 86 public final InvocationHandler mInvocationHandler; 87 88 final int mId; 89 90 protected final AccessibilityServiceInfo mAccessibilityServiceInfo; 91 92 // Lock must match the one used by AccessibilityManagerService 93 protected final Object mLock; 94 95 protected final SecurityPolicy mSecurityPolicy; 96 97 // The service that's bound to this instance. Whenever this value is non-null, this 98 // object is registered as a death recipient 99 IBinder mService; 100 101 IAccessibilityServiceClient mServiceInterface; 102 103 int mEventTypes; 104 105 int mFeedbackType; 106 107 Set<String> mPackageNames = new HashSet<>(); 108 109 boolean mIsDefault; 110 111 boolean mRequestTouchExplorationMode; 112 113 boolean mRequestFilterKeyEvents; 114 115 boolean mRetrieveInteractiveWindows; 116 117 boolean mCaptureFingerprintGestures; 118 119 boolean mRequestAccessibilityButton; 120 121 boolean mReceivedAccessibilityButtonCallbackSinceBind; 122 123 boolean mLastAccessibilityButtonCallbackState; 124 125 int mFetchFlags; 126 127 long mNotificationTimeout; 128 129 final ComponentName mComponentName; 130 131 // the events pending events to be dispatched to this service 132 final SparseArray<AccessibilityEvent> mPendingEvents = new SparseArray<>(); 133 134 /** Whether this service relies on its {@link AccessibilityCache} being up to date */ 135 boolean mUsesAccessibilityCache = false; 136 137 // Handler only for dispatching accessibility events since we use event 138 // types as message types allowing us to remove messages per event type. 139 public Handler mEventDispatchHandler; 140 141 final IBinder mOverlayWindowToken = new Binder(); 142 143 144 public interface SystemSupport { 145 /** 146 * @return The current dispatcher for key events 147 */ 148 @NonNull KeyEventDispatcher getKeyEventDispatcher(); 149 150 /** 151 * @param windowId The id of the window of interest 152 * @return The magnification spec for the window, or {@code null} if none is available 153 */ 154 @Nullable MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId); 155 156 /** 157 * @return The current injector of motion events, if one exists 158 */ 159 @Nullable MotionEventInjector getMotionEventInjectorLocked(); 160 161 /** 162 * @return The current dispatcher for fingerprint gestures, if one exists 163 */ 164 @Nullable FingerprintGestureDispatcher getFingerprintGestureDispatcher(); 165 166 /** 167 * @return The magnification controller 168 */ 169 @NonNull MagnificationController getMagnificationController(); 170 171 /** 172 * Resolve a connection wrapper for a window id 173 * 174 * @param windowId The id of the window of interest 175 * 176 * @return a connection to the window 177 */ 178 RemoteAccessibilityConnection getConnectionLocked(int windowId); 179 180 /** 181 * Perform the specified accessibility action 182 * 183 * @param resolvedWindowId The window ID 184 * [Other parameters match the method on IAccessibilityServiceConnection] 185 * 186 * @return Whether or not the action could be sent to the app process 187 */ 188 boolean performAccessibilityAction(int resolvedWindowId, 189 long accessibilityNodeId, int action, Bundle arguments, int interactionId, 190 IAccessibilityInteractionConnectionCallback callback, int fetchFlags, 191 long interrogatingTid); 192 193 /** 194 * Replace the interaction callback if needed, for example if the window is in picture- 195 * in-picture mode and needs its nodes replaced. 196 * 197 * @param originalCallback The callback we were planning to use 198 * @param resolvedWindowId The ID of the window we're calling 199 * @param interactionId The id for the original callback 200 * @param interrogatingPid Process ID of requester 201 * @param interrogatingTid Thread ID of requester 202 * 203 * @return The callback to use, which may be the original one. 204 */ 205 @NonNull IAccessibilityInteractionConnectionCallback replaceCallbackIfNeeded( 206 IAccessibilityInteractionConnectionCallback originalCallback, 207 int resolvedWindowId, int interactionId, int interrogatingPid, 208 long interrogatingTid); 209 210 /** 211 * Request that the system make sure windows are available to interrogate 212 */ 213 void ensureWindowsAvailableTimed(); 214 215 /** 216 * Called back to notify system that the client has changed 217 * @param serviceInfoChanged True if the service's AccessibilityServiceInfo changed. 218 */ 219 void onClientChange(boolean serviceInfoChanged); 220 221 int getCurrentUserIdLocked(); 222 223 boolean isAccessibilityButtonShown(); 224 225 /** 226 * Persists the component names in the specified setting in a 227 * colon separated fashion. 228 * 229 * @param settingName The setting name. 230 * @param componentNames The component names. 231 * @param userId The user id to persist the setting for. 232 */ 233 void persistComponentNamesToSettingLocked(String settingName, 234 Set<ComponentName> componentNames, int userId); 235 236 /* This is exactly PendingIntent.getActivity, separated out for testability */ 237 PendingIntent getPendingIntentActivity(Context context, int requestCode, Intent intent, 238 int flags); 239 } 240 241 public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName, 242 AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler, 243 Object lock, SecurityPolicy securityPolicy, SystemSupport systemSupport, 244 WindowManagerInternal windowManagerInternal, 245 GlobalActionPerformer globalActionPerfomer) { 246 mContext = context; 247 mWindowManagerService = windowManagerInternal; 248 mId = id; 249 mComponentName = componentName; 250 mAccessibilityServiceInfo = accessibilityServiceInfo; 251 mLock = lock; 252 mSecurityPolicy = securityPolicy; 253 mGlobalActionPerformer = globalActionPerfomer; 254 mSystemSupport = systemSupport; 255 mInvocationHandler = new InvocationHandler(mainHandler.getLooper()); 256 mEventDispatchHandler = new Handler(mainHandler.getLooper()) { 257 @Override 258 public void handleMessage(Message message) { 259 final int eventType = message.what; 260 AccessibilityEvent event = (AccessibilityEvent) message.obj; 261 boolean serviceWantsEvent = message.arg1 != 0; 262 notifyAccessibilityEventInternal(eventType, event, serviceWantsEvent); 263 } 264 }; 265 setDynamicallyConfigurableProperties(accessibilityServiceInfo); 266 } 267 268 @Override 269 public boolean onKeyEvent(KeyEvent keyEvent, int sequenceNumber) { 270 if (!mRequestFilterKeyEvents || (mServiceInterface == null)) { 271 return false; 272 } 273 if((mAccessibilityServiceInfo.getCapabilities() 274 & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) == 0) { 275 return false; 276 } 277 try { 278 mServiceInterface.onKeyEvent(keyEvent, sequenceNumber); 279 } catch (RemoteException e) { 280 return false; 281 } 282 return true; 283 } 284 285 public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) { 286 mEventTypes = info.eventTypes; 287 mFeedbackType = info.feedbackType; 288 String[] packageNames = info.packageNames; 289 if (packageNames != null) { 290 mPackageNames.addAll(Arrays.asList(packageNames)); 291 } 292 mNotificationTimeout = info.notificationTimeout; 293 mIsDefault = (info.flags & DEFAULT) != 0; 294 295 if (supportsFlagForNotImportantViews(info)) { 296 if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) { 297 mFetchFlags |= AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS; 298 } else { 299 mFetchFlags &= ~AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS; 300 } 301 } 302 303 if ((info.flags & AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS) != 0) { 304 mFetchFlags |= AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS; 305 } else { 306 mFetchFlags &= ~AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS; 307 } 308 309 mRequestTouchExplorationMode = (info.flags 310 & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0; 311 mRequestFilterKeyEvents = (info.flags 312 & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0; 313 mRetrieveInteractiveWindows = (info.flags 314 & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0; 315 mCaptureFingerprintGestures = (info.flags 316 & AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES) != 0; 317 mRequestAccessibilityButton = (info.flags 318 & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; 319 } 320 321 protected boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) { 322 return info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion 323 >= Build.VERSION_CODES.JELLY_BEAN; 324 } 325 326 public boolean canReceiveEventsLocked() { 327 return (mEventTypes != 0 && mFeedbackType != 0 && mService != null); 328 } 329 330 @Override 331 public void setOnKeyEventResult(boolean handled, int sequence) { 332 mSystemSupport.getKeyEventDispatcher().setOnKeyEventResult(this, handled, sequence); 333 } 334 335 @Override 336 public AccessibilityServiceInfo getServiceInfo() { 337 synchronized (mLock) { 338 return mAccessibilityServiceInfo; 339 } 340 } 341 342 public int getCapabilities() { 343 return mAccessibilityServiceInfo.getCapabilities(); 344 } 345 346 int getRelevantEventTypes() { 347 return (mUsesAccessibilityCache ? AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK : 0) 348 | mEventTypes; 349 } 350 351 @Override 352 public void setServiceInfo(AccessibilityServiceInfo info) { 353 final long identity = Binder.clearCallingIdentity(); 354 try { 355 synchronized (mLock) { 356 // If the XML manifest had data to configure the service its info 357 // should be already set. In such a case update only the dynamically 358 // configurable properties. 359 AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo; 360 if (oldInfo != null) { 361 oldInfo.updateDynamicallyConfigurableProperties(info); 362 setDynamicallyConfigurableProperties(oldInfo); 363 } else { 364 setDynamicallyConfigurableProperties(info); 365 } 366 mSystemSupport.onClientChange(true); 367 } 368 } finally { 369 Binder.restoreCallingIdentity(identity); 370 } 371 } 372 373 protected abstract boolean isCalledForCurrentUserLocked(); 374 375 @Override 376 public List<AccessibilityWindowInfo> getWindows() { 377 mSystemSupport.ensureWindowsAvailableTimed(); 378 synchronized (mLock) { 379 if (!isCalledForCurrentUserLocked()) { 380 return null; 381 } 382 final boolean permissionGranted = 383 mSecurityPolicy.canRetrieveWindowsLocked(this); 384 if (!permissionGranted) { 385 return null; 386 } 387 if (mSecurityPolicy.mWindows == null) { 388 return null; 389 } 390 List<AccessibilityWindowInfo> windows = new ArrayList<>(); 391 final int windowCount = mSecurityPolicy.mWindows.size(); 392 for (int i = 0; i < windowCount; i++) { 393 AccessibilityWindowInfo window = mSecurityPolicy.mWindows.get(i); 394 AccessibilityWindowInfo windowClone = 395 AccessibilityWindowInfo.obtain(window); 396 windowClone.setConnectionId(mId); 397 windows.add(windowClone); 398 } 399 return windows; 400 } 401 } 402 403 @Override 404 public AccessibilityWindowInfo getWindow(int windowId) { 405 mSystemSupport.ensureWindowsAvailableTimed(); 406 synchronized (mLock) { 407 if (!isCalledForCurrentUserLocked()) { 408 return null; 409 } 410 final boolean permissionGranted = 411 mSecurityPolicy.canRetrieveWindowsLocked(this); 412 if (!permissionGranted) { 413 return null; 414 } 415 AccessibilityWindowInfo window = mSecurityPolicy.findA11yWindowInfoById(windowId); 416 if (window != null) { 417 AccessibilityWindowInfo windowClone = AccessibilityWindowInfo.obtain(window); 418 windowClone.setConnectionId(mId); 419 return windowClone; 420 } 421 return null; 422 } 423 } 424 425 @Override 426 public String[] findAccessibilityNodeInfosByViewId(int accessibilityWindowId, 427 long accessibilityNodeId, String viewIdResName, int interactionId, 428 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) 429 throws RemoteException { 430 final int resolvedWindowId; 431 RemoteAccessibilityConnection connection; 432 Region partialInteractiveRegion = Region.obtain(); 433 MagnificationSpec spec; 434 synchronized (mLock) { 435 mUsesAccessibilityCache = true; 436 if (!isCalledForCurrentUserLocked()) { 437 return null; 438 } 439 resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); 440 final boolean permissionGranted = 441 mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); 442 if (!permissionGranted) { 443 return null; 444 } else { 445 connection = mSystemSupport.getConnectionLocked(resolvedWindowId); 446 if (connection == null) { 447 return null; 448 } 449 } 450 if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( 451 resolvedWindowId, partialInteractiveRegion)) { 452 partialInteractiveRegion.recycle(); 453 partialInteractiveRegion = null; 454 } 455 spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId); 456 } 457 final int interrogatingPid = Binder.getCallingPid(); 458 callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, 459 interrogatingPid, interrogatingTid); 460 final int callingUid = Binder.getCallingUid(); 461 final long identityToken = Binder.clearCallingIdentity(); 462 try { 463 connection.getRemote().findAccessibilityNodeInfosByViewId(accessibilityNodeId, 464 viewIdResName, partialInteractiveRegion, interactionId, callback, mFetchFlags, 465 interrogatingPid, interrogatingTid, spec); 466 return mSecurityPolicy.computeValidReportedPackages(callingUid, 467 connection.getPackageName(), connection.getUid()); 468 } catch (RemoteException re) { 469 if (DEBUG) { 470 Slog.e(LOG_TAG, "Error findAccessibilityNodeInfoByViewId()."); 471 } 472 } finally { 473 Binder.restoreCallingIdentity(identityToken); 474 // Recycle if passed to another process. 475 if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) { 476 partialInteractiveRegion.recycle(); 477 } 478 } 479 return null; 480 } 481 482 @Override 483 public String[] findAccessibilityNodeInfosByText(int accessibilityWindowId, 484 long accessibilityNodeId, String text, int interactionId, 485 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) 486 throws RemoteException { 487 final int resolvedWindowId; 488 RemoteAccessibilityConnection connection; 489 Region partialInteractiveRegion = Region.obtain(); 490 MagnificationSpec spec; 491 synchronized (mLock) { 492 mUsesAccessibilityCache = true; 493 if (!isCalledForCurrentUserLocked()) { 494 return null; 495 } 496 resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); 497 final boolean permissionGranted = 498 mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); 499 if (!permissionGranted) { 500 return null; 501 } else { 502 connection = mSystemSupport.getConnectionLocked(resolvedWindowId); 503 if (connection == null) { 504 return null; 505 } 506 } 507 if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( 508 resolvedWindowId, partialInteractiveRegion)) { 509 partialInteractiveRegion.recycle(); 510 partialInteractiveRegion = null; 511 } 512 spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId); 513 } 514 final int interrogatingPid = Binder.getCallingPid(); 515 callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, 516 interrogatingPid, interrogatingTid); 517 final int callingUid = Binder.getCallingUid(); 518 final long identityToken = Binder.clearCallingIdentity(); 519 try { 520 connection.getRemote().findAccessibilityNodeInfosByText(accessibilityNodeId, 521 text, partialInteractiveRegion, interactionId, callback, mFetchFlags, 522 interrogatingPid, interrogatingTid, spec); 523 return mSecurityPolicy.computeValidReportedPackages(callingUid, 524 connection.getPackageName(), connection.getUid()); 525 } catch (RemoteException re) { 526 if (DEBUG) { 527 Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()"); 528 } 529 } finally { 530 Binder.restoreCallingIdentity(identityToken); 531 // Recycle if passed to another process. 532 if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) { 533 partialInteractiveRegion.recycle(); 534 } 535 } 536 return null; 537 } 538 539 @Override 540 public String[] findAccessibilityNodeInfoByAccessibilityId( 541 int accessibilityWindowId, long accessibilityNodeId, int interactionId, 542 IAccessibilityInteractionConnectionCallback callback, int flags, 543 long interrogatingTid, Bundle arguments) throws RemoteException { 544 final int resolvedWindowId; 545 RemoteAccessibilityConnection connection; 546 Region partialInteractiveRegion = Region.obtain(); 547 MagnificationSpec spec; 548 synchronized (mLock) { 549 mUsesAccessibilityCache = true; 550 if (!isCalledForCurrentUserLocked()) { 551 return null; 552 } 553 resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); 554 final boolean permissionGranted = 555 mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); 556 if (!permissionGranted) { 557 return null; 558 } else { 559 connection = mSystemSupport.getConnectionLocked(resolvedWindowId); 560 if (connection == null) { 561 return null; 562 } 563 } 564 if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( 565 resolvedWindowId, partialInteractiveRegion)) { 566 partialInteractiveRegion.recycle(); 567 partialInteractiveRegion = null; 568 } 569 spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId); 570 } 571 final int interrogatingPid = Binder.getCallingPid(); 572 callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, 573 interrogatingPid, interrogatingTid); 574 final int callingUid = Binder.getCallingUid(); 575 final long identityToken = Binder.clearCallingIdentity(); 576 try { 577 connection.getRemote().findAccessibilityNodeInfoByAccessibilityId( 578 accessibilityNodeId, partialInteractiveRegion, interactionId, callback, 579 mFetchFlags | flags, interrogatingPid, interrogatingTid, spec, arguments); 580 return mSecurityPolicy.computeValidReportedPackages(callingUid, 581 connection.getPackageName(), connection.getUid()); 582 } catch (RemoteException re) { 583 if (DEBUG) { 584 Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()"); 585 } 586 } finally { 587 Binder.restoreCallingIdentity(identityToken); 588 // Recycle if passed to another process. 589 if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) { 590 partialInteractiveRegion.recycle(); 591 } 592 } 593 return null; 594 } 595 596 @Override 597 public String[] findFocus(int accessibilityWindowId, long accessibilityNodeId, 598 int focusType, int interactionId, 599 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) 600 throws RemoteException { 601 final int resolvedWindowId; 602 RemoteAccessibilityConnection connection; 603 Region partialInteractiveRegion = Region.obtain(); 604 MagnificationSpec spec; 605 synchronized (mLock) { 606 if (!isCalledForCurrentUserLocked()) { 607 return null; 608 } 609 resolvedWindowId = resolveAccessibilityWindowIdForFindFocusLocked( 610 accessibilityWindowId, focusType); 611 final boolean permissionGranted = 612 mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); 613 if (!permissionGranted) { 614 return null; 615 } else { 616 connection = mSystemSupport.getConnectionLocked(resolvedWindowId); 617 if (connection == null) { 618 return null; 619 } 620 } 621 if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( 622 resolvedWindowId, partialInteractiveRegion)) { 623 partialInteractiveRegion.recycle(); 624 partialInteractiveRegion = null; 625 } 626 spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId); 627 } 628 final int interrogatingPid = Binder.getCallingPid(); 629 callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, 630 interrogatingPid, interrogatingTid); 631 final int callingUid = Binder.getCallingUid(); 632 final long identityToken = Binder.clearCallingIdentity(); 633 try { 634 connection.getRemote().findFocus(accessibilityNodeId, focusType, 635 partialInteractiveRegion, interactionId, callback, mFetchFlags, 636 interrogatingPid, interrogatingTid, spec); 637 return mSecurityPolicy.computeValidReportedPackages(callingUid, 638 connection.getPackageName(), connection.getUid()); 639 } catch (RemoteException re) { 640 if (DEBUG) { 641 Slog.e(LOG_TAG, "Error calling findFocus()"); 642 } 643 } finally { 644 Binder.restoreCallingIdentity(identityToken); 645 // Recycle if passed to another process. 646 if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) { 647 partialInteractiveRegion.recycle(); 648 } 649 } 650 return null; 651 } 652 653 @Override 654 public String[] focusSearch(int accessibilityWindowId, long accessibilityNodeId, 655 int direction, int interactionId, 656 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) 657 throws RemoteException { 658 final int resolvedWindowId; 659 RemoteAccessibilityConnection connection; 660 Region partialInteractiveRegion = Region.obtain(); 661 MagnificationSpec spec; 662 synchronized (mLock) { 663 if (!isCalledForCurrentUserLocked()) { 664 return null; 665 } 666 resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); 667 final boolean permissionGranted = 668 mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId); 669 if (!permissionGranted) { 670 return null; 671 } else { 672 connection = mSystemSupport.getConnectionLocked(resolvedWindowId); 673 if (connection == null) { 674 return null; 675 } 676 } 677 if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked( 678 resolvedWindowId, partialInteractiveRegion)) { 679 partialInteractiveRegion.recycle(); 680 partialInteractiveRegion = null; 681 } 682 spec = mSystemSupport.getCompatibleMagnificationSpecLocked(resolvedWindowId); 683 } 684 final int interrogatingPid = Binder.getCallingPid(); 685 callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, 686 interrogatingPid, interrogatingTid); 687 final int callingUid = Binder.getCallingUid(); 688 final long identityToken = Binder.clearCallingIdentity(); 689 try { 690 connection.getRemote().focusSearch(accessibilityNodeId, direction, 691 partialInteractiveRegion, interactionId, callback, mFetchFlags, 692 interrogatingPid, interrogatingTid, spec); 693 return mSecurityPolicy.computeValidReportedPackages(callingUid, 694 connection.getPackageName(), connection.getUid()); 695 } catch (RemoteException re) { 696 if (DEBUG) { 697 Slog.e(LOG_TAG, "Error calling accessibilityFocusSearch()"); 698 } 699 } finally { 700 Binder.restoreCallingIdentity(identityToken); 701 // Recycle if passed to another process. 702 if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) { 703 partialInteractiveRegion.recycle(); 704 } 705 } 706 return null; 707 } 708 709 @Override 710 public void sendGesture(int sequence, ParceledListSlice gestureSteps) { 711 } 712 713 @Override 714 public boolean performAccessibilityAction(int accessibilityWindowId, 715 long accessibilityNodeId, int action, Bundle arguments, int interactionId, 716 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) 717 throws RemoteException { 718 final int resolvedWindowId; 719 IAccessibilityInteractionConnection connection = null; 720 synchronized (mLock) { 721 if (!isCalledForCurrentUserLocked()) { 722 return false; 723 } 724 resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId); 725 if (!mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId)) { 726 return false; 727 } 728 } 729 boolean returnValue = 730 mSystemSupport.performAccessibilityAction(resolvedWindowId, accessibilityNodeId, 731 action, arguments, interactionId, callback, mFetchFlags, interrogatingTid); 732 return returnValue; 733 } 734 735 @Override 736 public boolean performGlobalAction(int action) { 737 synchronized (mLock) { 738 if (!isCalledForCurrentUserLocked()) { 739 return false; 740 } 741 } 742 return mGlobalActionPerformer.performGlobalAction(action); 743 } 744 745 @Override 746 public boolean isFingerprintGestureDetectionAvailable() { 747 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { 748 return false; 749 } 750 if (isCapturingFingerprintGestures()) { 751 FingerprintGestureDispatcher dispatcher = 752 mSystemSupport.getFingerprintGestureDispatcher(); 753 return (dispatcher != null) && dispatcher.isFingerprintGestureDetectionAvailable(); 754 } 755 return false; 756 } 757 758 @Override 759 public float getMagnificationScale() { 760 synchronized (mLock) { 761 if (!isCalledForCurrentUserLocked()) { 762 return 1.0f; 763 } 764 } 765 final long identity = Binder.clearCallingIdentity(); 766 try { 767 return mSystemSupport.getMagnificationController().getScale(); 768 } finally { 769 Binder.restoreCallingIdentity(identity); 770 } 771 } 772 773 @Override 774 public Region getMagnificationRegion() { 775 synchronized (mLock) { 776 final Region region = Region.obtain(); 777 if (!isCalledForCurrentUserLocked()) { 778 return region; 779 } 780 MagnificationController magnificationController = 781 mSystemSupport.getMagnificationController(); 782 boolean registeredJustForThisCall = 783 registerMagnificationIfNeeded(magnificationController); 784 final long identity = Binder.clearCallingIdentity(); 785 try { 786 magnificationController.getMagnificationRegion(region); 787 return region; 788 } finally { 789 Binder.restoreCallingIdentity(identity); 790 if (registeredJustForThisCall) { 791 magnificationController.unregister(); 792 } 793 } 794 } 795 } 796 797 @Override 798 public float getMagnificationCenterX() { 799 synchronized (mLock) { 800 if (!isCalledForCurrentUserLocked()) { 801 return 0.0f; 802 } 803 MagnificationController magnificationController = 804 mSystemSupport.getMagnificationController(); 805 boolean registeredJustForThisCall = 806 registerMagnificationIfNeeded(magnificationController); 807 final long identity = Binder.clearCallingIdentity(); 808 try { 809 return magnificationController.getCenterX(); 810 } finally { 811 Binder.restoreCallingIdentity(identity); 812 if (registeredJustForThisCall) { 813 magnificationController.unregister(); 814 } 815 } 816 } 817 } 818 819 @Override 820 public float getMagnificationCenterY() { 821 synchronized (mLock) { 822 if (!isCalledForCurrentUserLocked()) { 823 return 0.0f; 824 } 825 MagnificationController magnificationController = 826 mSystemSupport.getMagnificationController(); 827 boolean registeredJustForThisCall = 828 registerMagnificationIfNeeded(magnificationController); 829 final long identity = Binder.clearCallingIdentity(); 830 try { 831 return magnificationController.getCenterY(); 832 } finally { 833 Binder.restoreCallingIdentity(identity); 834 if (registeredJustForThisCall) { 835 magnificationController.unregister(); 836 } 837 } 838 } 839 } 840 841 private boolean registerMagnificationIfNeeded( 842 MagnificationController magnificationController) { 843 if (!magnificationController.isRegisteredLocked() 844 && mSecurityPolicy.canControlMagnification(this)) { 845 magnificationController.register(); 846 return true; 847 } 848 return false; 849 } 850 851 @Override 852 public boolean resetMagnification(boolean animate) { 853 synchronized (mLock) { 854 if (!isCalledForCurrentUserLocked()) { 855 return false; 856 } 857 if (!mSecurityPolicy.canControlMagnification(this)) { 858 return false; 859 } 860 } 861 final long identity = Binder.clearCallingIdentity(); 862 try { 863 return mSystemSupport.getMagnificationController().reset(animate); 864 } finally { 865 Binder.restoreCallingIdentity(identity); 866 } 867 } 868 869 @Override 870 public boolean setMagnificationScaleAndCenter(float scale, float centerX, float centerY, 871 boolean animate) { 872 synchronized (mLock) { 873 if (!isCalledForCurrentUserLocked()) { 874 return false; 875 } 876 if (!mSecurityPolicy.canControlMagnification(this)) { 877 return false; 878 } 879 final long identity = Binder.clearCallingIdentity(); 880 try { 881 MagnificationController magnificationController = 882 mSystemSupport.getMagnificationController(); 883 if (!magnificationController.isRegisteredLocked()) { 884 magnificationController.register(); 885 } 886 return magnificationController 887 .setScaleAndCenter(scale, centerX, centerY, animate, mId); 888 } finally { 889 Binder.restoreCallingIdentity(identity); 890 } 891 } 892 } 893 894 @Override 895 public void setMagnificationCallbackEnabled(boolean enabled) { 896 mInvocationHandler.setMagnificationCallbackEnabled(enabled); 897 } 898 899 public boolean isMagnificationCallbackEnabled() { 900 return mInvocationHandler.mIsMagnificationCallbackEnabled; 901 } 902 903 @Override 904 public void setSoftKeyboardCallbackEnabled(boolean enabled) { 905 mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled); 906 } 907 908 @Override 909 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { 910 if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return; 911 synchronized (mLock) { 912 pw.append("Service[label=" + mAccessibilityServiceInfo.getResolveInfo() 913 .loadLabel(mContext.getPackageManager())); 914 pw.append(", feedbackType" 915 + AccessibilityServiceInfo.feedbackTypeToString(mFeedbackType)); 916 pw.append(", capabilities=" + mAccessibilityServiceInfo.getCapabilities()); 917 pw.append(", eventTypes=" 918 + AccessibilityEvent.eventTypeToString(mEventTypes)); 919 pw.append(", notificationTimeout=" + mNotificationTimeout); 920 pw.append("]"); 921 } 922 } 923 924 public void onAdded() { 925 final long identity = Binder.clearCallingIdentity(); 926 try { 927 mWindowManagerService.addWindowToken(mOverlayWindowToken, 928 TYPE_ACCESSIBILITY_OVERLAY, DEFAULT_DISPLAY); 929 } finally { 930 Binder.restoreCallingIdentity(identity); 931 } 932 } 933 934 public void onRemoved() { 935 final long identity = Binder.clearCallingIdentity(); 936 try { 937 mWindowManagerService.removeWindowToken(mOverlayWindowToken, true, DEFAULT_DISPLAY); 938 } finally { 939 Binder.restoreCallingIdentity(identity); 940 } 941 } 942 943 public void resetLocked() { 944 mSystemSupport.getKeyEventDispatcher().flush(this); 945 try { 946 // Clear the proxy in the other process so this 947 // IAccessibilityServiceConnection can be garbage collected. 948 if (mServiceInterface != null) { 949 mServiceInterface.init(null, mId, null); 950 } 951 } catch (RemoteException re) { 952 /* ignore */ 953 } 954 if (mService != null) { 955 mService.unlinkToDeath(this, 0); 956 mService = null; 957 } 958 959 mServiceInterface = null; 960 mReceivedAccessibilityButtonCallbackSinceBind = false; 961 } 962 963 public boolean isConnectedLocked() { 964 return (mService != null); 965 } 966 967 public void notifyAccessibilityEvent(AccessibilityEvent event) { 968 synchronized (mLock) { 969 final int eventType = event.getEventType(); 970 971 final boolean serviceWantsEvent = wantsEventLocked(event); 972 final boolean requiredForCacheConsistency = mUsesAccessibilityCache 973 && ((AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK & eventType) != 0); 974 if (!serviceWantsEvent && !requiredForCacheConsistency) { 975 return; 976 } 977 978 // Make a copy since during dispatch it is possible the event to 979 // be modified to remove its source if the receiving service does 980 // not have permission to access the window content. 981 AccessibilityEvent newEvent = AccessibilityEvent.obtain(event); 982 Message message; 983 if ((mNotificationTimeout > 0) 984 && (eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)) { 985 // Allow at most one pending event 986 final AccessibilityEvent oldEvent = mPendingEvents.get(eventType); 987 mPendingEvents.put(eventType, newEvent); 988 if (oldEvent != null) { 989 mEventDispatchHandler.removeMessages(eventType); 990 oldEvent.recycle(); 991 } 992 message = mEventDispatchHandler.obtainMessage(eventType); 993 } else { 994 // Send all messages, bypassing mPendingEvents 995 message = mEventDispatchHandler.obtainMessage(eventType, newEvent); 996 } 997 message.arg1 = serviceWantsEvent ? 1 : 0; 998 999 mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout); 1000 } 1001 } 1002 1003 /** 1004 * Determines if given event can be dispatched to a service based on the package of the 1005 * event source. Specifically, a service is notified if it is interested in events from the 1006 * package. 1007 * 1008 * @param event The event. 1009 * @return True if the listener should be notified, false otherwise. 1010 */ 1011 private boolean wantsEventLocked(AccessibilityEvent event) { 1012 1013 if (!canReceiveEventsLocked()) { 1014 return false; 1015 } 1016 1017 if ((event.getWindowId() != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) 1018 && !event.isImportantForAccessibility() 1019 && (mFetchFlags & AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) == 0) { 1020 return false; 1021 } 1022 1023 int eventType = event.getEventType(); 1024 if ((mEventTypes & eventType) != eventType) { 1025 return false; 1026 } 1027 1028 Set<String> packageNames = mPackageNames; 1029 String packageName = (event.getPackageName() != null) 1030 ? event.getPackageName().toString() : null; 1031 1032 return (packageNames.isEmpty() || packageNames.contains(packageName)); 1033 } 1034 1035 /** 1036 * Notifies an accessibility service client for a scheduled event given the event type. 1037 * 1038 * @param eventType The type of the event to dispatch. 1039 */ 1040 private void notifyAccessibilityEventInternal( 1041 int eventType, 1042 AccessibilityEvent event, 1043 boolean serviceWantsEvent) { 1044 IAccessibilityServiceClient listener; 1045 1046 synchronized (mLock) { 1047 listener = mServiceInterface; 1048 1049 // If the service died/was disabled while the message for dispatching 1050 // the accessibility event was propagating the listener may be null. 1051 if (listener == null) { 1052 return; 1053 } 1054 1055 // There are two ways we notify for events, throttled AND non-throttled. If we 1056 // are not throttling, then messages come with events, which we handle with 1057 // minimal fuss. 1058 if (event == null) { 1059 // We are throttling events, so we'll send the event for this type in 1060 // mPendingEvents as long as it it's null. It can only null due to a race 1061 // condition: 1062 // 1063 // 1) A binder thread calls notifyAccessibilityServiceDelayedLocked 1064 // which posts a message for dispatching an event and stores the event 1065 // in mPendingEvents. 1066 // 2) The message is pulled from the queue by the handler on the service 1067 // thread and this method is just about to acquire the lock. 1068 // 3) Another binder thread acquires the lock in notifyAccessibilityEvent 1069 // 4) notifyAccessibilityEvent recycles the event that this method was about 1070 // to process, replaces it with a new one, and posts a second message 1071 // 5) This method grabs the new event, processes it, and removes it from 1072 // mPendingEvents 1073 // 6) The second message dispatched in (4) arrives, but the event has been 1074 // remvoved in (5). 1075 event = mPendingEvents.get(eventType); 1076 if (event == null) { 1077 return; 1078 } 1079 mPendingEvents.remove(eventType); 1080 } 1081 if (mSecurityPolicy.canRetrieveWindowContentLocked(this)) { 1082 event.setConnectionId(mId); 1083 } else { 1084 event.setSource((View) null); 1085 } 1086 event.setSealed(true); 1087 } 1088 1089 try { 1090 listener.onAccessibilityEvent(event, serviceWantsEvent); 1091 if (DEBUG) { 1092 Slog.i(LOG_TAG, "Event " + event + " sent to " + listener); 1093 } 1094 } catch (RemoteException re) { 1095 Slog.e(LOG_TAG, "Error during sending " + event + " to " + listener, re); 1096 } finally { 1097 event.recycle(); 1098 } 1099 } 1100 1101 public void notifyGesture(int gestureId) { 1102 mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE, 1103 gestureId, 0).sendToTarget(); 1104 } 1105 1106 public void notifyClearAccessibilityNodeInfoCache() { 1107 mInvocationHandler.sendEmptyMessage( 1108 InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE); 1109 } 1110 1111 public void notifyMagnificationChangedLocked(@NonNull Region region, 1112 float scale, float centerX, float centerY) { 1113 mInvocationHandler 1114 .notifyMagnificationChangedLocked(region, scale, centerX, centerY); 1115 } 1116 1117 public void notifySoftKeyboardShowModeChangedLocked(int showState) { 1118 mInvocationHandler.notifySoftKeyboardShowModeChangedLocked(showState); 1119 } 1120 1121 public void notifyAccessibilityButtonClickedLocked() { 1122 mInvocationHandler.notifyAccessibilityButtonClickedLocked(); 1123 } 1124 1125 public void notifyAccessibilityButtonAvailabilityChangedLocked(boolean available) { 1126 mInvocationHandler.notifyAccessibilityButtonAvailabilityChangedLocked(available); 1127 } 1128 1129 /** 1130 * Called by the invocation handler to notify the service that the 1131 * state of magnification has changed. 1132 */ 1133 private void notifyMagnificationChangedInternal(@NonNull Region region, 1134 float scale, float centerX, float centerY) { 1135 final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); 1136 if (listener != null) { 1137 try { 1138 listener.onMagnificationChanged(region, scale, centerX, centerY); 1139 } catch (RemoteException re) { 1140 Slog.e(LOG_TAG, "Error sending magnification changes to " + mService, re); 1141 } 1142 } 1143 } 1144 1145 /** 1146 * Called by the invocation handler to notify the service that the state of the soft 1147 * keyboard show mode has changed. 1148 */ 1149 private void notifySoftKeyboardShowModeChangedInternal(int showState) { 1150 final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); 1151 if (listener != null) { 1152 try { 1153 listener.onSoftKeyboardShowModeChanged(showState); 1154 } catch (RemoteException re) { 1155 Slog.e(LOG_TAG, "Error sending soft keyboard show mode changes to " + mService, 1156 re); 1157 } 1158 } 1159 } 1160 1161 private void notifyAccessibilityButtonClickedInternal() { 1162 final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); 1163 if (listener != null) { 1164 try { 1165 listener.onAccessibilityButtonClicked(); 1166 } catch (RemoteException re) { 1167 Slog.e(LOG_TAG, "Error sending accessibility button click to " + mService, re); 1168 } 1169 } 1170 } 1171 1172 private void notifyAccessibilityButtonAvailabilityChangedInternal(boolean available) { 1173 // Only notify the service if it's not been notified or the state has changed 1174 if (mReceivedAccessibilityButtonCallbackSinceBind 1175 && (mLastAccessibilityButtonCallbackState == available)) { 1176 return; 1177 } 1178 mReceivedAccessibilityButtonCallbackSinceBind = true; 1179 mLastAccessibilityButtonCallbackState = available; 1180 final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); 1181 if (listener != null) { 1182 try { 1183 listener.onAccessibilityButtonAvailabilityChanged(available); 1184 } catch (RemoteException re) { 1185 Slog.e(LOG_TAG, 1186 "Error sending accessibility button availability change to " + mService, 1187 re); 1188 } 1189 } 1190 } 1191 1192 private void notifyGestureInternal(int gestureId) { 1193 final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); 1194 if (listener != null) { 1195 try { 1196 listener.onGesture(gestureId); 1197 } catch (RemoteException re) { 1198 Slog.e(LOG_TAG, "Error during sending gesture " + gestureId 1199 + " to " + mService, re); 1200 } 1201 } 1202 } 1203 1204 private void notifyClearAccessibilityCacheInternal() { 1205 final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); 1206 if (listener != null) { 1207 try { 1208 listener.clearAccessibilityCache(); 1209 } catch (RemoteException re) { 1210 Slog.e(LOG_TAG, "Error during requesting accessibility info cache" 1211 + " to be cleared.", re); 1212 } 1213 } 1214 } 1215 1216 private IAccessibilityServiceClient getServiceInterfaceSafely() { 1217 synchronized (mLock) { 1218 return mServiceInterface; 1219 } 1220 } 1221 1222 private int resolveAccessibilityWindowIdLocked(int accessibilityWindowId) { 1223 if (accessibilityWindowId == AccessibilityWindowInfo.ACTIVE_WINDOW_ID) { 1224 return mSecurityPolicy.getActiveWindowId(); 1225 } 1226 return accessibilityWindowId; 1227 } 1228 1229 private int resolveAccessibilityWindowIdForFindFocusLocked(int windowId, int focusType) { 1230 if (windowId == AccessibilityWindowInfo.ACTIVE_WINDOW_ID) { 1231 return mSecurityPolicy.mActiveWindowId; 1232 } 1233 if (windowId == AccessibilityWindowInfo.ANY_WINDOW_ID) { 1234 if (focusType == AccessibilityNodeInfo.FOCUS_INPUT) { 1235 return mSecurityPolicy.mFocusedWindowId; 1236 } else if (focusType == AccessibilityNodeInfo.FOCUS_ACCESSIBILITY) { 1237 return mSecurityPolicy.mAccessibilityFocusedWindowId; 1238 } 1239 } 1240 return windowId; 1241 } 1242 1243 public ComponentName getComponentName() { 1244 return mComponentName; 1245 } 1246 1247 private final class InvocationHandler extends Handler { 1248 public static final int MSG_ON_GESTURE = 1; 1249 public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 2; 1250 1251 private static final int MSG_ON_MAGNIFICATION_CHANGED = 5; 1252 private static final int MSG_ON_SOFT_KEYBOARD_STATE_CHANGED = 6; 1253 private static final int MSG_ON_ACCESSIBILITY_BUTTON_CLICKED = 7; 1254 private static final int MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 8; 1255 1256 private boolean mIsMagnificationCallbackEnabled = false; 1257 private boolean mIsSoftKeyboardCallbackEnabled = false; 1258 1259 public InvocationHandler(Looper looper) { 1260 super(looper, null, true); 1261 } 1262 1263 @Override 1264 public void handleMessage(Message message) { 1265 final int type = message.what; 1266 switch (type) { 1267 case MSG_ON_GESTURE: { 1268 final int gestureId = message.arg1; 1269 notifyGestureInternal(gestureId); 1270 } break; 1271 1272 case MSG_CLEAR_ACCESSIBILITY_CACHE: { 1273 notifyClearAccessibilityCacheInternal(); 1274 } break; 1275 1276 case MSG_ON_MAGNIFICATION_CHANGED: { 1277 final SomeArgs args = (SomeArgs) message.obj; 1278 final Region region = (Region) args.arg1; 1279 final float scale = (float) args.arg2; 1280 final float centerX = (float) args.arg3; 1281 final float centerY = (float) args.arg4; 1282 notifyMagnificationChangedInternal(region, scale, centerX, centerY); 1283 args.recycle(); 1284 } break; 1285 1286 case MSG_ON_SOFT_KEYBOARD_STATE_CHANGED: { 1287 final int showState = (int) message.arg1; 1288 notifySoftKeyboardShowModeChangedInternal(showState); 1289 } break; 1290 1291 case MSG_ON_ACCESSIBILITY_BUTTON_CLICKED: { 1292 notifyAccessibilityButtonClickedInternal(); 1293 } break; 1294 1295 case MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED: { 1296 final boolean available = (message.arg1 != 0); 1297 notifyAccessibilityButtonAvailabilityChangedInternal(available); 1298 } break; 1299 1300 default: { 1301 throw new IllegalArgumentException("Unknown message: " + type); 1302 } 1303 } 1304 } 1305 1306 public void notifyMagnificationChangedLocked(@NonNull Region region, float scale, 1307 float centerX, float centerY) { 1308 if (!mIsMagnificationCallbackEnabled) { 1309 // Callback is disabled, don't bother packing args. 1310 return; 1311 } 1312 1313 final SomeArgs args = SomeArgs.obtain(); 1314 args.arg1 = region; 1315 args.arg2 = scale; 1316 args.arg3 = centerX; 1317 args.arg4 = centerY; 1318 1319 final Message msg = obtainMessage(MSG_ON_MAGNIFICATION_CHANGED, args); 1320 msg.sendToTarget(); 1321 } 1322 1323 public void setMagnificationCallbackEnabled(boolean enabled) { 1324 mIsMagnificationCallbackEnabled = enabled; 1325 } 1326 1327 public void notifySoftKeyboardShowModeChangedLocked(int showState) { 1328 if (!mIsSoftKeyboardCallbackEnabled) { 1329 return; 1330 } 1331 1332 final Message msg = obtainMessage(MSG_ON_SOFT_KEYBOARD_STATE_CHANGED, showState, 0); 1333 msg.sendToTarget(); 1334 } 1335 1336 public void setSoftKeyboardCallbackEnabled(boolean enabled) { 1337 mIsSoftKeyboardCallbackEnabled = enabled; 1338 } 1339 1340 public void notifyAccessibilityButtonClickedLocked() { 1341 final Message msg = obtainMessage(MSG_ON_ACCESSIBILITY_BUTTON_CLICKED); 1342 msg.sendToTarget(); 1343 } 1344 1345 public void notifyAccessibilityButtonAvailabilityChangedLocked(boolean available) { 1346 final Message msg = obtainMessage(MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED, 1347 (available ? 1 : 0), 0); 1348 msg.sendToTarget(); 1349 } 1350 } 1351 } 1352