1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.accessibility; 18 19 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; 20 import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED; 21 22 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.graphics.Region; 27 import android.os.Binder; 28 import android.os.Handler; 29 import android.os.IBinder; 30 import android.os.Process; 31 import android.os.RemoteException; 32 import android.os.UserHandle; 33 import android.text.TextUtils; 34 import android.util.ArrayMap; 35 import android.util.Slog; 36 import android.util.SparseArray; 37 import android.view.Display; 38 import android.view.IWindow; 39 import android.view.WindowInfo; 40 import android.view.WindowManager; 41 import android.view.accessibility.AccessibilityEvent; 42 import android.view.accessibility.AccessibilityNodeInfo; 43 import android.view.accessibility.AccessibilityWindowInfo; 44 import android.view.accessibility.IAccessibilityInteractionConnection; 45 46 import com.android.internal.annotations.VisibleForTesting; 47 import com.android.server.accessibility.AccessibilitySecurityPolicy.AccessibilityUserManager; 48 import com.android.server.wm.WindowManagerInternal; 49 50 import java.io.FileDescriptor; 51 import java.io.PrintWriter; 52 import java.util.ArrayList; 53 import java.util.Collections; 54 import java.util.List; 55 56 /** 57 * This class provides APIs for accessibility manager to manage {@link AccessibilityWindowInfo}s and 58 * {@link WindowInfo}s. 59 */ 60 public class AccessibilityWindowManager { 61 private static final String LOG_TAG = "AccessibilityWindowManager"; 62 private static final boolean DEBUG = false; 63 64 private static int sNextWindowId; 65 66 private final Object mLock; 67 private final Handler mHandler; 68 private final WindowManagerInternal mWindowManagerInternal; 69 private final AccessibilityEventSender mAccessibilityEventSender; 70 private final AccessibilitySecurityPolicy mSecurityPolicy; 71 private final AccessibilityUserManager mAccessibilityUserManager; 72 73 // Connections and window tokens for cross-user windows 74 private final SparseArray<RemoteAccessibilityConnection> 75 mGlobalInteractionConnections = new SparseArray<>(); 76 private final SparseArray<IBinder> mGlobalWindowTokens = new SparseArray<>(); 77 78 // Connections and window tokens for per-user windows, indexed as one sparse array per user 79 private final SparseArray<SparseArray<RemoteAccessibilityConnection>> 80 mInteractionConnections = new SparseArray<>(); 81 private final SparseArray<SparseArray<IBinder>> mWindowTokens = new SparseArray<>(); 82 83 private RemoteAccessibilityConnection mPictureInPictureActionReplacingConnection; 84 // There is only one active window in the system. It is updated when the top focused window 85 // of the top focused display changes and when we receive a TYPE_WINDOW_STATE_CHANGED event. 86 private int mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 87 // There is only one top focused window in the system. It is updated when the window manager 88 // updates the window lists. 89 private int mTopFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 90 private int mAccessibilityFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 91 private long mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; 92 // The top focused display and window token updated with the callback of window lists change. 93 private int mTopFocusedDisplayId; 94 private IBinder mTopFocusedWindowToken; 95 // The display has the accessibility focused window currently. 96 private int mAccessibilityFocusedDisplayId = Display.INVALID_DISPLAY; 97 98 private boolean mTouchInteractionInProgress; 99 100 /** List of Display Windows Observer, mapping from displayId -> DisplayWindowsObserver. */ 101 private final SparseArray<DisplayWindowsObserver> mDisplayWindowsObservers = 102 new SparseArray<>(); 103 104 /** 105 * Map of host view and embedded hierarchy, mapping from leash token of its ViewRootImpl. 106 * The key is the token from embedded hierarchy, and the value is the token from its host. 107 */ 108 private final ArrayMap<IBinder, IBinder> mHostEmbeddedMap = new ArrayMap<>(); 109 110 /** 111 * Map of window id and view hierarchy. 112 * The key is the window id when the ViewRootImpl register to accessibility, and the value is 113 * its leash token. 114 */ 115 private final SparseArray<IBinder> mWindowIdMap = new SparseArray<>(); 116 117 /** 118 * This class implements {@link WindowManagerInternal.WindowsForAccessibilityCallback} to 119 * receive {@link WindowInfo}s from window manager when there's an accessibility change in 120 * window and holds window lists information per display. 121 */ 122 private final class DisplayWindowsObserver implements 123 WindowManagerInternal.WindowsForAccessibilityCallback { 124 125 private final int mDisplayId; 126 private final SparseArray<AccessibilityWindowInfo> mA11yWindowInfoById = 127 new SparseArray<>(); 128 private final SparseArray<WindowInfo> mWindowInfoById = new SparseArray<>(); 129 private final List<WindowInfo> mCachedWindowInfos = new ArrayList<>(); 130 private List<AccessibilityWindowInfo> mWindows; 131 private boolean mTrackingWindows = false; 132 private boolean mHasWatchOutsideTouchWindow; 133 134 /** 135 * Constructor for DisplayWindowsObserver. 136 */ DisplayWindowsObserver(int displayId)137 DisplayWindowsObserver(int displayId) { 138 mDisplayId = displayId; 139 } 140 141 /** 142 * Starts tracking windows changes from window manager by registering callback. 143 * 144 * @return true if callback registers successful. 145 */ startTrackingWindowsLocked()146 boolean startTrackingWindowsLocked() { 147 boolean result = true; 148 149 if (!mTrackingWindows) { 150 // Turns on the flag before setup the callback. 151 // In some cases, onWindowsForAccessibilityChanged will be called immediately in 152 // setWindowsForAccessibilityCallback. We'll lost windows if flag is false. 153 mTrackingWindows = true; 154 result = mWindowManagerInternal.setWindowsForAccessibilityCallback( 155 mDisplayId, this); 156 if (!result) { 157 mTrackingWindows = false; 158 Slog.w(LOG_TAG, "set windowsObserver callbacks fail, displayId:" 159 + mDisplayId); 160 } 161 } 162 return result; 163 } 164 165 /** 166 * Stops tracking windows changes from window manager, and clear all windows info. 167 */ stopTrackingWindowsLocked()168 void stopTrackingWindowsLocked() { 169 if (mTrackingWindows) { 170 mWindowManagerInternal.setWindowsForAccessibilityCallback( 171 mDisplayId, null); 172 mTrackingWindows = false; 173 clearWindowsLocked(); 174 } 175 } 176 177 /** 178 * Returns true if windows changes tracking. 179 * 180 * @return true if windows changes tracking 181 */ isTrackingWindowsLocked()182 boolean isTrackingWindowsLocked() { 183 return mTrackingWindows; 184 } 185 186 /** 187 * Returns accessibility windows. 188 * @return accessibility windows. 189 */ 190 @Nullable getWindowListLocked()191 List<AccessibilityWindowInfo> getWindowListLocked() { 192 return mWindows; 193 } 194 195 /** 196 * Returns accessibility window info according to given windowId. 197 * 198 * @param windowId The windowId 199 * @return The accessibility window info 200 */ 201 @Nullable findA11yWindowInfoByIdLocked(int windowId)202 AccessibilityWindowInfo findA11yWindowInfoByIdLocked(int windowId) { 203 return mA11yWindowInfoById.get(windowId); 204 } 205 206 /** 207 * Returns the window info according to given windowId. 208 * 209 * @param windowId The windowId 210 * @return The window info 211 */ 212 @Nullable findWindowInfoByIdLocked(int windowId)213 WindowInfo findWindowInfoByIdLocked(int windowId) { 214 return mWindowInfoById.get(windowId); 215 } 216 217 /** 218 * Returns {@link AccessibilityWindowInfo} of PIP window. 219 * 220 * @return PIP accessibility window info 221 */ 222 @Nullable getPictureInPictureWindowLocked()223 AccessibilityWindowInfo getPictureInPictureWindowLocked() { 224 if (mWindows != null) { 225 final int windowCount = mWindows.size(); 226 for (int i = 0; i < windowCount; i++) { 227 final AccessibilityWindowInfo window = mWindows.get(i); 228 if (window.isInPictureInPictureMode()) { 229 return window; 230 } 231 } 232 } 233 return null; 234 } 235 236 /** 237 * Sets the active flag of the window according to given windowId, others set to inactive. 238 * 239 * @param windowId The windowId 240 */ setActiveWindowLocked(int windowId)241 void setActiveWindowLocked(int windowId) { 242 if (mWindows != null) { 243 final int windowCount = mWindows.size(); 244 for (int i = 0; i < windowCount; i++) { 245 AccessibilityWindowInfo window = mWindows.get(i); 246 if (window.getId() == windowId) { 247 window.setActive(true); 248 mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked( 249 AccessibilityEvent.obtainWindowsChangedEvent(windowId, 250 AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)); 251 } else { 252 window.setActive(false); 253 } 254 } 255 } 256 } 257 258 /** 259 * Sets the window accessibility focused according to given windowId, others set 260 * unfocused. 261 * 262 * @param windowId The windowId 263 */ setAccessibilityFocusedWindowLocked(int windowId)264 void setAccessibilityFocusedWindowLocked(int windowId) { 265 if (mWindows != null) { 266 final int windowCount = mWindows.size(); 267 for (int i = 0; i < windowCount; i++) { 268 AccessibilityWindowInfo window = mWindows.get(i); 269 if (window.getId() == windowId) { 270 mAccessibilityFocusedDisplayId = mDisplayId; 271 window.setAccessibilityFocused(true); 272 mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked( 273 AccessibilityEvent.obtainWindowsChangedEvent( 274 windowId, WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)); 275 276 } else { 277 window.setAccessibilityFocused(false); 278 } 279 } 280 } 281 } 282 283 /** 284 * Computes partial interactive region of given windowId. 285 * 286 * @param windowId The windowId 287 * @param outRegion The output to which to write the bounds. 288 * @return true if outRegion is not empty. 289 */ computePartialInteractiveRegionForWindowLocked(int windowId, @NonNull Region outRegion)290 boolean computePartialInteractiveRegionForWindowLocked(int windowId, 291 @NonNull Region outRegion) { 292 if (mWindows == null) { 293 return false; 294 } 295 296 // Windows are ordered in z order so start from the bottom and find 297 // the window of interest. After that all windows that cover it should 298 // be subtracted from the resulting region. Note that for accessibility 299 // we are returning only interactive windows. 300 Region windowInteractiveRegion = null; 301 boolean windowInteractiveRegionChanged = false; 302 303 final int windowCount = mWindows.size(); 304 final Region currentWindowRegions = new Region(); 305 for (int i = windowCount - 1; i >= 0; i--) { 306 AccessibilityWindowInfo currentWindow = mWindows.get(i); 307 if (windowInteractiveRegion == null) { 308 if (currentWindow.getId() == windowId) { 309 currentWindow.getRegionInScreen(currentWindowRegions); 310 outRegion.set(currentWindowRegions); 311 windowInteractiveRegion = outRegion; 312 continue; 313 } 314 } else if (currentWindow.getType() 315 != AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY) { 316 currentWindow.getRegionInScreen(currentWindowRegions); 317 if (windowInteractiveRegion.op(currentWindowRegions, Region.Op.DIFFERENCE)) { 318 windowInteractiveRegionChanged = true; 319 } 320 } 321 } 322 323 return windowInteractiveRegionChanged; 324 } 325 getWatchOutsideTouchWindowIdLocked(int targetWindowId)326 List<Integer> getWatchOutsideTouchWindowIdLocked(int targetWindowId) { 327 final WindowInfo targetWindow = mWindowInfoById.get(targetWindowId); 328 if (targetWindow != null && mHasWatchOutsideTouchWindow) { 329 final List<Integer> outsideWindowsId = new ArrayList<>(); 330 for (int i = 0; i < mWindowInfoById.size(); i++) { 331 final WindowInfo window = mWindowInfoById.valueAt(i); 332 if (window != null && window.layer < targetWindow.layer 333 && window.hasFlagWatchOutsideTouch) { 334 outsideWindowsId.add(mWindowInfoById.keyAt(i)); 335 } 336 } 337 return outsideWindowsId; 338 } 339 return Collections.emptyList(); 340 } 341 342 /** 343 * Callbacks from window manager when there's an accessibility change in windows. 344 * 345 * @param forceSend Send the windows for accessibility even if they haven't changed. 346 * @param topFocusedDisplayId The display Id which has the top focused window. 347 * @param topFocusedWindowToken The window token of top focused window. 348 * @param windows The windows for accessibility. 349 */ 350 @Override onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId, IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows)351 public void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId, 352 IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows) { 353 synchronized (mLock) { 354 if (DEBUG) { 355 Slog.i(LOG_TAG, "Display Id = " + mDisplayId); 356 Slog.i(LOG_TAG, "Windows changed: " + windows); 357 } 358 if (shouldUpdateWindowsLocked(forceSend, windows)) { 359 mTopFocusedDisplayId = topFocusedDisplayId; 360 mTopFocusedWindowToken = topFocusedWindowToken; 361 cacheWindows(windows); 362 // Lets the policy update the focused and active windows. 363 updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), 364 windows); 365 // Someone may be waiting for the windows - advertise it. 366 mLock.notifyAll(); 367 } 368 } 369 } 370 shouldUpdateWindowsLocked(boolean forceSend, @NonNull List<WindowInfo> windows)371 private boolean shouldUpdateWindowsLocked(boolean forceSend, 372 @NonNull List<WindowInfo> windows) { 373 if (forceSend) { 374 return true; 375 } 376 377 final int windowCount = windows.size(); 378 // We computed the windows and if they changed notify the client. 379 if (mCachedWindowInfos.size() != windowCount) { 380 // Different size means something changed. 381 return true; 382 } else if (!mCachedWindowInfos.isEmpty() || !windows.isEmpty()) { 383 // Since we always traverse windows from high to low layer 384 // the old and new windows at the same index should be the 385 // same, otherwise something changed. 386 for (int i = 0; i < windowCount; i++) { 387 WindowInfo oldWindow = mCachedWindowInfos.get(i); 388 WindowInfo newWindow = windows.get(i); 389 // We do not care for layer changes given the window 390 // order does not change. This brings no new information 391 // to the clients. 392 if (windowChangedNoLayer(oldWindow, newWindow)) { 393 return true; 394 } 395 } 396 } 397 398 return false; 399 } 400 cacheWindows(List<WindowInfo> windows)401 private void cacheWindows(List<WindowInfo> windows) { 402 final int oldWindowCount = mCachedWindowInfos.size(); 403 for (int i = oldWindowCount - 1; i >= 0; i--) { 404 mCachedWindowInfos.remove(i).recycle(); 405 } 406 final int newWindowCount = windows.size(); 407 for (int i = 0; i < newWindowCount; i++) { 408 WindowInfo newWindow = windows.get(i); 409 mCachedWindowInfos.add(WindowInfo.obtain(newWindow)); 410 } 411 } 412 windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow)413 private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) { 414 if (oldWindow == newWindow) { 415 return false; 416 } 417 if (oldWindow == null) { 418 return true; 419 } 420 if (newWindow == null) { 421 return true; 422 } 423 if (oldWindow.type != newWindow.type) { 424 return true; 425 } 426 if (oldWindow.focused != newWindow.focused) { 427 return true; 428 } 429 if (oldWindow.token == null) { 430 if (newWindow.token != null) { 431 return true; 432 } 433 } else if (!oldWindow.token.equals(newWindow.token)) { 434 return true; 435 } 436 if (oldWindow.parentToken == null) { 437 if (newWindow.parentToken != null) { 438 return true; 439 } 440 } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) { 441 return true; 442 } 443 if (oldWindow.activityToken == null) { 444 if (newWindow.activityToken != null) { 445 return true; 446 } 447 } else if (!oldWindow.activityToken.equals(newWindow.activityToken)) { 448 return true; 449 } 450 if (!oldWindow.regionInScreen.equals(newWindow.regionInScreen)) { 451 return true; 452 } 453 if (oldWindow.childTokens != null && newWindow.childTokens != null 454 && !oldWindow.childTokens.equals(newWindow.childTokens)) { 455 return true; 456 } 457 if (!TextUtils.equals(oldWindow.title, newWindow.title)) { 458 return true; 459 } 460 if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) { 461 return true; 462 } 463 if (oldWindow.inPictureInPicture != newWindow.inPictureInPicture) { 464 return true; 465 } 466 if (oldWindow.hasFlagWatchOutsideTouch != newWindow.hasFlagWatchOutsideTouch) { 467 return true; 468 } 469 if (oldWindow.displayId != newWindow.displayId) { 470 return true; 471 } 472 return false; 473 } 474 475 /** 476 * Clears all {@link AccessibilityWindowInfo}s and {@link WindowInfo}s. 477 */ clearWindowsLocked()478 private void clearWindowsLocked() { 479 final List<WindowInfo> windows = Collections.emptyList(); 480 final int activeWindowId = mActiveWindowId; 481 // UserId is useless in updateWindowsLocked, when we update a empty window list. 482 // Just pass current userId here. 483 updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), windows); 484 // Do not reset mActiveWindowId here. mActiveWindowId will be clear after accessibility 485 // interaction connection removed. 486 mActiveWindowId = activeWindowId; 487 mWindows = null; 488 } 489 490 /** 491 * Updates windows info according to specified userId and windows. 492 * 493 * @param userId The userId to update 494 * @param windows The windows to update 495 */ updateWindowsLocked(int userId, @NonNull List<WindowInfo> windows)496 private void updateWindowsLocked(int userId, @NonNull List<WindowInfo> windows) { 497 if (mWindows == null) { 498 mWindows = new ArrayList<>(); 499 } 500 501 final List<AccessibilityWindowInfo> oldWindowList = new ArrayList<>(mWindows); 502 final SparseArray<AccessibilityWindowInfo> oldWindowsById = mA11yWindowInfoById.clone(); 503 boolean shouldClearAccessibilityFocus = false; 504 505 mWindows.clear(); 506 mA11yWindowInfoById.clear(); 507 508 for (int i = 0; i < mWindowInfoById.size(); i++) { 509 mWindowInfoById.valueAt(i).recycle(); 510 } 511 mWindowInfoById.clear(); 512 mHasWatchOutsideTouchWindow = false; 513 514 final int windowCount = windows.size(); 515 final boolean isTopFocusedDisplay = mDisplayId == mTopFocusedDisplayId; 516 final boolean isAccessibilityFocusedDisplay = 517 mDisplayId == mAccessibilityFocusedDisplayId; 518 // Modifies the value of top focused window, active window and a11y focused window 519 // only if this display is top focused display which has the top focused window. 520 if (isTopFocusedDisplay) { 521 if (windowCount > 0) { 522 // Sets the top focus window by top focused window token. 523 mTopFocusedWindowId = findWindowIdLocked(userId, mTopFocusedWindowToken); 524 } else { 525 // Resets the top focus window when stopping tracking window of this display. 526 mTopFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 527 } 528 // The active window doesn't need to be reset if the touch operation is progressing. 529 if (!mTouchInteractionInProgress) { 530 mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 531 } 532 } 533 534 // If the active window goes away while the user is touch exploring we 535 // reset the active window id and wait for the next hover event from 536 // under the user's finger to determine which one is the new one. It 537 // is possible that the finger is not moving and the input system 538 // filters out such events. 539 boolean activeWindowGone = true; 540 541 // We'll clear accessibility focus if the window with focus is no longer visible to 542 // accessibility services. 543 if (isAccessibilityFocusedDisplay) { 544 shouldClearAccessibilityFocus = mAccessibilityFocusedWindowId 545 != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 546 } 547 if (windowCount > 0) { 548 for (int i = 0; i < windowCount; i++) { 549 final WindowInfo windowInfo = windows.get(i); 550 final AccessibilityWindowInfo window; 551 if (mTrackingWindows) { 552 window = populateReportedWindowLocked(userId, windowInfo); 553 } else { 554 window = null; 555 } 556 if (window != null) { 557 558 // Flip layers in list to be consistent with AccessibilityService#getWindows 559 window.setLayer(windowCount - 1 - window.getLayer()); 560 561 final int windowId = window.getId(); 562 if (window.isFocused() && isTopFocusedDisplay) { 563 if (!mTouchInteractionInProgress) { 564 // This display is top one, and sets the focus window 565 // as active window. 566 mActiveWindowId = windowId; 567 window.setActive(true); 568 } else if (windowId == mActiveWindowId) { 569 activeWindowGone = false; 570 } 571 } 572 if (!mHasWatchOutsideTouchWindow && windowInfo.hasFlagWatchOutsideTouch) { 573 mHasWatchOutsideTouchWindow = true; 574 } 575 mWindows.add(window); 576 mA11yWindowInfoById.put(windowId, window); 577 mWindowInfoById.put(windowId, WindowInfo.obtain(windowInfo)); 578 } 579 } 580 final int accessibilityWindowCount = mWindows.size(); 581 if (isTopFocusedDisplay) { 582 if (mTouchInteractionInProgress && activeWindowGone) { 583 mActiveWindowId = mTopFocusedWindowId; 584 } 585 // Focused window may change the active one, so set the 586 // active window once we decided which it is. 587 for (int i = 0; i < accessibilityWindowCount; i++) { 588 final AccessibilityWindowInfo window = mWindows.get(i); 589 if (window.getId() == mActiveWindowId) { 590 window.setActive(true); 591 } 592 } 593 } 594 if (isAccessibilityFocusedDisplay) { 595 for (int i = 0; i < accessibilityWindowCount; i++) { 596 final AccessibilityWindowInfo window = mWindows.get(i); 597 if (window.getId() == mAccessibilityFocusedWindowId) { 598 window.setAccessibilityFocused(true); 599 shouldClearAccessibilityFocus = false; 600 break; 601 } 602 } 603 } 604 } 605 606 sendEventsForChangedWindowsLocked(oldWindowList, oldWindowsById); 607 608 final int oldWindowCount = oldWindowList.size(); 609 for (int i = oldWindowCount - 1; i >= 0; i--) { 610 oldWindowList.remove(i).recycle(); 611 } 612 613 if (shouldClearAccessibilityFocus) { 614 clearAccessibilityFocusLocked(mAccessibilityFocusedWindowId); 615 } 616 } 617 sendEventsForChangedWindowsLocked(List<AccessibilityWindowInfo> oldWindows, SparseArray<AccessibilityWindowInfo> oldWindowsById)618 private void sendEventsForChangedWindowsLocked(List<AccessibilityWindowInfo> oldWindows, 619 SparseArray<AccessibilityWindowInfo> oldWindowsById) { 620 List<AccessibilityEvent> events = new ArrayList<>(); 621 // Sends events for all removed windows. 622 final int oldWindowsCount = oldWindows.size(); 623 for (int i = 0; i < oldWindowsCount; i++) { 624 final AccessibilityWindowInfo window = oldWindows.get(i); 625 if (mA11yWindowInfoById.get(window.getId()) == null) { 626 events.add(AccessibilityEvent.obtainWindowsChangedEvent( 627 window.getId(), AccessibilityEvent.WINDOWS_CHANGE_REMOVED)); 628 } 629 } 630 631 // Looks for other changes. 632 final int newWindowCount = mWindows.size(); 633 for (int i = 0; i < newWindowCount; i++) { 634 final AccessibilityWindowInfo newWindow = mWindows.get(i); 635 final AccessibilityWindowInfo oldWindow = oldWindowsById.get(newWindow.getId()); 636 if (oldWindow == null) { 637 events.add(AccessibilityEvent.obtainWindowsChangedEvent( 638 newWindow.getId(), AccessibilityEvent.WINDOWS_CHANGE_ADDED)); 639 } else { 640 int changes = newWindow.differenceFrom(oldWindow); 641 if (changes != 0) { 642 events.add(AccessibilityEvent.obtainWindowsChangedEvent( 643 newWindow.getId(), changes)); 644 } 645 } 646 } 647 648 final int numEvents = events.size(); 649 for (int i = 0; i < numEvents; i++) { 650 mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(events.get(i)); 651 } 652 } 653 populateReportedWindowLocked(int userId, WindowInfo window)654 private AccessibilityWindowInfo populateReportedWindowLocked(int userId, 655 WindowInfo window) { 656 final int windowId = findWindowIdLocked(userId, window.token); 657 if (windowId < 0) { 658 return null; 659 } 660 661 final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain(); 662 663 reportedWindow.setId(windowId); 664 reportedWindow.setType(getTypeForWindowManagerWindowType(window.type)); 665 reportedWindow.setLayer(window.layer); 666 reportedWindow.setFocused(window.focused); 667 reportedWindow.setRegionInScreen(window.regionInScreen); 668 reportedWindow.setTitle(window.title); 669 reportedWindow.setAnchorId(window.accessibilityIdOfAnchor); 670 reportedWindow.setPictureInPicture(window.inPictureInPicture); 671 reportedWindow.setDisplayId(window.displayId); 672 673 final int parentId = findWindowIdLocked(userId, window.parentToken); 674 if (parentId >= 0) { 675 reportedWindow.setParentId(parentId); 676 } 677 678 if (window.childTokens != null) { 679 final int childCount = window.childTokens.size(); 680 for (int i = 0; i < childCount; i++) { 681 final IBinder childToken = window.childTokens.get(i); 682 final int childId = findWindowIdLocked(userId, childToken); 683 if (childId >= 0) { 684 reportedWindow.addChild(childId); 685 } 686 } 687 } 688 689 return reportedWindow; 690 } 691 getTypeForWindowManagerWindowType(int windowType)692 private int getTypeForWindowManagerWindowType(int windowType) { 693 switch (windowType) { 694 case WindowManager.LayoutParams.TYPE_APPLICATION: 695 case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: 696 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: 697 case WindowManager.LayoutParams.TYPE_APPLICATION_STARTING: 698 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: 699 case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL: 700 case WindowManager.LayoutParams.TYPE_BASE_APPLICATION: 701 case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION: 702 case WindowManager.LayoutParams.TYPE_PHONE: 703 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: 704 case WindowManager.LayoutParams.TYPE_TOAST: 705 case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: { 706 return AccessibilityWindowInfo.TYPE_APPLICATION; 707 } 708 709 case WindowManager.LayoutParams.TYPE_INPUT_METHOD: 710 case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG: { 711 return AccessibilityWindowInfo.TYPE_INPUT_METHOD; 712 } 713 714 case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: 715 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR: 716 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: 717 case WindowManager.LayoutParams.TYPE_SEARCH_BAR: 718 case WindowManager.LayoutParams.TYPE_STATUS_BAR: 719 case WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE: 720 case WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL: 721 case WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL: 722 case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: 723 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: 724 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: 725 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: 726 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: 727 case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY: 728 case WindowManager.LayoutParams.TYPE_SCREENSHOT: 729 case WindowManager.LayoutParams.TYPE_TRUSTED_APPLICATION_OVERLAY: { 730 return AccessibilityWindowInfo.TYPE_SYSTEM; 731 } 732 733 case WindowManager.LayoutParams.TYPE_DOCK_DIVIDER: { 734 return AccessibilityWindowInfo.TYPE_SPLIT_SCREEN_DIVIDER; 735 } 736 737 case TYPE_ACCESSIBILITY_OVERLAY: { 738 return AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY; 739 } 740 741 default: { 742 return -1; 743 } 744 } 745 } 746 747 /** 748 * Dumps all {@link AccessibilityWindowInfo}s here. 749 */ dumpLocked(FileDescriptor fd, final PrintWriter pw, String[] args)750 void dumpLocked(FileDescriptor fd, final PrintWriter pw, String[] args) { 751 if (mWindows != null) { 752 final int windowCount = mWindows.size(); 753 for (int j = 0; j < windowCount; j++) { 754 if (j == 0) { 755 pw.append("Display["); 756 pw.append(Integer.toString(mDisplayId)); 757 pw.append("] : "); 758 pw.println(); 759 } 760 if (j > 0) { 761 pw.append(','); 762 pw.println(); 763 } 764 pw.append("Window["); 765 AccessibilityWindowInfo window = mWindows.get(j); 766 pw.append(window.toString()); 767 pw.append(']'); 768 } 769 pw.println(); 770 } 771 } 772 } 773 /** 774 * Interface to send {@link AccessibilityEvent}. 775 */ 776 public interface AccessibilityEventSender { 777 /** 778 * Sends {@link AccessibilityEvent} for current user. 779 */ sendAccessibilityEventForCurrentUserLocked(AccessibilityEvent event)780 void sendAccessibilityEventForCurrentUserLocked(AccessibilityEvent event); 781 } 782 783 /** 784 * Wrapper of accessibility interaction connection for window. 785 */ 786 // In order to avoid using DexmakerShareClassLoaderRule, make this class visible for testing. 787 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 788 public final class RemoteAccessibilityConnection implements IBinder.DeathRecipient { 789 private final int mUid; 790 private final String mPackageName; 791 private final int mWindowId; 792 private final int mUserId; 793 private final IAccessibilityInteractionConnection mConnection; 794 RemoteAccessibilityConnection(int windowId, IAccessibilityInteractionConnection connection, String packageName, int uid, int userId)795 RemoteAccessibilityConnection(int windowId, 796 IAccessibilityInteractionConnection connection, 797 String packageName, int uid, int userId) { 798 mWindowId = windowId; 799 mPackageName = packageName; 800 mUid = uid; 801 mUserId = userId; 802 mConnection = connection; 803 } 804 getUid()805 int getUid() { 806 return mUid; 807 } 808 getPackageName()809 String getPackageName() { 810 return mPackageName; 811 } 812 getRemote()813 IAccessibilityInteractionConnection getRemote() { 814 return mConnection; 815 } 816 linkToDeath()817 void linkToDeath() throws RemoteException { 818 mConnection.asBinder().linkToDeath(this, 0); 819 } 820 unlinkToDeath()821 void unlinkToDeath() { 822 mConnection.asBinder().unlinkToDeath(this, 0); 823 } 824 825 @Override binderDied()826 public void binderDied() { 827 unlinkToDeath(); 828 synchronized (mLock) { 829 removeAccessibilityInteractionConnectionLocked(mWindowId, mUserId); 830 } 831 } 832 } 833 834 /** 835 * Constructor for AccessibilityManagerService. 836 */ AccessibilityWindowManager(@onNull Object lock, @NonNull Handler handler, @NonNull WindowManagerInternal windowManagerInternal, @NonNull AccessibilityEventSender accessibilityEventSender, @NonNull AccessibilitySecurityPolicy securityPolicy, @NonNull AccessibilityUserManager accessibilityUserManager)837 public AccessibilityWindowManager(@NonNull Object lock, @NonNull Handler handler, 838 @NonNull WindowManagerInternal windowManagerInternal, 839 @NonNull AccessibilityEventSender accessibilityEventSender, 840 @NonNull AccessibilitySecurityPolicy securityPolicy, 841 @NonNull AccessibilityUserManager accessibilityUserManager) { 842 mLock = lock; 843 mHandler = handler; 844 mWindowManagerInternal = windowManagerInternal; 845 mAccessibilityEventSender = accessibilityEventSender; 846 mSecurityPolicy = securityPolicy; 847 mAccessibilityUserManager = accessibilityUserManager; 848 } 849 850 /** 851 * Starts tracking windows changes from window manager for specified display. 852 * 853 * @param displayId The logical display id. 854 */ startTrackingWindows(int displayId)855 public void startTrackingWindows(int displayId) { 856 synchronized (mLock) { 857 DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId); 858 if (observer == null) { 859 observer = new DisplayWindowsObserver(displayId); 860 } 861 if (observer.isTrackingWindowsLocked()) { 862 return; 863 } 864 if (observer.startTrackingWindowsLocked()) { 865 mDisplayWindowsObservers.put(displayId, observer); 866 } 867 } 868 } 869 870 /** 871 * Stops tracking windows changes from window manager, and clear all windows info for specified 872 * display. 873 * 874 * @param displayId The logical display id. 875 */ stopTrackingWindows(int displayId)876 public void stopTrackingWindows(int displayId) { 877 synchronized (mLock) { 878 final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId); 879 if (observer != null) { 880 observer.stopTrackingWindowsLocked(); 881 mDisplayWindowsObservers.remove(displayId); 882 } 883 } 884 } 885 886 /** 887 * Checks if we are tracking windows on any display. 888 * 889 * @return {@code true} if the observer is tracking windows on any display, 890 * {@code false} otherwise. 891 */ isTrackingWindowsLocked()892 public boolean isTrackingWindowsLocked() { 893 final int count = mDisplayWindowsObservers.size(); 894 if (count > 0) { 895 return true; 896 } 897 return false; 898 } 899 900 /** 901 * Checks if we are tracking windows on specified display. 902 * 903 * @param displayId The logical display id. 904 * @return {@code true} if the observer is tracking windows on specified display, 905 * {@code false} otherwise. 906 */ isTrackingWindowsLocked(int displayId)907 public boolean isTrackingWindowsLocked(int displayId) { 908 final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId); 909 if (observer != null) { 910 return observer.isTrackingWindowsLocked(); 911 } 912 return false; 913 } 914 915 /** 916 * Returns accessibility windows for specified display. 917 * 918 * @param displayId The logical display id. 919 * @return accessibility windows for specified display. 920 */ 921 @Nullable getWindowListLocked(int displayId)922 public List<AccessibilityWindowInfo> getWindowListLocked(int displayId) { 923 final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId); 924 if (observer != null) { 925 return observer.getWindowListLocked(); 926 } 927 return null; 928 } 929 930 /** 931 * Adds accessibility interaction connection according to given window token, package name and 932 * window token. 933 * 934 * @param window The window token of accessibility interaction connection 935 * @param leashToken The leash token of accessibility interaction connection 936 * @param connection The accessibility interaction connection 937 * @param packageName The package name 938 * @param userId The userId 939 * @return The windowId of added connection 940 * @throws RemoteException 941 */ addAccessibilityInteractionConnection(@onNull IWindow window, @NonNull IBinder leashToken, @NonNull IAccessibilityInteractionConnection connection, @NonNull String packageName, int userId)942 public int addAccessibilityInteractionConnection(@NonNull IWindow window, 943 @NonNull IBinder leashToken, @NonNull IAccessibilityInteractionConnection connection, 944 @NonNull String packageName, int userId) throws RemoteException { 945 final int windowId; 946 boolean shouldComputeWindows = false; 947 final IBinder token = window.asBinder(); 948 final int displayId = mWindowManagerInternal.getDisplayIdForWindow(token); 949 synchronized (mLock) { 950 // We treat calls from a profile as if made by its parent as profiles 951 // share the accessibility state of the parent. The call below 952 // performs the current profile parent resolution. 953 final int resolvedUserId = mSecurityPolicy 954 .resolveCallingUserIdEnforcingPermissionsLocked(userId); 955 final int resolvedUid = UserHandle.getUid(resolvedUserId, UserHandle.getCallingAppId()); 956 957 // Makes sure the reported package is one the caller has access to. 958 packageName = mSecurityPolicy.resolveValidReportedPackageLocked( 959 packageName, UserHandle.getCallingAppId(), resolvedUserId, 960 Binder.getCallingPid()); 961 962 windowId = sNextWindowId++; 963 // If the window is from a process that runs across users such as 964 // the system UI or the system we add it to the global state that 965 // is shared across users. 966 if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) { 967 RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection( 968 windowId, connection, packageName, resolvedUid, UserHandle.USER_ALL); 969 wrapper.linkToDeath(); 970 mGlobalInteractionConnections.put(windowId, wrapper); 971 mGlobalWindowTokens.put(windowId, token); 972 if (DEBUG) { 973 Slog.i(LOG_TAG, "Added global connection for pid:" + Binder.getCallingPid() 974 + " with windowId: " + windowId + " and token: " + token); 975 } 976 } else { 977 RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection( 978 windowId, connection, packageName, resolvedUid, resolvedUserId); 979 wrapper.linkToDeath(); 980 getInteractionConnectionsForUserLocked(resolvedUserId).put(windowId, wrapper); 981 getWindowTokensForUserLocked(resolvedUserId).put(windowId, token); 982 if (DEBUG) { 983 Slog.i(LOG_TAG, "Added user connection for pid:" + Binder.getCallingPid() 984 + " with windowId: " + windowId + " and token: " + token); 985 } 986 } 987 988 if (isTrackingWindowsLocked(displayId)) { 989 shouldComputeWindows = true; 990 } 991 registerIdLocked(leashToken, windowId); 992 } 993 if (shouldComputeWindows) { 994 mWindowManagerInternal.computeWindowsForAccessibility(displayId); 995 } 996 997 mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(token, windowId); 998 return windowId; 999 } 1000 1001 /** 1002 * Removes accessibility interaction connection according to given window token. 1003 * 1004 * @param window The window token of accessibility interaction connection 1005 */ removeAccessibilityInteractionConnection(@onNull IWindow window)1006 public void removeAccessibilityInteractionConnection(@NonNull IWindow window) { 1007 synchronized (mLock) { 1008 // We treat calls from a profile as if made by its parent as profiles 1009 // share the accessibility state of the parent. The call below 1010 // performs the current profile parent resolution. 1011 mSecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked( 1012 UserHandle.getCallingUserId()); 1013 IBinder token = window.asBinder(); 1014 final int removedWindowId = removeAccessibilityInteractionConnectionInternalLocked( 1015 token, mGlobalWindowTokens, mGlobalInteractionConnections); 1016 if (removedWindowId >= 0) { 1017 onAccessibilityInteractionConnectionRemovedLocked(removedWindowId, token); 1018 if (DEBUG) { 1019 Slog.i(LOG_TAG, "Removed global connection for pid:" + Binder.getCallingPid() 1020 + " with windowId: " + removedWindowId + " and token: " 1021 + window.asBinder()); 1022 } 1023 return; 1024 } 1025 final int userCount = mWindowTokens.size(); 1026 for (int i = 0; i < userCount; i++) { 1027 final int userId = mWindowTokens.keyAt(i); 1028 final int removedWindowIdForUser = 1029 removeAccessibilityInteractionConnectionInternalLocked(token, 1030 getWindowTokensForUserLocked(userId), 1031 getInteractionConnectionsForUserLocked(userId)); 1032 if (removedWindowIdForUser >= 0) { 1033 onAccessibilityInteractionConnectionRemovedLocked( 1034 removedWindowIdForUser, token); 1035 if (DEBUG) { 1036 Slog.i(LOG_TAG, "Removed user connection for pid:" + Binder.getCallingPid() 1037 + " with windowId: " + removedWindowIdForUser + " and userId:" 1038 + userId + " and token: " + window.asBinder()); 1039 } 1040 return; 1041 } 1042 } 1043 } 1044 } 1045 1046 /** 1047 * Resolves a connection wrapper for a window id. 1048 * 1049 * @param userId The user id for any user-specific windows 1050 * @param windowId The id of the window of interest 1051 * 1052 * @return a connection to the window 1053 */ 1054 @Nullable getConnectionLocked(int userId, int windowId)1055 public RemoteAccessibilityConnection getConnectionLocked(int userId, int windowId) { 1056 if (DEBUG) { 1057 Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId); 1058 } 1059 RemoteAccessibilityConnection connection = mGlobalInteractionConnections.get(windowId); 1060 if (connection == null && isValidUserForInteractionConnectionsLocked(userId)) { 1061 connection = getInteractionConnectionsForUserLocked(userId).get(windowId); 1062 } 1063 if (connection != null && connection.getRemote() != null) { 1064 return connection; 1065 } 1066 if (DEBUG) { 1067 Slog.e(LOG_TAG, "No interaction connection to window: " + windowId); 1068 } 1069 return null; 1070 } 1071 removeAccessibilityInteractionConnectionInternalLocked(IBinder windowToken, SparseArray<IBinder> windowTokens, SparseArray<RemoteAccessibilityConnection> interactionConnections)1072 private int removeAccessibilityInteractionConnectionInternalLocked(IBinder windowToken, 1073 SparseArray<IBinder> windowTokens, SparseArray<RemoteAccessibilityConnection> 1074 interactionConnections) { 1075 final int count = windowTokens.size(); 1076 for (int i = 0; i < count; i++) { 1077 if (windowTokens.valueAt(i) == windowToken) { 1078 final int windowId = windowTokens.keyAt(i); 1079 windowTokens.removeAt(i); 1080 RemoteAccessibilityConnection wrapper = interactionConnections.get(windowId); 1081 wrapper.unlinkToDeath(); 1082 interactionConnections.remove(windowId); 1083 return windowId; 1084 } 1085 } 1086 return -1; 1087 } 1088 1089 /** 1090 * Removes accessibility interaction connection according to given windowId and userId. 1091 * 1092 * @param windowId The windowId of accessibility interaction connection 1093 * @param userId The userId to remove 1094 */ removeAccessibilityInteractionConnectionLocked(int windowId, int userId)1095 private void removeAccessibilityInteractionConnectionLocked(int windowId, int userId) { 1096 IBinder window = null; 1097 if (userId == UserHandle.USER_ALL) { 1098 window = mGlobalWindowTokens.get(windowId); 1099 mGlobalWindowTokens.remove(windowId); 1100 mGlobalInteractionConnections.remove(windowId); 1101 } else { 1102 if (isValidUserForWindowTokensLocked(userId)) { 1103 window = getWindowTokensForUserLocked(userId).get(windowId); 1104 getWindowTokensForUserLocked(userId).remove(windowId); 1105 } 1106 if (isValidUserForInteractionConnectionsLocked(userId)) { 1107 getInteractionConnectionsForUserLocked(userId).remove(windowId); 1108 } 1109 } 1110 onAccessibilityInteractionConnectionRemovedLocked(windowId, window); 1111 if (DEBUG) { 1112 Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId); 1113 } 1114 } 1115 1116 /** 1117 * Invoked when accessibility interaction connection of window is removed. 1118 * 1119 * @param windowId Removed windowId 1120 * @param binder Removed window token 1121 */ onAccessibilityInteractionConnectionRemovedLocked( int windowId, @Nullable IBinder binder)1122 private void onAccessibilityInteractionConnectionRemovedLocked( 1123 int windowId, @Nullable IBinder binder) { 1124 // Active window will not update, if windows callback is unregistered. 1125 // Update active window to invalid, when its a11y interaction connection is removed. 1126 if (!isTrackingWindowsLocked() && windowId >= 0 && mActiveWindowId == windowId) { 1127 mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 1128 } 1129 if (binder != null) { 1130 mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata( 1131 binder, AccessibilityWindowInfo.UNDEFINED_WINDOW_ID); 1132 } 1133 unregisterIdLocked(windowId); 1134 } 1135 1136 /** 1137 * Gets window token according to given userId and windowId. 1138 * 1139 * @param userId The userId 1140 * @param windowId The windowId 1141 * @return The window token 1142 */ 1143 @Nullable getWindowTokenForUserAndWindowIdLocked(int userId, int windowId)1144 public IBinder getWindowTokenForUserAndWindowIdLocked(int userId, int windowId) { 1145 IBinder windowToken = mGlobalWindowTokens.get(windowId); 1146 if (windowToken == null && isValidUserForWindowTokensLocked(userId)) { 1147 windowToken = getWindowTokensForUserLocked(userId).get(windowId); 1148 } 1149 return windowToken; 1150 } 1151 1152 /** 1153 * Returns the userId that owns the given window token, {@link UserHandle#USER_NULL} 1154 * if not found. 1155 * 1156 * @param windowToken The window token 1157 * @return The userId 1158 */ getWindowOwnerUserId(@onNull IBinder windowToken)1159 public int getWindowOwnerUserId(@NonNull IBinder windowToken) { 1160 return mWindowManagerInternal.getWindowOwnerUserId(windowToken); 1161 } 1162 1163 /** 1164 * Returns windowId of given userId and window token. 1165 * 1166 * @param userId The userId 1167 * @param token The window token 1168 * @return The windowId 1169 */ findWindowIdLocked(int userId, @NonNull IBinder token)1170 public int findWindowIdLocked(int userId, @NonNull IBinder token) { 1171 final int globalIndex = mGlobalWindowTokens.indexOfValue(token); 1172 if (globalIndex >= 0) { 1173 return mGlobalWindowTokens.keyAt(globalIndex); 1174 } 1175 if (isValidUserForWindowTokensLocked(userId)) { 1176 final int userIndex = getWindowTokensForUserLocked(userId).indexOfValue(token); 1177 if (userIndex >= 0) { 1178 return getWindowTokensForUserLocked(userId).keyAt(userIndex); 1179 } 1180 } 1181 return -1; 1182 } 1183 1184 /** 1185 * Establish the relationship between the host and the embedded view hierarchy. 1186 * 1187 * @param host The token of host hierarchy 1188 * @param embedded The token of the embedded hierarchy 1189 */ associateEmbeddedHierarchyLocked(@onNull IBinder host, @NonNull IBinder embedded)1190 public void associateEmbeddedHierarchyLocked(@NonNull IBinder host, @NonNull IBinder embedded) { 1191 // Use embedded window as key, since one host window may have multiple embedded windows. 1192 associateLocked(embedded, host); 1193 } 1194 1195 /** 1196 * Clear the relationship by given token. 1197 * 1198 * @param token The token 1199 */ disassociateEmbeddedHierarchyLocked(@onNull IBinder token)1200 public void disassociateEmbeddedHierarchyLocked(@NonNull IBinder token) { 1201 disassociateLocked(token); 1202 } 1203 1204 /** 1205 * Gets the parent windowId of the window according to the specified windowId. 1206 * 1207 * @param windowId The windowId to check 1208 * @return The windowId of the parent window, or self if no parent exists 1209 */ resolveParentWindowIdLocked(int windowId)1210 public int resolveParentWindowIdLocked(int windowId) { 1211 final IBinder token = getTokenLocked(windowId); 1212 if (token == null) { 1213 return windowId; 1214 } 1215 final IBinder resolvedToken = resolveTopParentTokenLocked(token); 1216 final int resolvedWindowId = getWindowIdLocked(resolvedToken); 1217 return resolvedWindowId != -1 ? resolvedWindowId : windowId; 1218 } 1219 resolveTopParentTokenLocked(IBinder token)1220 private IBinder resolveTopParentTokenLocked(IBinder token) { 1221 final IBinder hostToken = getHostTokenLocked(token); 1222 if (hostToken == null) { 1223 return token; 1224 } 1225 return resolveTopParentTokenLocked(hostToken); 1226 } 1227 1228 /** 1229 * Computes partial interactive region of given windowId. 1230 * 1231 * @param windowId The windowId 1232 * @param outRegion The output to which to write the bounds. 1233 * @return true if outRegion is not empty. 1234 */ computePartialInteractiveRegionForWindowLocked(int windowId, @NonNull Region outRegion)1235 public boolean computePartialInteractiveRegionForWindowLocked(int windowId, 1236 @NonNull Region outRegion) { 1237 windowId = resolveParentWindowIdLocked(windowId); 1238 final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId); 1239 if (observer != null) { 1240 return observer.computePartialInteractiveRegionForWindowLocked(windowId, outRegion); 1241 } 1242 1243 return false; 1244 } 1245 1246 /** 1247 * Updates active windowId and accessibility focused windowId according to given accessibility 1248 * event and action. 1249 * 1250 * @param userId The userId 1251 * @param windowId The windowId of accessibility event 1252 * @param nodeId The accessibility node id of accessibility event 1253 * @param eventType The accessibility event type 1254 * @param eventAction The accessibility event action 1255 */ updateActiveAndAccessibilityFocusedWindowLocked(int userId, int windowId, long nodeId, int eventType, int eventAction)1256 public void updateActiveAndAccessibilityFocusedWindowLocked(int userId, int windowId, 1257 long nodeId, int eventType, int eventAction) { 1258 // The active window is either the window that has input focus or 1259 // the window that the user is currently touching. If the user is 1260 // touching a window that does not have input focus as soon as the 1261 // the user stops touching that window the focused window becomes 1262 // the active one. Here we detect the touched window and make it 1263 // active. In updateWindowsLocked() we update the focused window 1264 // and if the user is not touching the screen, we make the focused 1265 // window the active one. 1266 switch (eventType) { 1267 case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: { 1268 // If no service has the capability to introspect screen, 1269 // we do not register callback in the window manager for 1270 // window changes, so we have to ask the window manager 1271 // what the focused window is to update the active one. 1272 // The active window also determined events from which 1273 // windows are delivered. 1274 synchronized (mLock) { 1275 if (!isTrackingWindowsLocked()) { 1276 mTopFocusedWindowId = findFocusedWindowId(userId); 1277 if (windowId == mTopFocusedWindowId) { 1278 mActiveWindowId = windowId; 1279 } 1280 } 1281 } 1282 } break; 1283 1284 case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: { 1285 // Do not allow delayed hover events to confuse us 1286 // which the active window is. 1287 synchronized (mLock) { 1288 if (mTouchInteractionInProgress && mActiveWindowId != windowId) { 1289 setActiveWindowLocked(windowId); 1290 } 1291 } 1292 } break; 1293 1294 case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { 1295 synchronized (mLock) { 1296 if (mAccessibilityFocusedWindowId != windowId) { 1297 clearAccessibilityFocusLocked(mAccessibilityFocusedWindowId); 1298 setAccessibilityFocusedWindowLocked(windowId); 1299 } 1300 mAccessibilityFocusNodeId = nodeId; 1301 } 1302 } break; 1303 1304 case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: { 1305 synchronized (mLock) { 1306 if (mAccessibilityFocusNodeId == nodeId) { 1307 mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; 1308 } 1309 // Clear the window with focus if it no longer has focus and we aren't 1310 // just moving focus from one view to the other in the same window. 1311 if ((mAccessibilityFocusNodeId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) 1312 && (mAccessibilityFocusedWindowId == windowId) 1313 && (eventAction != AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS)) { 1314 mAccessibilityFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 1315 mAccessibilityFocusedDisplayId = Display.INVALID_DISPLAY; 1316 } 1317 } 1318 } break; 1319 } 1320 } 1321 1322 /** 1323 * Callbacks from AccessibilityManagerService when touch explorer turn on and 1324 * motion down detected. 1325 */ onTouchInteractionStart()1326 public void onTouchInteractionStart() { 1327 synchronized (mLock) { 1328 mTouchInteractionInProgress = true; 1329 } 1330 } 1331 1332 /** 1333 * Callbacks from AccessibilityManagerService when touch explorer turn on and 1334 * gesture or motion up detected. 1335 */ onTouchInteractionEnd()1336 public void onTouchInteractionEnd() { 1337 synchronized (mLock) { 1338 mTouchInteractionInProgress = false; 1339 // We want to set the active window to be current immediately 1340 // after the user has stopped touching the screen since if the 1341 // user types with the IME he should get a feedback for the 1342 // letter typed in the text view which is in the input focused 1343 // window. Note that we always deliver hover accessibility events 1344 // (they are a result of user touching the screen) so change of 1345 // the active window before all hover accessibility events from 1346 // the touched window are delivered is fine. 1347 final int oldActiveWindow = mActiveWindowId; 1348 setActiveWindowLocked(mTopFocusedWindowId); 1349 1350 // If there is no service that can operate with interactive windows 1351 // then we keep the old behavior where a window loses accessibility 1352 // focus if it is no longer active. This still changes the behavior 1353 // for services that do not operate with interactive windows and run 1354 // at the same time as the one(s) which does. In practice however, 1355 // there is only one service that uses accessibility focus and it 1356 // is typically the one that operates with interactive windows, So, 1357 // this is fine. Note that to allow a service to work across windows 1358 // we have to allow accessibility focus stay in any of them. Sigh... 1359 final boolean accessibilityFocusOnlyInActiveWindow = !isTrackingWindowsLocked(); 1360 if (oldActiveWindow != mActiveWindowId 1361 && mAccessibilityFocusedWindowId == oldActiveWindow 1362 && accessibilityFocusOnlyInActiveWindow) { 1363 clearAccessibilityFocusLocked(oldActiveWindow); 1364 } 1365 } 1366 } 1367 1368 /** 1369 * Gets the id of the current active window. 1370 * 1371 * @return The userId 1372 */ getActiveWindowId(int userId)1373 public int getActiveWindowId(int userId) { 1374 if (mActiveWindowId == AccessibilityWindowInfo.UNDEFINED_WINDOW_ID 1375 && !mTouchInteractionInProgress) { 1376 mActiveWindowId = findFocusedWindowId(userId); 1377 } 1378 return mActiveWindowId; 1379 } 1380 setActiveWindowLocked(int windowId)1381 private void setActiveWindowLocked(int windowId) { 1382 if (mActiveWindowId != windowId) { 1383 mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked( 1384 AccessibilityEvent.obtainWindowsChangedEvent( 1385 mActiveWindowId, AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)); 1386 1387 mActiveWindowId = windowId; 1388 // Goes through all windows for each display. 1389 final int count = mDisplayWindowsObservers.size(); 1390 for (int i = 0; i < count; i++) { 1391 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); 1392 if (observer != null) { 1393 observer.setActiveWindowLocked(windowId); 1394 } 1395 } 1396 } 1397 } 1398 setAccessibilityFocusedWindowLocked(int windowId)1399 private void setAccessibilityFocusedWindowLocked(int windowId) { 1400 if (mAccessibilityFocusedWindowId != windowId) { 1401 mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked( 1402 AccessibilityEvent.obtainWindowsChangedEvent( 1403 mAccessibilityFocusedWindowId, 1404 WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)); 1405 1406 mAccessibilityFocusedWindowId = windowId; 1407 // Goes through all windows for each display. 1408 final int count = mDisplayWindowsObservers.size(); 1409 for (int i = 0; i < count; i++) { 1410 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); 1411 if (observer != null) { 1412 observer.setAccessibilityFocusedWindowLocked(windowId); 1413 } 1414 } 1415 } 1416 } 1417 1418 /** 1419 * Returns accessibility window info according to given windowId. 1420 * 1421 * @param windowId The windowId 1422 * @return The accessibility window info 1423 */ 1424 @Nullable findA11yWindowInfoByIdLocked(int windowId)1425 public AccessibilityWindowInfo findA11yWindowInfoByIdLocked(int windowId) { 1426 windowId = resolveParentWindowIdLocked(windowId); 1427 final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId); 1428 if (observer != null) { 1429 return observer.findA11yWindowInfoByIdLocked(windowId); 1430 } 1431 return null; 1432 } 1433 1434 /** 1435 * Returns the window info according to given windowId. 1436 * 1437 * @param windowId The windowId 1438 * @return The window info 1439 */ 1440 @Nullable findWindowInfoByIdLocked(int windowId)1441 public WindowInfo findWindowInfoByIdLocked(int windowId) { 1442 windowId = resolveParentWindowIdLocked(windowId); 1443 final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId); 1444 if (observer != null) { 1445 return observer.findWindowInfoByIdLocked(windowId); 1446 } 1447 return null; 1448 } 1449 1450 /** 1451 * Returns focused windowId or accessibility focused windowId according to given focusType. 1452 * 1453 * @param focusType {@link AccessibilityNodeInfo#FOCUS_INPUT} or 1454 * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY} 1455 * @return The focused windowId 1456 */ getFocusedWindowId(int focusType)1457 public int getFocusedWindowId(int focusType) { 1458 if (focusType == AccessibilityNodeInfo.FOCUS_INPUT) { 1459 return mTopFocusedWindowId; 1460 } else if (focusType == AccessibilityNodeInfo.FOCUS_ACCESSIBILITY) { 1461 return mAccessibilityFocusedWindowId; 1462 } 1463 return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 1464 } 1465 1466 /** 1467 * Returns {@link AccessibilityWindowInfo} of PIP window. 1468 * 1469 * @return PIP accessibility window info 1470 */ 1471 @Nullable getPictureInPictureWindowLocked()1472 public AccessibilityWindowInfo getPictureInPictureWindowLocked() { 1473 AccessibilityWindowInfo windowInfo = null; 1474 final int count = mDisplayWindowsObservers.size(); 1475 for (int i = 0; i < count; i++) { 1476 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); 1477 if (observer != null) { 1478 if ((windowInfo = observer.getPictureInPictureWindowLocked()) != null) { 1479 break; 1480 } 1481 } 1482 } 1483 return windowInfo; 1484 } 1485 1486 /** 1487 * Sets an IAccessibilityInteractionConnection to replace the actions of a picture-in-picture 1488 * window. 1489 */ setPictureInPictureActionReplacingConnection( @ullable IAccessibilityInteractionConnection connection)1490 public void setPictureInPictureActionReplacingConnection( 1491 @Nullable IAccessibilityInteractionConnection connection) throws RemoteException { 1492 synchronized (mLock) { 1493 if (mPictureInPictureActionReplacingConnection != null) { 1494 mPictureInPictureActionReplacingConnection.unlinkToDeath(); 1495 mPictureInPictureActionReplacingConnection = null; 1496 } 1497 if (connection != null) { 1498 RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection( 1499 AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID, 1500 connection, "foo.bar.baz", Process.SYSTEM_UID, UserHandle.USER_ALL); 1501 mPictureInPictureActionReplacingConnection = wrapper; 1502 wrapper.linkToDeath(); 1503 } 1504 } 1505 } 1506 1507 /** 1508 * Returns accessibility interaction connection for picture-in-picture window. 1509 */ 1510 @Nullable getPictureInPictureActionReplacingConnection()1511 public RemoteAccessibilityConnection getPictureInPictureActionReplacingConnection() { 1512 return mPictureInPictureActionReplacingConnection; 1513 } 1514 1515 /** 1516 * Invokes {@link IAccessibilityInteractionConnection#notifyOutsideTouch()} for windows that 1517 * have watch outside touch flag and its layer is upper than target window. 1518 */ notifyOutsideTouch(int userId, int targetWindowId)1519 public void notifyOutsideTouch(int userId, int targetWindowId) { 1520 final List<Integer> outsideWindowsIds; 1521 final List<RemoteAccessibilityConnection> connectionList = new ArrayList<>(); 1522 synchronized (mLock) { 1523 final DisplayWindowsObserver observer = 1524 getDisplayWindowObserverByWindowIdLocked(targetWindowId); 1525 if (observer != null) { 1526 outsideWindowsIds = observer.getWatchOutsideTouchWindowIdLocked(targetWindowId); 1527 for (int i = 0; i < outsideWindowsIds.size(); i++) { 1528 connectionList.add(getConnectionLocked(userId, outsideWindowsIds.get(i))); 1529 } 1530 } 1531 } 1532 for (int i = 0; i < connectionList.size(); i++) { 1533 final RemoteAccessibilityConnection connection = connectionList.get(i); 1534 if (connection != null) { 1535 try { 1536 connection.getRemote().notifyOutsideTouch(); 1537 } catch (RemoteException re) { 1538 if (DEBUG) { 1539 Slog.e(LOG_TAG, "Error calling notifyOutsideTouch()"); 1540 } 1541 } 1542 } 1543 } 1544 } 1545 1546 /** 1547 * Returns the display ID according to given userId and windowId. 1548 * 1549 * @param userId The userId 1550 * @param windowId The windowId 1551 * @return The display ID 1552 */ getDisplayIdByUserIdAndWindowIdLocked(int userId, int windowId)1553 public int getDisplayIdByUserIdAndWindowIdLocked(int userId, int windowId) { 1554 final IBinder windowToken = getWindowTokenForUserAndWindowIdLocked(userId, windowId); 1555 final int displayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken); 1556 return displayId; 1557 } 1558 1559 /** 1560 * Returns the display list including all displays which are tracking windows. 1561 * 1562 * @return The display list. 1563 */ getDisplayListLocked()1564 public ArrayList<Integer> getDisplayListLocked() { 1565 final ArrayList<Integer> displayList = new ArrayList<>(); 1566 final int count = mDisplayWindowsObservers.size(); 1567 for (int i = 0; i < count; i++) { 1568 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); 1569 if (observer != null) { 1570 displayList.add(observer.mDisplayId); 1571 } 1572 } 1573 return displayList; 1574 } 1575 1576 /** 1577 * Gets current input focused window token from window manager, and returns its windowId. 1578 * 1579 * @param userId The userId 1580 * @return The input focused windowId, or -1 if not found 1581 */ findFocusedWindowId(int userId)1582 private int findFocusedWindowId(int userId) { 1583 final IBinder token = mWindowManagerInternal.getFocusedWindowToken(); 1584 synchronized (mLock) { 1585 return findWindowIdLocked(userId, token); 1586 } 1587 } 1588 isValidUserForInteractionConnectionsLocked(int userId)1589 private boolean isValidUserForInteractionConnectionsLocked(int userId) { 1590 return mInteractionConnections.indexOfKey(userId) >= 0; 1591 } 1592 isValidUserForWindowTokensLocked(int userId)1593 private boolean isValidUserForWindowTokensLocked(int userId) { 1594 return mWindowTokens.indexOfKey(userId) >= 0; 1595 } 1596 getInteractionConnectionsForUserLocked( int userId)1597 private SparseArray<RemoteAccessibilityConnection> getInteractionConnectionsForUserLocked( 1598 int userId) { 1599 SparseArray<RemoteAccessibilityConnection> connection = mInteractionConnections.get( 1600 userId); 1601 if (connection == null) { 1602 connection = new SparseArray<>(); 1603 mInteractionConnections.put(userId, connection); 1604 } 1605 return connection; 1606 } 1607 getWindowTokensForUserLocked(int userId)1608 private SparseArray<IBinder> getWindowTokensForUserLocked(int userId) { 1609 SparseArray<IBinder> windowTokens = mWindowTokens.get(userId); 1610 if (windowTokens == null) { 1611 windowTokens = new SparseArray<>(); 1612 mWindowTokens.put(userId, windowTokens); 1613 } 1614 return windowTokens; 1615 } 1616 clearAccessibilityFocusLocked(int windowId)1617 private void clearAccessibilityFocusLocked(int windowId) { 1618 mHandler.sendMessage(obtainMessage( 1619 AccessibilityWindowManager::clearAccessibilityFocusMainThread, 1620 AccessibilityWindowManager.this, 1621 mAccessibilityUserManager.getCurrentUserIdLocked(), windowId)); 1622 } 1623 clearAccessibilityFocusMainThread(int userId, int windowId)1624 private void clearAccessibilityFocusMainThread(int userId, int windowId) { 1625 final RemoteAccessibilityConnection connection; 1626 synchronized (mLock) { 1627 connection = getConnectionLocked(userId, windowId); 1628 if (connection == null) { 1629 return; 1630 } 1631 } 1632 try { 1633 connection.getRemote().clearAccessibilityFocus(); 1634 } catch (RemoteException re) { 1635 if (DEBUG) { 1636 Slog.e(LOG_TAG, "Error calling clearAccessibilityFocus()"); 1637 } 1638 } 1639 } 1640 getDisplayWindowObserverByWindowIdLocked(int windowId)1641 private DisplayWindowsObserver getDisplayWindowObserverByWindowIdLocked(int windowId) { 1642 final int count = mDisplayWindowsObservers.size(); 1643 for (int i = 0; i < count; i++) { 1644 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); 1645 if (observer != null) { 1646 if (observer.findWindowInfoByIdLocked(windowId) != null) { 1647 return mDisplayWindowsObservers.get(observer.mDisplayId); 1648 } 1649 } 1650 } 1651 return null; 1652 } 1653 1654 /** 1655 * Associate the token of the embedded view hierarchy to the host view hierarchy. 1656 * 1657 * @param embedded The leash token from the view root of embedded hierarchy 1658 * @param host The leash token from the view root of host hierarchy 1659 */ associateLocked(IBinder embedded, IBinder host)1660 void associateLocked(IBinder embedded, IBinder host) { 1661 mHostEmbeddedMap.put(embedded, host); 1662 } 1663 1664 /** 1665 * Clear the relationship of given token. 1666 * 1667 * @param token The leash token 1668 */ disassociateLocked(IBinder token)1669 void disassociateLocked(IBinder token) { 1670 mHostEmbeddedMap.remove(token); 1671 for (int i = mHostEmbeddedMap.size() - 1; i >= 0; i--) { 1672 if (mHostEmbeddedMap.valueAt(i).equals(token)) { 1673 mHostEmbeddedMap.removeAt(i); 1674 } 1675 } 1676 } 1677 1678 /** 1679 * Register the leash token with its windowId. 1680 * 1681 * @param token The token. 1682 * @param windowId The windowID. 1683 */ registerIdLocked(IBinder token, int windowId)1684 void registerIdLocked(IBinder token, int windowId) { 1685 mWindowIdMap.put(windowId, token); 1686 } 1687 1688 /** 1689 * Unregister the windowId and also disassociate its token. 1690 * 1691 * @param windowId The windowID 1692 */ unregisterIdLocked(int windowId)1693 void unregisterIdLocked(int windowId) { 1694 final IBinder token = mWindowIdMap.get(windowId); 1695 if (token == null) { 1696 return; 1697 } 1698 disassociateLocked(token); 1699 mWindowIdMap.remove(windowId); 1700 } 1701 1702 /** 1703 * Get the leash token by given windowID. 1704 * 1705 * @param windowId The windowID. 1706 * @return The token, or {@code NULL} if this windowID doesn't exist 1707 */ getTokenLocked(int windowId)1708 IBinder getTokenLocked(int windowId) { 1709 return mWindowIdMap.get(windowId); 1710 } 1711 1712 /** 1713 * Get the windowId by given leash token. 1714 * 1715 * @param token The token 1716 * @return The windowID, or -1 if the token doesn't exist 1717 */ getWindowIdLocked(IBinder token)1718 int getWindowIdLocked(IBinder token) { 1719 final int index = mWindowIdMap.indexOfValue(token); 1720 if (index == -1) { 1721 return index; 1722 } 1723 return mWindowIdMap.keyAt(index); 1724 } 1725 1726 /** 1727 * Get the leash token of the host hierarchy by given token. 1728 * 1729 * @param token The token 1730 * @return The token of host hierarchy, or {@code NULL} if no host exists 1731 */ getHostTokenLocked(IBinder token)1732 IBinder getHostTokenLocked(IBinder token) { 1733 return mHostEmbeddedMap.get(token); 1734 } 1735 1736 /** 1737 * Dumps all {@link AccessibilityWindowInfo}s here. 1738 */ dump(FileDescriptor fd, final PrintWriter pw, String[] args)1739 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { 1740 final int count = mDisplayWindowsObservers.size(); 1741 for (int i = 0; i < count; i++) { 1742 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); 1743 if (observer != null) { 1744 observer.dumpLocked(fd, pw, args); 1745 } 1746 } 1747 } 1748 } 1749