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.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION; 20 import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL; 21 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; 22 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; 23 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; 24 import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED; 25 26 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; 27 import static com.android.server.accessibility.AbstractAccessibilityServiceConnection.DISPLAY_TYPE_DEFAULT; 28 import static com.android.server.accessibility.AbstractAccessibilityServiceConnection.DISPLAY_TYPE_PROXY; 29 30 import android.annotation.NonNull; 31 import android.annotation.Nullable; 32 import android.graphics.Point; 33 import android.graphics.Region; 34 import android.os.Binder; 35 import android.os.Handler; 36 import android.os.IBinder; 37 import android.os.Process; 38 import android.os.RemoteException; 39 import android.os.SystemClock; 40 import android.os.UserHandle; 41 import android.text.TextUtils; 42 import android.util.ArrayMap; 43 import android.util.ArraySet; 44 import android.util.Slog; 45 import android.util.SparseArray; 46 import android.view.Display; 47 import android.view.IWindow; 48 import android.view.WindowInfo; 49 import android.view.WindowManager; 50 import android.view.accessibility.AccessibilityEvent; 51 import android.view.accessibility.AccessibilityNodeInfo; 52 import android.view.accessibility.AccessibilityWindowAttributes; 53 import android.view.accessibility.AccessibilityWindowInfo; 54 import android.view.accessibility.IAccessibilityInteractionConnection; 55 56 import com.android.internal.annotations.VisibleForTesting; 57 import com.android.server.accessibility.AccessibilitySecurityPolicy.AccessibilityUserManager; 58 import com.android.server.utils.Slogf; 59 import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow; 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.Collections; 67 import java.util.List; 68 import java.util.Set; 69 import java.util.stream.Collectors; 70 71 /** 72 * This class provides APIs for accessibility manager to manage {@link AccessibilityWindowInfo}s and 73 * {@link WindowInfo}s. 74 */ 75 public class AccessibilityWindowManager { 76 private static final String LOG_TAG = "AccessibilityWindowManager"; 77 private static final boolean DEBUG = false; 78 private static final boolean VERBOSE = false; 79 80 private static int sNextWindowId; 81 82 private final Region mTmpRegion = new Region(); 83 84 private final Object mLock; 85 private final Handler mHandler; 86 private final WindowManagerInternal mWindowManagerInternal; 87 private final AccessibilityEventSender mAccessibilityEventSender; 88 private final AccessibilitySecurityPolicy mSecurityPolicy; 89 private final AccessibilityUserManager mAccessibilityUserManager; 90 private final AccessibilityTraceManager mTraceManager; 91 92 // Connections and window tokens for cross-user windows 93 private final SparseArray<RemoteAccessibilityConnection> 94 mGlobalInteractionConnections = new SparseArray<>(); 95 private final SparseArray<IBinder> mGlobalWindowTokens = new SparseArray<>(); 96 97 // Connections and window tokens for per-user windows, indexed as one sparse array per user 98 private final SparseArray<SparseArray<RemoteAccessibilityConnection>> 99 mInteractionConnections = new SparseArray<>(); 100 private final SparseArray<SparseArray<IBinder>> mWindowTokens = new SparseArray<>(); 101 102 private RemoteAccessibilityConnection mPictureInPictureActionReplacingConnection; 103 // There is only one active window in the system. It is updated when the top focused window 104 // of the top focused display changes and when we receive a TYPE_WINDOW_STATE_CHANGED event. 105 private int mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 106 // There is only one top focused window in the system. It is updated when the window manager 107 // updates the window lists. 108 private int mTopFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 109 private int mAccessibilityFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 110 private long mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; 111 // The top focused display and window token updated with the callback of window lists change. 112 private int mTopFocusedDisplayId; 113 private IBinder mTopFocusedWindowToken; 114 115 // The non-proxy display that most recently had top focus. 116 private int mLastNonProxyTopFocusedDisplayId; 117 // The display has the accessibility focused window currently. 118 private int mAccessibilityFocusedDisplayId = Display.INVALID_DISPLAY; 119 120 private boolean mTouchInteractionInProgress; 121 122 private boolean mHasProxy; 123 124 /** List of Display Windows Observer, mapping from displayId -> DisplayWindowsObserver. */ 125 private final SparseArray<DisplayWindowsObserver> mDisplayWindowsObservers = 126 new SparseArray<>(); 127 128 /** 129 * Map of host view and embedded hierarchy, mapping from leash token of its ViewRootImpl. 130 * The key is the token from embedded hierarchy, and the value is the token from its host. 131 */ 132 private final ArrayMap<IBinder, IBinder> mHostEmbeddedMap = new ArrayMap<>(); 133 134 /** 135 * Map of window id and view hierarchy. 136 * The key is the window id when the ViewRootImpl register to accessibility, and the value is 137 * its leash token. 138 */ 139 private final SparseArray<IBinder> mWindowIdMap = new SparseArray<>(); 140 141 /** 142 * Map of window id and window attribute hierarchy. 143 * The key is the window id when the ViewRootImpl register to accessibility, and the value is 144 * its window attribute . 145 */ 146 private final SparseArray<AccessibilityWindowAttributes> mWindowAttributes = 147 new SparseArray<>(); 148 149 /** 150 * Sets the {@link AccessibilityWindowAttributes} to the window associated with the given 151 * window id. 152 * 153 * @param displayId The display id of the window. 154 * @param windowId The id of the window 155 * @param userId The user id. 156 * @param attributes The accessibility window attributes. 157 */ setAccessibilityWindowAttributes(int displayId, int windowId, int userId, AccessibilityWindowAttributes attributes)158 public void setAccessibilityWindowAttributes(int displayId, int windowId, int userId, 159 AccessibilityWindowAttributes attributes) { 160 boolean shouldComputeWindows = false; 161 synchronized (mLock) { 162 final int resolvedUserId = 163 mSecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(userId); 164 if (getWindowTokenForUserAndWindowIdLocked(resolvedUserId, windowId) == null) { 165 return; 166 } 167 mWindowAttributes.put(windowId, attributes); 168 shouldComputeWindows = findWindowInfoByIdLocked(windowId) != null; 169 } 170 if (shouldComputeWindows) { 171 mWindowManagerInternal.computeWindowsForAccessibility(displayId); 172 } 173 } 174 175 /** 176 * Returns {@code true} if the window belongs to a display of {@code displayTypes}. 177 */ windowIdBelongsToDisplayType(int focusedWindowId, int displayTypes)178 public boolean windowIdBelongsToDisplayType(int focusedWindowId, int displayTypes) { 179 if (!mHasProxy) { 180 return true; 181 } 182 // UIAutomation wants focus from any display type. 183 final int displayTypeMask = DISPLAY_TYPE_PROXY | DISPLAY_TYPE_DEFAULT; 184 if ((displayTypes & displayTypeMask) == displayTypeMask) { 185 return true; 186 } 187 synchronized (mLock) { 188 final int count = mDisplayWindowsObservers.size(); 189 for (int i = 0; i < count; i++) { 190 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); 191 if (observer != null 192 && observer.findA11yWindowInfoByIdLocked(focusedWindowId) != null) { 193 return observer.mIsProxy 194 ? ((displayTypes & DISPLAY_TYPE_PROXY) != 0) 195 : (displayTypes & DISPLAY_TYPE_DEFAULT) != 0; 196 } 197 } 198 } 199 return false; 200 } 201 202 /** 203 * This class implements {@link WindowManagerInternal.WindowsForAccessibilityCallback} to 204 * receive {@link WindowInfo}s from window manager when there's an accessibility change in 205 * window and holds window lists information per display. 206 */ 207 private final class DisplayWindowsObserver implements 208 WindowManagerInternal.WindowsForAccessibilityCallback { 209 210 private final int mDisplayId; 211 private final SparseArray<AccessibilityWindowInfo> mA11yWindowInfoById = 212 new SparseArray<>(); 213 private final SparseArray<WindowInfo> mWindowInfoById = new SparseArray<>(); 214 private final List<WindowInfo> mCachedWindowInfos = new ArrayList<>(); 215 private List<AccessibilityWindowInfo> mWindows; 216 private boolean mTrackingWindows = false; 217 private boolean mHasWatchOutsideTouchWindow; 218 private int mProxyDisplayAccessibilityFocusedWindow = 219 AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 220 private boolean mIsProxy; 221 222 /** 223 * Constructor for DisplayWindowsObserver. 224 */ DisplayWindowsObserver(int displayId)225 DisplayWindowsObserver(int displayId) { 226 if (DEBUG) { 227 Slogf.d(LOG_TAG, "Creating DisplayWindowsObserver for displayId %d", displayId); 228 } 229 mDisplayId = displayId; 230 } 231 232 /** 233 * Starts tracking windows changes from window manager by registering callback. 234 */ startTrackingWindowsLocked()235 void startTrackingWindowsLocked() { 236 if (!mTrackingWindows) { 237 // Turns on the flag before setup the callback. 238 // In some cases, onWindowsForAccessibilityChanged will be called immediately in 239 // setWindowsForAccessibilityCallback. We'll lost windows if flag is false. 240 mTrackingWindows = true; 241 if (traceWMEnabled()) { 242 logTraceWM("setWindowsForAccessibilityCallback", 243 "displayId=" + mDisplayId + ";callback=" + this); 244 } 245 mWindowManagerInternal.setWindowsForAccessibilityCallback( 246 mDisplayId, this); 247 } 248 } 249 250 /** 251 * Stops tracking windows changes from window manager, and clear all windows info. 252 */ stopTrackingWindowsLocked()253 void stopTrackingWindowsLocked() { 254 if (mTrackingWindows) { 255 if (traceWMEnabled()) { 256 logTraceWM("setWindowsForAccessibilityCallback", 257 "displayId=" + mDisplayId + ";callback=null"); 258 } 259 mWindowManagerInternal.setWindowsForAccessibilityCallback( 260 mDisplayId, null); 261 mTrackingWindows = false; 262 clearWindowsLocked(); 263 } 264 } 265 266 /** 267 * Returns true if windows changes tracking. 268 * 269 * @return true if windows changes tracking 270 */ isTrackingWindowsLocked()271 boolean isTrackingWindowsLocked() { 272 return mTrackingWindows; 273 } 274 275 /** 276 * Returns accessibility windows. 277 * @return accessibility windows. 278 */ 279 @Nullable getWindowListLocked()280 List<AccessibilityWindowInfo> getWindowListLocked() { 281 return mWindows; 282 } 283 284 /** 285 * Returns accessibility window info according to given windowId. 286 * 287 * @param windowId The windowId 288 * @return The accessibility window info 289 */ 290 @Nullable findA11yWindowInfoByIdLocked(int windowId)291 AccessibilityWindowInfo findA11yWindowInfoByIdLocked(int windowId) { 292 return mA11yWindowInfoById.get(windowId); 293 } 294 295 /** 296 * Returns the window info according to given windowId. 297 * 298 * @param windowId The windowId 299 * @return The window info 300 */ 301 @Nullable findWindowInfoByIdLocked(int windowId)302 WindowInfo findWindowInfoByIdLocked(int windowId) { 303 return mWindowInfoById.get(windowId); 304 } 305 306 /** 307 * Returns {@link AccessibilityWindowInfo} of PIP window. 308 * 309 * @return PIP accessibility window info 310 */ 311 @Nullable getPictureInPictureWindowLocked()312 AccessibilityWindowInfo getPictureInPictureWindowLocked() { 313 if (mWindows != null) { 314 final int windowCount = mWindows.size(); 315 for (int i = 0; i < windowCount; i++) { 316 final AccessibilityWindowInfo window = mWindows.get(i); 317 if (window.isInPictureInPictureMode()) { 318 return window; 319 } 320 } 321 } 322 return null; 323 } 324 325 /** 326 * Sets the active flag of the window according to given windowId, others set to inactive. 327 * 328 * @param windowId The windowId 329 * @return {@code true} if the window is in this display, {@code false} otherwise. 330 */ setActiveWindowLocked(int windowId)331 boolean setActiveWindowLocked(int windowId) { 332 boolean foundWindow = false; 333 if (mWindows != null) { 334 final int windowCount = mWindows.size(); 335 for (int i = 0; i < windowCount; i++) { 336 AccessibilityWindowInfo window = mWindows.get(i); 337 if (window.getId() == windowId) { 338 window.setActive(true); 339 foundWindow = true; 340 } else { 341 window.setActive(false); 342 } 343 } 344 } 345 return foundWindow; 346 } 347 348 /** 349 * Sets the window accessibility focused according to given windowId, others set 350 * unfocused. 351 * 352 * @param windowId The windowId 353 * @return {@code true} if the window is in this display, {@code false} otherwise. 354 */ setAccessibilityFocusedWindowLocked(int windowId)355 boolean setAccessibilityFocusedWindowLocked(int windowId) { 356 boolean foundWindow = false; 357 if (mWindows != null) { 358 final int windowCount = mWindows.size(); 359 for (int i = 0; i < windowCount; i++) { 360 AccessibilityWindowInfo window = mWindows.get(i); 361 if (window.getId() == windowId) { 362 window.setAccessibilityFocused(true); 363 foundWindow = true; 364 } else { 365 window.setAccessibilityFocused(false); 366 } 367 } 368 } 369 return foundWindow; 370 } 371 372 /** 373 * Computes partial interactive region of given windowId. 374 * 375 * @param windowId The windowId 376 * @param forceComputeRegion set outRegion when the windowId matches one on the screen even 377 * though the region is not covered by other windows above it. 378 * @param outRegion The output to which to write the bounds. 379 * @return {@code true} if outRegion is not empty. 380 */ computePartialInteractiveRegionForWindowLocked(int windowId, boolean forceComputeRegion, @NonNull Region outRegion)381 boolean computePartialInteractiveRegionForWindowLocked(int windowId, 382 boolean forceComputeRegion, @NonNull Region outRegion) { 383 if (mWindows == null) { 384 return false; 385 } 386 387 // Windows are ordered in z order so start from the bottom and find 388 // the window of interest. After that all windows that cover it should 389 // be subtracted from the resulting region. Note that for accessibility 390 // we are returning only interactive windows. 391 Region windowInteractiveRegion = null; 392 boolean windowInteractiveRegionChanged = false; 393 394 final int windowCount = mWindows.size(); 395 final Region currentWindowRegions = new Region(); 396 for (int i = windowCount - 1; i >= 0; i--) { 397 AccessibilityWindowInfo currentWindow = mWindows.get(i); 398 if (windowInteractiveRegion == null) { 399 if (currentWindow.getId() == windowId) { 400 currentWindow.getRegionInScreen(currentWindowRegions); 401 outRegion.set(currentWindowRegions); 402 windowInteractiveRegion = outRegion; 403 if (forceComputeRegion) { 404 windowInteractiveRegionChanged = true; 405 } 406 continue; 407 } 408 } else if (currentWindow.getType() 409 != AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY) { 410 currentWindow.getRegionInScreen(currentWindowRegions); 411 if (windowInteractiveRegion.op(currentWindowRegions, Region.Op.DIFFERENCE)) { 412 windowInteractiveRegionChanged = true; 413 } 414 } 415 } 416 417 return windowInteractiveRegionChanged; 418 } 419 getWatchOutsideTouchWindowIdLocked(int targetWindowId)420 List<Integer> getWatchOutsideTouchWindowIdLocked(int targetWindowId) { 421 final WindowInfo targetWindow = mWindowInfoById.get(targetWindowId); 422 if (targetWindow != null && mHasWatchOutsideTouchWindow) { 423 final List<Integer> outsideWindowsId = new ArrayList<>(); 424 for (int i = 0; i < mWindowInfoById.size(); i++) { 425 final WindowInfo window = mWindowInfoById.valueAt(i); 426 if (window != null && window.layer < targetWindow.layer 427 && window.hasFlagWatchOutsideTouch) { 428 outsideWindowsId.add(mWindowInfoById.keyAt(i)); 429 } 430 } 431 return outsideWindowsId; 432 } 433 return Collections.emptyList(); 434 } 435 436 /** 437 * Callbacks from window manager when there's an accessibility change in windows. 438 * 439 * @param forceSend Send the windows for accessibility even if they haven't changed. 440 * @param topFocusedDisplayId The display Id which has the top focused window. 441 * @param topFocusedWindowToken The window token of top focused window. 442 * @param windows The windows for accessibility. 443 */ 444 @Override onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId, IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows)445 public void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId, 446 IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows) { 447 synchronized (mLock) { 448 if (!Flags.computeWindowChangesOnA11yV2()) { 449 // If the flag is enabled, it's already done in #createWindowInfoListLocked. 450 updateWindowsByWindowAttributesLocked(windows); 451 } 452 if (DEBUG) { 453 Slogf.i(LOG_TAG, "mDisplayId=%d, topFocusedDisplayId=%d, currentUserId=%d, " 454 + "visibleBgUsers=%s", mDisplayId, topFocusedDisplayId, 455 mAccessibilityUserManager.getCurrentUserIdLocked(), 456 mAccessibilityUserManager.getVisibleUserIdsLocked()); 457 if (VERBOSE) { 458 Slogf.i(LOG_TAG, "%d windows changed: %s ", windows.size(), windows); 459 } else { 460 List<String> windowsInfo = windows.stream() 461 .map(w -> "{displayId=" + w.displayId + ", title=" + w.title + "}") 462 .collect(Collectors.toList()); 463 Slogf.i(LOG_TAG, "%d windows changed: %s", windows.size(), windowsInfo); 464 } 465 } 466 if (shouldUpdateWindowsLocked(forceSend, windows)) { 467 mTopFocusedDisplayId = topFocusedDisplayId; 468 if (!isProxyed(topFocusedDisplayId)) { 469 mLastNonProxyTopFocusedDisplayId = topFocusedDisplayId; 470 } 471 mTopFocusedWindowToken = topFocusedWindowToken; 472 if (DEBUG) { 473 Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): updating windows for " 474 + "display %d and token %s", 475 topFocusedDisplayId, topFocusedWindowToken); 476 } 477 cacheWindows(windows); 478 // Lets the policy update the focused and active windows. 479 updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), 480 windows); 481 // Someone may be waiting for the windows - advertise it. 482 mLock.notifyAll(); 483 } 484 else if (DEBUG) { 485 Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): NOT updating windows for " 486 + "display %d and token %s", 487 topFocusedDisplayId, topFocusedWindowToken); 488 } 489 } 490 } 491 492 /** 493 * Called when the windows for accessibility changed. This is called if 494 * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2} is 495 * true. 496 * 497 * @param forceSend Send the windows for accessibility even if they haven't 498 * changed. 499 * @param topFocusedDisplayId The display Id which has the top focused window. 500 * @param topFocusedWindowToken The window token of top focused window. 501 * @param screenSize The size of the display that the change happened. 502 * @param windows The windows for accessibility. 503 */ 504 @Override onAccessibilityWindowsChanged(boolean forceSend, int topFocusedDisplayId, @NonNull IBinder topFocusedWindowToken, @NonNull Point screenSize, @NonNull List<AccessibilityWindow> windows)505 public void onAccessibilityWindowsChanged(boolean forceSend, int topFocusedDisplayId, 506 @NonNull IBinder topFocusedWindowToken, @NonNull Point screenSize, 507 @NonNull List<AccessibilityWindow> windows) { 508 synchronized (mLock) { 509 final List<WindowInfo> windowInfoList = 510 createWindowInfoListLocked(screenSize, windows); 511 onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId, 512 topFocusedWindowToken, windowInfoList); 513 } 514 } 515 createWindowInfoListLocked(@onNull Point screenSize, @NonNull List<AccessibilityWindow> visibleWindows)516 private List<WindowInfo> createWindowInfoListLocked(@NonNull Point screenSize, 517 @NonNull List<AccessibilityWindow> visibleWindows) { 518 final Set<IBinder> addedWindows = new ArraySet<>(); 519 final List<WindowInfo> windows = new ArrayList<>(); 520 521 // Avoid allocating Region for each window. 522 final Region regionInWindow = new Region(); 523 final Region touchableRegionInScreen = new Region(); 524 525 final int userId = mAccessibilityUserManager.getCurrentUserIdLocked(); 526 527 // Iterate until we figure out what is touchable for the entire screen. 528 boolean focusedWindowAdded = false; 529 final Region unaccountedSpace = new Region(0, 0, screenSize.x, screenSize.y); 530 for (final AccessibilityWindow a11yWindow : visibleWindows) { 531 a11yWindow.getTouchableRegionInWindow(regionInWindow); 532 533 final WindowInfo window = a11yWindow.getWindowInfo(); 534 final int windowId = window.token != null 535 ? findWindowIdLocked(userId, window.token) 536 : AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 537 538 if (windowMattersToAccessibilityLocked(a11yWindow, windowId, regionInWindow, 539 unaccountedSpace)) { 540 if (windowId >= 0) { 541 // Even if token is null, the window will be used in calculating visible 542 // windows, but is excluded from the accessibility window list. 543 window.regionInScreen.set(regionInWindow); 544 window.layer = addedWindows.size(); 545 updateWindowWithWindowAttributes(window, mWindowAttributes.get(windowId)); 546 547 windows.add(window); 548 addedWindows.add(window.token); 549 } 550 551 if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) { 552 // Account for the space this window takes. 553 a11yWindow.getTouchableRegionInScreen(touchableRegionInScreen); 554 unaccountedSpace.op(touchableRegionInScreen, unaccountedSpace, 555 Region.Op.REVERSE_DIFFERENCE); 556 } 557 558 focusedWindowAdded |= a11yWindow.isFocused(); 559 } else if (a11yWindow.isUntouchableNavigationBar() 560 && a11yWindow.getSystemBarInsetsFrame() != null) { 561 // If this widow is navigation bar without touchable region, accounting the 562 // region of navigation bar inset because all touch events from this region 563 // would be received by launcher, i.e. this region is a un-touchable one 564 // for the application. 565 unaccountedSpace.op( 566 a11yWindow.getSystemBarInsetsFrame(), 567 unaccountedSpace, 568 Region.Op.REVERSE_DIFFERENCE); 569 } 570 571 if (unaccountedSpace.isEmpty() && focusedWindowAdded) { 572 break; 573 } 574 } 575 576 // Remove child/parent references to windows that were not added. 577 for (final WindowInfo window : windows) { 578 if (!addedWindows.contains(window.parentToken)) { 579 window.parentToken = null; 580 } 581 if (window.childTokens != null) { 582 final int childTokenCount = window.childTokens.size(); 583 for (int j = childTokenCount - 1; j >= 0; j--) { 584 if (!addedWindows.contains(window.childTokens.get(j))) { 585 window.childTokens.remove(j); 586 } 587 } 588 // Leave the child token list if empty. 589 } 590 } 591 592 return windows; 593 } 594 windowMattersToAccessibilityLocked(AccessibilityWindow a11yWindow, int windowId, Region regionInScreen, Region unaccountedSpace)595 private boolean windowMattersToAccessibilityLocked(AccessibilityWindow a11yWindow, 596 int windowId, Region regionInScreen, Region unaccountedSpace) { 597 if (a11yWindow.ignoreRecentsAnimationForAccessibility()) { 598 return false; 599 } 600 601 if (a11yWindow.isFocused()) { 602 return true; 603 } 604 605 // Ignore non-touchable windows, except the split-screen divider, which is 606 // occasionally non-touchable but still useful for identifying split-screen 607 // mode and the PIP menu. 608 if (!a11yWindow.isTouchable() 609 && a11yWindow.getType() != TYPE_DOCK_DIVIDER && !a11yWindow.isPIPMenu()) { 610 return false; 611 } 612 613 if (isEmbeddedHierarchyWindowsLocked(windowId)) { 614 return false; 615 } 616 617 // If the window is completely covered by other windows - ignore. 618 if (!mTmpRegion.op(unaccountedSpace, regionInScreen, Region.Op.INTERSECT)) { 619 return false; 620 } 621 622 // Add windows of certain types not covered by modal windows. 623 if (isReportedWindowType(a11yWindow.getType())) { 624 return true; 625 } 626 627 return false; 628 } 629 isReportedWindowType(int windowType)630 private static boolean isReportedWindowType(int windowType) { 631 return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER 632 && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS 633 && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY 634 && windowType != WindowManager.LayoutParams.TYPE_DRAG 635 && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER 636 && windowType != WindowManager.LayoutParams.TYPE_POINTER 637 && windowType != TYPE_MAGNIFICATION_OVERLAY 638 && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY 639 && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY 640 && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION); 641 } 642 643 // Some windows should be excluded from unaccounted space computation, though they still 644 // should be reported windowMattersToUnaccountedSpaceComputation( AccessibilityWindow a11yWindow)645 private static boolean windowMattersToUnaccountedSpaceComputation( 646 AccessibilityWindow a11yWindow) { 647 // Do not account space of trusted non-touchable windows, except the split-screen 648 // divider. 649 // If it's not trusted, touch events are not sent to the windows behind it. 650 if (!a11yWindow.isTouchable() 651 && (a11yWindow.getType() != TYPE_DOCK_DIVIDER) 652 && a11yWindow.isTrustedOverlay()) { 653 return false; 654 } 655 656 if (a11yWindow.getType() == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { 657 return false; 658 } 659 return true; 660 } 661 updateWindowsByWindowAttributesLocked(List<WindowInfo> windows)662 private void updateWindowsByWindowAttributesLocked(List<WindowInfo> windows) { 663 for (int i = windows.size() - 1; i >= 0; i--) { 664 final WindowInfo windowInfo = windows.get(i); 665 final IBinder token = windowInfo.token; 666 final int windowId = findWindowIdLocked( 667 mAccessibilityUserManager.getCurrentUserIdLocked(), token); 668 updateWindowWithWindowAttributes(windowInfo, mWindowAttributes.get(windowId)); 669 } 670 } 671 updateWindowWithWindowAttributes(@onNull WindowInfo windowInfo, @Nullable AccessibilityWindowAttributes attributes)672 private void updateWindowWithWindowAttributes(@NonNull WindowInfo windowInfo, 673 @Nullable AccessibilityWindowAttributes attributes) { 674 if (attributes == null) { 675 return; 676 } 677 windowInfo.title = attributes.getWindowTitle(); 678 windowInfo.locales = attributes.getLocales(); 679 } 680 shouldUpdateWindowsLocked(boolean forceSend, @NonNull List<WindowInfo> windows)681 private boolean shouldUpdateWindowsLocked(boolean forceSend, 682 @NonNull List<WindowInfo> windows) { 683 if (forceSend) { 684 return true; 685 } 686 687 final int windowCount = windows.size(); 688 if (VERBOSE) { 689 Slogf.v(LOG_TAG, 690 "shouldUpdateWindowsLocked(): mDisplayId=%d, windowCount=%d, " 691 + "mCachedWindowInfos.size()=%d, windows.size()=%d", mDisplayId, 692 windowCount, mCachedWindowInfos.size(), windows.size()); 693 } 694 // We computed the windows and if they changed notify the client. 695 if (mCachedWindowInfos.size() != windowCount) { 696 // Different size means something changed. 697 return true; 698 } else if (!mCachedWindowInfos.isEmpty() || !windows.isEmpty()) { 699 // Since we always traverse windows from high to low layer 700 // the old and new windows at the same index should be the 701 // same, otherwise something changed. 702 for (int i = 0; i < windowCount; i++) { 703 WindowInfo oldWindow = mCachedWindowInfos.get(i); 704 WindowInfo newWindow = windows.get(i); 705 // We do not care for layer changes given the window 706 // order does not change. This brings no new information 707 // to the clients. 708 if (windowChangedNoLayer(oldWindow, newWindow)) { 709 return true; 710 } 711 } 712 } 713 714 return false; 715 } 716 cacheWindows(List<WindowInfo> windows)717 private void cacheWindows(List<WindowInfo> windows) { 718 final int oldWindowCount = mCachedWindowInfos.size(); 719 for (int i = oldWindowCount - 1; i >= 0; i--) { 720 mCachedWindowInfos.remove(i).recycle(); 721 } 722 final int newWindowCount = windows.size(); 723 for (int i = 0; i < newWindowCount; i++) { 724 WindowInfo newWindow = windows.get(i); 725 mCachedWindowInfos.add(WindowInfo.obtain(newWindow)); 726 } 727 } 728 windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow)729 private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) { 730 if (oldWindow == newWindow) { 731 return false; 732 } 733 if (oldWindow == null) { 734 return true; 735 } 736 if (newWindow == null) { 737 return true; 738 } 739 if (oldWindow.type != newWindow.type) { 740 return true; 741 } 742 if (oldWindow.focused != newWindow.focused) { 743 return true; 744 } 745 if (oldWindow.token == null) { 746 if (newWindow.token != null) { 747 return true; 748 } 749 } else if (!oldWindow.token.equals(newWindow.token)) { 750 return true; 751 } 752 if (oldWindow.parentToken == null) { 753 if (newWindow.parentToken != null) { 754 return true; 755 } 756 } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) { 757 return true; 758 } 759 if (oldWindow.activityToken == null) { 760 if (newWindow.activityToken != null) { 761 return true; 762 } 763 } else if (!oldWindow.activityToken.equals(newWindow.activityToken)) { 764 return true; 765 } 766 if (!oldWindow.regionInScreen.equals(newWindow.regionInScreen)) { 767 return true; 768 } 769 if (oldWindow.childTokens != null && newWindow.childTokens != null 770 && !oldWindow.childTokens.equals(newWindow.childTokens)) { 771 return true; 772 } 773 if (!TextUtils.equals(oldWindow.title, newWindow.title)) { 774 return true; 775 } 776 if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) { 777 return true; 778 } 779 if (oldWindow.inPictureInPicture != newWindow.inPictureInPicture) { 780 return true; 781 } 782 if (oldWindow.hasFlagWatchOutsideTouch != newWindow.hasFlagWatchOutsideTouch) { 783 return true; 784 } 785 if (oldWindow.displayId != newWindow.displayId) { 786 return true; 787 } 788 if (oldWindow.taskId != newWindow.taskId) { 789 return true; 790 } 791 if (!Arrays.equals(oldWindow.mTransformMatrix, newWindow.mTransformMatrix)) { 792 return true; 793 } 794 return false; 795 } 796 797 /** 798 * Clears all {@link AccessibilityWindowInfo}s and {@link WindowInfo}s. 799 */ clearWindowsLocked()800 private void clearWindowsLocked() { 801 final List<WindowInfo> windows = Collections.emptyList(); 802 final int activeWindowId = mActiveWindowId; 803 // UserId is useless in updateWindowsLocked, when we update a empty window list. 804 // Just pass current userId here. 805 updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(), windows); 806 // Do not reset mActiveWindowId here. mActiveWindowId will be clear after accessibility 807 // interaction connection removed. 808 mActiveWindowId = activeWindowId; 809 mWindows = null; 810 } 811 812 /** 813 * Updates windows info according to specified userId and windows. 814 * 815 * @param userId The userId to update 816 * @param windows The windows to update 817 */ updateWindowsLocked(int userId, @NonNull List<WindowInfo> windows)818 private void updateWindowsLocked(int userId, @NonNull List<WindowInfo> windows) { 819 if (mWindows == null) { 820 mWindows = new ArrayList<>(); 821 } 822 823 final List<AccessibilityWindowInfo> oldWindowList = new ArrayList<>(mWindows); 824 final SparseArray<AccessibilityWindowInfo> oldWindowsById = mA11yWindowInfoById.clone(); 825 boolean shouldClearAccessibilityFocus = false; 826 827 mWindows.clear(); 828 mA11yWindowInfoById.clear(); 829 830 for (int i = 0; i < mWindowInfoById.size(); i++) { 831 mWindowInfoById.valueAt(i).recycle(); 832 } 833 mWindowInfoById.clear(); 834 mHasWatchOutsideTouchWindow = false; 835 836 final int windowCount = windows.size(); 837 final boolean isTopFocusedDisplay = mDisplayId == mTopFocusedDisplayId; 838 // A proxy with an a11y-focused window is a11y-focused should use the proxy focus id. 839 final boolean isAccessibilityFocusedDisplay = 840 mDisplayId == mAccessibilityFocusedDisplayId 841 || (mIsProxy && mProxyDisplayAccessibilityFocusedWindow 842 != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID); 843 // Modifies the value of top focused window, active window and a11y focused window 844 // only if this display is top focused display which has the top focused window. 845 if (isTopFocusedDisplay) { 846 if (windowCount > 0) { 847 // Sets the top focus window by top focused window token. 848 mTopFocusedWindowId = findWindowIdLocked(userId, mTopFocusedWindowToken); 849 } else { 850 // Resets the top focus window when stopping tracking window of this display. 851 mTopFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 852 } 853 // The active window doesn't need to be reset if the touch operation is progressing. 854 if (!mTouchInteractionInProgress) { 855 mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 856 } 857 } 858 859 // If the active window goes away while the user is touch exploring we 860 // reset the active window id and wait for the next hover event from 861 // under the user's finger to determine which one is the new one. It 862 // is possible that the finger is not moving and the input system 863 // filters out such events. 864 boolean activeWindowGone = true; 865 866 // We'll clear accessibility focus if the window with focus is no longer visible to 867 // accessibility services. 868 int a11yFocusedWindowId = mIsProxy 869 ? mProxyDisplayAccessibilityFocusedWindow 870 : mAccessibilityFocusedWindowId; 871 if (isAccessibilityFocusedDisplay) { 872 shouldClearAccessibilityFocus = a11yFocusedWindowId 873 != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 874 } 875 876 boolean hasWindowIgnore = false; 877 if (windowCount > 0) { 878 for (int i = 0; i < windowCount; i++) { 879 final WindowInfo windowInfo = windows.get(i); 880 final AccessibilityWindowInfo window; 881 if (mTrackingWindows) { 882 window = populateReportedWindowLocked(userId, windowInfo, oldWindowsById); 883 if (window == null) { 884 hasWindowIgnore = true; 885 } 886 } else { 887 window = null; 888 } 889 if (window != null) { 890 891 // Flip layers in list to be consistent with AccessibilityService#getWindows 892 window.setLayer(windowCount - 1 - window.getLayer()); 893 894 final int windowId = window.getId(); 895 if (window.isFocused() && isTopFocusedDisplay) { 896 if (!mTouchInteractionInProgress) { 897 // This display is top one, and sets the focus window 898 // as active window. 899 mActiveWindowId = windowId; 900 window.setActive(true); 901 } else if (windowId == mActiveWindowId) { 902 activeWindowGone = false; 903 } 904 } 905 if (!mHasWatchOutsideTouchWindow && windowInfo.hasFlagWatchOutsideTouch) { 906 mHasWatchOutsideTouchWindow = true; 907 } 908 mWindows.add(window); 909 mA11yWindowInfoById.put(windowId, window); 910 mWindowInfoById.put(windowId, WindowInfo.obtain(windowInfo)); 911 } 912 } 913 final int accessibilityWindowCount = mWindows.size(); 914 // Re-order the window layer of all windows in the windows list because there's 915 // window not been added into the windows list. 916 if (hasWindowIgnore) { 917 for (int i = 0; i < accessibilityWindowCount; i++) { 918 mWindows.get(i).setLayer(accessibilityWindowCount - 1 - i); 919 } 920 } 921 if (isTopFocusedDisplay) { 922 if (mTouchInteractionInProgress && activeWindowGone) { 923 mActiveWindowId = mTopFocusedWindowId; 924 } 925 // Focused window may change the active one, so set the 926 // active window once we decided which it is. 927 for (int i = 0; i < accessibilityWindowCount; i++) { 928 final AccessibilityWindowInfo window = mWindows.get(i); 929 if (window.getId() == mActiveWindowId) { 930 window.setActive(true); 931 } 932 } 933 } 934 if (isAccessibilityFocusedDisplay) { 935 for (int i = 0; i < accessibilityWindowCount; i++) { 936 final AccessibilityWindowInfo window = mWindows.get(i); 937 if (window.getId() == a11yFocusedWindowId) { 938 window.setAccessibilityFocused(true); 939 shouldClearAccessibilityFocus = false; 940 break; 941 } 942 } 943 } 944 } 945 946 sendEventsForChangedWindowsLocked(oldWindowList, oldWindowsById); 947 948 final int oldWindowCount = oldWindowList.size(); 949 for (int i = oldWindowCount - 1; i >= 0; i--) { 950 oldWindowList.remove(i).recycle(); 951 } 952 953 if (shouldClearAccessibilityFocus) { 954 clearAccessibilityFocusLocked(a11yFocusedWindowId); 955 } 956 } 957 sendEventsForChangedWindowsLocked(List<AccessibilityWindowInfo> oldWindows, SparseArray<AccessibilityWindowInfo> oldWindowsById)958 private void sendEventsForChangedWindowsLocked(List<AccessibilityWindowInfo> oldWindows, 959 SparseArray<AccessibilityWindowInfo> oldWindowsById) { 960 List<AccessibilityEvent> events = new ArrayList<>(); 961 // Sends events for all removed windows. 962 final int oldWindowsCount = oldWindows.size(); 963 for (int i = 0; i < oldWindowsCount; i++) { 964 final AccessibilityWindowInfo window = oldWindows.get(i); 965 if (mA11yWindowInfoById.get(window.getId()) == null) { 966 events.add(AccessibilityEvent.obtainWindowsChangedEvent( 967 mDisplayId, window.getId(), AccessibilityEvent.WINDOWS_CHANGE_REMOVED)); 968 } 969 } 970 971 // Looks for other changes. 972 final int newWindowCount = mWindows.size(); 973 for (int i = 0; i < newWindowCount; i++) { 974 final AccessibilityWindowInfo newWindow = mWindows.get(i); 975 final AccessibilityWindowInfo oldWindow = oldWindowsById.get(newWindow.getId()); 976 if (oldWindow == null) { 977 events.add(AccessibilityEvent.obtainWindowsChangedEvent(mDisplayId, 978 newWindow.getId(), AccessibilityEvent.WINDOWS_CHANGE_ADDED)); 979 } else { 980 int changes = newWindow.differenceFrom(oldWindow); 981 if (changes != 0) { 982 events.add(AccessibilityEvent.obtainWindowsChangedEvent( 983 mDisplayId, newWindow.getId(), changes)); 984 } 985 } 986 } 987 988 final int numEvents = events.size(); 989 for (int i = 0; i < numEvents; i++) { 990 mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(events.get(i)); 991 } 992 } 993 populateReportedWindowLocked(int userId, WindowInfo window, SparseArray<AccessibilityWindowInfo> oldWindowsById)994 private AccessibilityWindowInfo populateReportedWindowLocked(int userId, 995 WindowInfo window, SparseArray<AccessibilityWindowInfo> oldWindowsById) { 996 final int windowId = findWindowIdLocked(userId, window.token); 997 998 // With the flag enabled, createWindowInfoListLocked() already removes invalid windows. 999 if (!Flags.computeWindowChangesOnA11yV2()) { 1000 if (windowId < 0) { 1001 return null; 1002 } 1003 1004 // Don't need to add the embedded hierarchy windows into the a11y windows list. 1005 if (isEmbeddedHierarchyWindowsLocked(windowId)) { 1006 return null; 1007 } 1008 } 1009 1010 final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain(); 1011 1012 reportedWindow.setId(windowId); 1013 reportedWindow.setType(getTypeForWindowManagerWindowType(window.type)); 1014 reportedWindow.setLayer(window.layer); 1015 reportedWindow.setFocused(window.focused); 1016 reportedWindow.setRegionInScreen(window.regionInScreen); 1017 reportedWindow.setTitle(window.title); 1018 reportedWindow.setAnchorId(window.accessibilityIdOfAnchor); 1019 reportedWindow.setPictureInPicture(window.inPictureInPicture); 1020 reportedWindow.setDisplayId(window.displayId); 1021 reportedWindow.setTaskId(window.taskId); 1022 reportedWindow.setLocales(window.locales); 1023 1024 final int parentId = findWindowIdLocked(userId, window.parentToken); 1025 if (parentId >= 0) { 1026 reportedWindow.setParentId(parentId); 1027 } 1028 1029 if (window.childTokens != null) { 1030 final int childCount = window.childTokens.size(); 1031 for (int i = 0; i < childCount; i++) { 1032 final IBinder childToken = window.childTokens.get(i); 1033 final int childId = findWindowIdLocked(userId, childToken); 1034 if (childId >= 0) { 1035 reportedWindow.addChild(childId); 1036 } 1037 } 1038 } 1039 1040 final AccessibilityWindowInfo oldWindowInfo = oldWindowsById.get(windowId); 1041 if (oldWindowInfo == null) { 1042 reportedWindow.setTransitionTimeMillis(SystemClock.uptimeMillis()); 1043 } else { 1044 final Region oldTouchRegion = new Region(); 1045 oldWindowInfo.getRegionInScreen(oldTouchRegion); 1046 if (oldTouchRegion.equals(window.regionInScreen)) { 1047 reportedWindow.setTransitionTimeMillis(oldWindowInfo.getTransitionTimeMillis()); 1048 } else { 1049 reportedWindow.setTransitionTimeMillis(SystemClock.uptimeMillis()); 1050 } 1051 } 1052 return reportedWindow; 1053 } 1054 getTypeForWindowManagerWindowType(int windowType)1055 private int getTypeForWindowManagerWindowType(int windowType) { 1056 switch (windowType) { 1057 case WindowManager.LayoutParams.TYPE_APPLICATION: 1058 case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: 1059 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: 1060 case WindowManager.LayoutParams.TYPE_APPLICATION_STARTING: 1061 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: 1062 case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL: 1063 case WindowManager.LayoutParams.TYPE_BASE_APPLICATION: 1064 case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION: 1065 case WindowManager.LayoutParams.TYPE_PHONE: 1066 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: 1067 case WindowManager.LayoutParams.TYPE_TOAST: 1068 case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: 1069 case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG: { 1070 return AccessibilityWindowInfo.TYPE_APPLICATION; 1071 } 1072 1073 case WindowManager.LayoutParams.TYPE_INPUT_METHOD: { 1074 return AccessibilityWindowInfo.TYPE_INPUT_METHOD; 1075 } 1076 1077 case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: 1078 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR: 1079 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: 1080 case WindowManager.LayoutParams.TYPE_SEARCH_BAR: 1081 case WindowManager.LayoutParams.TYPE_STATUS_BAR: 1082 case WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE: 1083 case WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL: 1084 case WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL: 1085 case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: 1086 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: 1087 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: 1088 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: 1089 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: 1090 case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY: 1091 case WindowManager.LayoutParams.TYPE_SCREENSHOT: { 1092 return AccessibilityWindowInfo.TYPE_SYSTEM; 1093 } 1094 1095 case WindowManager.LayoutParams.TYPE_DOCK_DIVIDER: { 1096 return AccessibilityWindowInfo.TYPE_SPLIT_SCREEN_DIVIDER; 1097 } 1098 1099 case TYPE_ACCESSIBILITY_OVERLAY: { 1100 return AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY; 1101 } 1102 1103 case WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY: { 1104 return AccessibilityWindowInfo.TYPE_MAGNIFICATION_OVERLAY; 1105 } 1106 1107 default: { 1108 return -1; 1109 } 1110 } 1111 } 1112 1113 /** 1114 * Dumps all {@link AccessibilityWindowInfo}s here. 1115 */ dumpLocked(FileDescriptor fd, final PrintWriter pw, String[] args)1116 void dumpLocked(FileDescriptor fd, final PrintWriter pw, String[] args) { 1117 if (mIsProxy) { 1118 pw.println("Proxy accessibility focused window = " 1119 + mProxyDisplayAccessibilityFocusedWindow); 1120 pw.println(); 1121 } 1122 if (mWindows != null) { 1123 final int windowCount = mWindows.size(); 1124 for (int j = 0; j < windowCount; j++) { 1125 if (j == 0) { 1126 pw.append("Display["); 1127 pw.append(Integer.toString(mDisplayId)); 1128 pw.append("] : "); 1129 pw.println(); 1130 } 1131 if (j > 0) { 1132 pw.append(','); 1133 pw.println(); 1134 } 1135 pw.append("A11yWindow["); 1136 AccessibilityWindowInfo window = mWindows.get(j); 1137 pw.append(window.toString()); 1138 pw.append(']'); 1139 pw.println(); 1140 final WindowInfo windowInfo = findWindowInfoByIdLocked(window.getId()); 1141 if (windowInfo != null) { 1142 pw.append("WindowInfo["); 1143 pw.append(windowInfo.toString()); 1144 pw.append("]"); 1145 pw.println(); 1146 } 1147 1148 } 1149 pw.println(); 1150 } 1151 } 1152 1153 } 1154 /** 1155 * Interface to send {@link AccessibilityEvent}. 1156 */ 1157 public interface AccessibilityEventSender { 1158 /** 1159 * Sends {@link AccessibilityEvent} for current user. 1160 */ sendAccessibilityEventForCurrentUserLocked(AccessibilityEvent event)1161 void sendAccessibilityEventForCurrentUserLocked(AccessibilityEvent event); 1162 } 1163 1164 /** 1165 * Wrapper of accessibility interaction connection for window. 1166 */ 1167 // In order to avoid using DexmakerShareClassLoaderRule, make this class visible for testing. 1168 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 1169 public final class RemoteAccessibilityConnection implements IBinder.DeathRecipient { 1170 private final int mUid; 1171 private final String mPackageName; 1172 private final int mWindowId; 1173 private final int mUserId; 1174 private final IAccessibilityInteractionConnection mConnection; 1175 RemoteAccessibilityConnection(int windowId, IAccessibilityInteractionConnection connection, String packageName, int uid, int userId)1176 RemoteAccessibilityConnection(int windowId, 1177 IAccessibilityInteractionConnection connection, 1178 String packageName, int uid, int userId) { 1179 mWindowId = windowId; 1180 mPackageName = packageName; 1181 mUid = uid; 1182 mUserId = userId; 1183 mConnection = connection; 1184 } 1185 getUid()1186 int getUid() { 1187 return mUid; 1188 } 1189 getPackageName()1190 String getPackageName() { 1191 return mPackageName; 1192 } 1193 getRemote()1194 IAccessibilityInteractionConnection getRemote() { 1195 return mConnection; 1196 } 1197 linkToDeath()1198 void linkToDeath() throws RemoteException { 1199 mConnection.asBinder().linkToDeath(this, 0); 1200 } 1201 unlinkToDeath()1202 void unlinkToDeath() { 1203 mConnection.asBinder().unlinkToDeath(this, 0); 1204 } 1205 1206 @Override binderDied()1207 public void binderDied() { 1208 unlinkToDeath(); 1209 synchronized (mLock) { 1210 removeAccessibilityInteractionConnectionLocked(mWindowId, mUserId); 1211 } 1212 } 1213 } 1214 1215 /** 1216 * Constructor for AccessibilityWindowManager. 1217 */ AccessibilityWindowManager(@onNull Object lock, @NonNull Handler handler, @NonNull WindowManagerInternal windowManagerInternal, @NonNull AccessibilityEventSender accessibilityEventSender, @NonNull AccessibilitySecurityPolicy securityPolicy, @NonNull AccessibilityUserManager accessibilityUserManager, @NonNull AccessibilityTraceManager traceManager)1218 public AccessibilityWindowManager(@NonNull Object lock, @NonNull Handler handler, 1219 @NonNull WindowManagerInternal windowManagerInternal, 1220 @NonNull AccessibilityEventSender accessibilityEventSender, 1221 @NonNull AccessibilitySecurityPolicy securityPolicy, 1222 @NonNull AccessibilityUserManager accessibilityUserManager, 1223 @NonNull AccessibilityTraceManager traceManager) { 1224 mLock = lock; 1225 mHandler = handler; 1226 mWindowManagerInternal = windowManagerInternal; 1227 mAccessibilityEventSender = accessibilityEventSender; 1228 mSecurityPolicy = securityPolicy; 1229 mAccessibilityUserManager = accessibilityUserManager; 1230 mTraceManager = traceManager; 1231 } 1232 1233 /** 1234 * Starts tracking windows changes from window manager for specified display. 1235 * 1236 * @param displayId The logical display id. 1237 */ startTrackingWindows(int displayId, boolean proxyed)1238 public void startTrackingWindows(int displayId, boolean proxyed) { 1239 synchronized (mLock) { 1240 DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId); 1241 if (observer == null) { 1242 observer = new DisplayWindowsObserver(displayId); 1243 } 1244 if (proxyed && !observer.mIsProxy) { 1245 observer.mIsProxy = true; 1246 mHasProxy = true; 1247 } 1248 if (observer.isTrackingWindowsLocked()) { 1249 return; 1250 } 1251 observer.startTrackingWindowsLocked(); 1252 mDisplayWindowsObservers.put(displayId, observer); 1253 } 1254 } 1255 1256 /** 1257 * Stops tracking windows changes from window manager, and clear all windows info for specified 1258 * display. 1259 * 1260 * @param displayId The logical display id. 1261 */ stopTrackingWindows(int displayId)1262 public void stopTrackingWindows(int displayId) { 1263 synchronized (mLock) { 1264 final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId); 1265 if (observer != null) { 1266 observer.stopTrackingWindowsLocked(); 1267 mDisplayWindowsObservers.remove(displayId); 1268 } 1269 resetHasProxyIfNeededLocked(); 1270 } 1271 } 1272 1273 /** 1274 * Stops tracking a display as belonging to a proxy. 1275 * @param displayId 1276 */ stopTrackingDisplayProxy(int displayId)1277 public void stopTrackingDisplayProxy(int displayId) { 1278 synchronized (mLock) { 1279 final DisplayWindowsObserver proxyObserver = mDisplayWindowsObservers.get(displayId); 1280 if (proxyObserver != null) { 1281 proxyObserver.mIsProxy = false; 1282 } 1283 resetHasProxyIfNeededLocked(); 1284 } 1285 } 1286 resetHasProxyIfNeededLocked()1287 private void resetHasProxyIfNeededLocked() { 1288 boolean hasProxy = false; 1289 final int count = mDisplayWindowsObservers.size(); 1290 for (int i = 0; i < count; i++) { 1291 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); 1292 if (observer != null) { 1293 if (observer.mIsProxy) { 1294 hasProxy = true; 1295 } 1296 } 1297 } 1298 mHasProxy = hasProxy; 1299 } 1300 1301 /** 1302 * Checks if we are tracking windows on any display. 1303 * 1304 * @return {@code true} if the observer is tracking windows on any display, 1305 * {@code false} otherwise. 1306 */ isTrackingWindowsLocked()1307 public boolean isTrackingWindowsLocked() { 1308 final int count = mDisplayWindowsObservers.size(); 1309 if (count > 0) { 1310 return true; 1311 } 1312 return false; 1313 } 1314 isProxyed(int displayId)1315 private boolean isProxyed(int displayId) { 1316 final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId); 1317 return (observer != null && observer.mIsProxy); 1318 } 1319 moveNonProxyTopFocusedDisplayToTopIfNeeded()1320 void moveNonProxyTopFocusedDisplayToTopIfNeeded() { 1321 if (mHasProxy 1322 && (mLastNonProxyTopFocusedDisplayId != mTopFocusedDisplayId)) { 1323 mWindowManagerInternal.moveDisplayToTopIfAllowed(mLastNonProxyTopFocusedDisplayId); 1324 } 1325 } getLastNonProxyTopFocusedDisplayId()1326 int getLastNonProxyTopFocusedDisplayId() { 1327 return mLastNonProxyTopFocusedDisplayId; 1328 } 1329 1330 /** 1331 * Checks if we are tracking windows on specified display. 1332 * 1333 * @param displayId The logical display id. 1334 * @return {@code true} if the observer is tracking windows on specified display, 1335 * {@code false} otherwise. 1336 */ isTrackingWindowsLocked(int displayId)1337 public boolean isTrackingWindowsLocked(int displayId) { 1338 final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId); 1339 if (observer != null) { 1340 return observer.isTrackingWindowsLocked(); 1341 } 1342 return false; 1343 } 1344 1345 /** 1346 * Returns accessibility windows for specified display. 1347 * 1348 * @param displayId The logical display id. 1349 * @return accessibility windows for specified display. 1350 */ 1351 @Nullable getWindowListLocked(int displayId)1352 public List<AccessibilityWindowInfo> getWindowListLocked(int displayId) { 1353 final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId); 1354 if (observer != null) { 1355 return observer.getWindowListLocked(); 1356 } 1357 return null; 1358 } 1359 1360 /** 1361 * Adds accessibility interaction connection according to given window token, package name and 1362 * window token. 1363 * 1364 * @param window The window token of accessibility interaction connection 1365 * @param leashToken The leash token of accessibility interaction connection 1366 * @param connection The accessibility interaction connection 1367 * @param packageName The package name 1368 * @param userId The userId 1369 * @return The windowId of added connection 1370 * @throws RemoteException 1371 */ addAccessibilityInteractionConnection(@onNull IWindow window, @NonNull IBinder leashToken, @NonNull IAccessibilityInteractionConnection connection, @NonNull String packageName, int userId)1372 public int addAccessibilityInteractionConnection(@NonNull IWindow window, 1373 @NonNull IBinder leashToken, @NonNull IAccessibilityInteractionConnection connection, 1374 @NonNull String packageName, int userId) throws RemoteException { 1375 final int windowId; 1376 boolean shouldComputeWindows = false; 1377 final IBinder token = window.asBinder(); 1378 if (traceWMEnabled()) { 1379 logTraceWM("getDisplayIdForWindow", "token=" + token); 1380 } 1381 final int displayId = mWindowManagerInternal.getDisplayIdForWindow(token); 1382 synchronized (mLock) { 1383 // We treat calls from a profile as if made by its parent as profiles 1384 // share the accessibility state of the parent. The call below 1385 // performs the current profile parent resolution. 1386 final int resolvedUserId = mSecurityPolicy 1387 .resolveCallingUserIdEnforcingPermissionsLocked(userId); 1388 final int resolvedUid = UserHandle.getUid(resolvedUserId, UserHandle.getCallingAppId()); 1389 1390 // Makes sure the reported package is one the caller has access to. 1391 packageName = mSecurityPolicy.resolveValidReportedPackageLocked( 1392 packageName, UserHandle.getCallingAppId(), resolvedUserId, 1393 Binder.getCallingPid()); 1394 1395 windowId = sNextWindowId++; 1396 // If the window is from a process that runs across users such as 1397 // the system UI or the system we add it to the global state that 1398 // is shared across users. 1399 if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) { 1400 RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection( 1401 windowId, connection, packageName, resolvedUid, UserHandle.USER_ALL); 1402 wrapper.linkToDeath(); 1403 mGlobalInteractionConnections.put(windowId, wrapper); 1404 mGlobalWindowTokens.put(windowId, token); 1405 if (DEBUG) { 1406 Slog.i(LOG_TAG, "Added global connection for pid:" + Binder.getCallingPid() 1407 + " with windowId: " + windowId + " and token: " + token); 1408 } 1409 } else { 1410 RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection( 1411 windowId, connection, packageName, resolvedUid, resolvedUserId); 1412 wrapper.linkToDeath(); 1413 getInteractionConnectionsForUserLocked(resolvedUserId).put(windowId, wrapper); 1414 getWindowTokensForUserLocked(resolvedUserId).put(windowId, token); 1415 if (DEBUG) { 1416 Slog.i(LOG_TAG, "Added user connection for pid:" + Binder.getCallingPid() 1417 + " with windowId: " + windowId + " and token: " + token); 1418 } 1419 } 1420 1421 if (isTrackingWindowsLocked(displayId)) { 1422 shouldComputeWindows = true; 1423 } 1424 registerIdLocked(leashToken, windowId); 1425 } 1426 if (shouldComputeWindows) { 1427 if (traceWMEnabled()) { 1428 logTraceWM("computeWindowsForAccessibility", "displayId=" + displayId); 1429 } 1430 mWindowManagerInternal.computeWindowsForAccessibility(displayId); 1431 } 1432 if (traceWMEnabled()) { 1433 logTraceWM("setAccessibilityIdToSurfaceMetadata", 1434 "token=" + token + ";windowId=" + windowId); 1435 } 1436 mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(token, windowId); 1437 return windowId; 1438 } 1439 1440 /** 1441 * Removes accessibility interaction connection according to given window token. 1442 * 1443 * @param window The window token of accessibility interaction connection 1444 */ removeAccessibilityInteractionConnection(@onNull IWindow window)1445 public void removeAccessibilityInteractionConnection(@NonNull IWindow window) { 1446 synchronized (mLock) { 1447 // We treat calls from a profile as if made by its parent as profiles 1448 // share the accessibility state of the parent. The call below 1449 // performs the current profile parent resolution. 1450 mSecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked( 1451 UserHandle.getCallingUserId()); 1452 IBinder token = window.asBinder(); 1453 final int removedWindowId = removeAccessibilityInteractionConnectionInternalLocked( 1454 token, mGlobalWindowTokens, mGlobalInteractionConnections); 1455 if (removedWindowId >= 0) { 1456 onAccessibilityInteractionConnectionRemovedLocked(removedWindowId, token); 1457 if (DEBUG) { 1458 Slog.i(LOG_TAG, "Removed global connection for pid:" + Binder.getCallingPid() 1459 + " with windowId: " + removedWindowId + " and token: " 1460 + window.asBinder()); 1461 } 1462 return; 1463 } 1464 final int userCount = mWindowTokens.size(); 1465 for (int i = 0; i < userCount; i++) { 1466 final int userId = mWindowTokens.keyAt(i); 1467 final int removedWindowIdForUser = 1468 removeAccessibilityInteractionConnectionInternalLocked(token, 1469 getWindowTokensForUserLocked(userId), 1470 getInteractionConnectionsForUserLocked(userId)); 1471 if (removedWindowIdForUser >= 0) { 1472 onAccessibilityInteractionConnectionRemovedLocked( 1473 removedWindowIdForUser, token); 1474 if (DEBUG) { 1475 Slog.i(LOG_TAG, "Removed user connection for pid:" + Binder.getCallingPid() 1476 + " with windowId: " + removedWindowIdForUser + " and userId:" 1477 + userId + " and token: " + window.asBinder()); 1478 } 1479 return; 1480 } 1481 } 1482 } 1483 } 1484 1485 /** 1486 * Resolves a connection wrapper for a window id. 1487 * 1488 * @param userId The user id for any user-specific windows 1489 * @param windowId The id of the window of interest 1490 * 1491 * @return a connection to the window 1492 */ 1493 @Nullable getConnectionLocked(int userId, int windowId)1494 public RemoteAccessibilityConnection getConnectionLocked(int userId, int windowId) { 1495 if (VERBOSE) { 1496 Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId); 1497 } 1498 RemoteAccessibilityConnection connection = mGlobalInteractionConnections.get(windowId); 1499 if (connection == null && isValidUserForInteractionConnectionsLocked(userId)) { 1500 connection = getInteractionConnectionsForUserLocked(userId).get(windowId); 1501 } 1502 if (connection != null && connection.getRemote() != null) { 1503 return connection; 1504 } 1505 if (DEBUG) { 1506 Slog.e(LOG_TAG, "No interaction connection to window: " + windowId); 1507 } 1508 return null; 1509 } 1510 removeAccessibilityInteractionConnectionInternalLocked(IBinder windowToken, SparseArray<IBinder> windowTokens, SparseArray<RemoteAccessibilityConnection> interactionConnections)1511 private int removeAccessibilityInteractionConnectionInternalLocked(IBinder windowToken, 1512 SparseArray<IBinder> windowTokens, SparseArray<RemoteAccessibilityConnection> 1513 interactionConnections) { 1514 final int count = windowTokens.size(); 1515 for (int i = 0; i < count; i++) { 1516 if (windowTokens.valueAt(i) == windowToken) { 1517 final int windowId = windowTokens.keyAt(i); 1518 windowTokens.removeAt(i); 1519 RemoteAccessibilityConnection wrapper = interactionConnections.get(windowId); 1520 wrapper.unlinkToDeath(); 1521 interactionConnections.remove(windowId); 1522 return windowId; 1523 } 1524 } 1525 return -1; 1526 } 1527 1528 /** 1529 * Removes accessibility interaction connection according to given windowId and userId. 1530 * 1531 * @param windowId The windowId of accessibility interaction connection 1532 * @param userId The userId to remove 1533 */ removeAccessibilityInteractionConnectionLocked(int windowId, int userId)1534 private void removeAccessibilityInteractionConnectionLocked(int windowId, int userId) { 1535 IBinder window = null; 1536 if (userId == UserHandle.USER_ALL) { 1537 window = mGlobalWindowTokens.get(windowId); 1538 mGlobalWindowTokens.remove(windowId); 1539 mGlobalInteractionConnections.remove(windowId); 1540 } else { 1541 if (isValidUserForWindowTokensLocked(userId)) { 1542 window = getWindowTokensForUserLocked(userId).get(windowId); 1543 getWindowTokensForUserLocked(userId).remove(windowId); 1544 } 1545 if (isValidUserForInteractionConnectionsLocked(userId)) { 1546 getInteractionConnectionsForUserLocked(userId).remove(windowId); 1547 } 1548 } 1549 onAccessibilityInteractionConnectionRemovedLocked(windowId, window); 1550 if (DEBUG) { 1551 Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId); 1552 } 1553 } 1554 1555 /** 1556 * Invoked when accessibility interaction connection of window is removed. 1557 * 1558 * @param windowId Removed windowId 1559 * @param binder Removed window token 1560 */ onAccessibilityInteractionConnectionRemovedLocked( int windowId, @Nullable IBinder binder)1561 private void onAccessibilityInteractionConnectionRemovedLocked( 1562 int windowId, @Nullable IBinder binder) { 1563 // Active window will not update, if windows callback is unregistered. 1564 // Update active window to invalid, when its a11y interaction connection is removed. 1565 if (!isTrackingWindowsLocked() && windowId >= 0 && mActiveWindowId == windowId) { 1566 mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 1567 } 1568 if (binder != null) { 1569 if (traceWMEnabled()) { 1570 logTraceWM("setAccessibilityIdToSurfaceMetadata", "token=" + binder 1571 + ";windowId=AccessibilityWindowInfo.UNDEFINED_WINDOW_ID"); 1572 } 1573 mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata( 1574 binder, AccessibilityWindowInfo.UNDEFINED_WINDOW_ID); 1575 } 1576 unregisterIdLocked(windowId); 1577 mWindowAttributes.remove(windowId); 1578 } 1579 1580 /** 1581 * Gets window token according to given userId and windowId. 1582 * 1583 * @param userId The userId 1584 * @param windowId The windowId 1585 * @return The window token 1586 */ 1587 @Nullable getWindowTokenForUserAndWindowIdLocked(int userId, int windowId)1588 public IBinder getWindowTokenForUserAndWindowIdLocked(int userId, int windowId) { 1589 IBinder windowToken = mGlobalWindowTokens.get(windowId); 1590 if (windowToken == null && isValidUserForWindowTokensLocked(userId)) { 1591 windowToken = getWindowTokensForUserLocked(userId).get(windowId); 1592 } 1593 return windowToken; 1594 } 1595 1596 /** 1597 * Returns the userId that owns the given window token, {@link UserHandle#USER_NULL} 1598 * if not found. 1599 * 1600 * @param windowToken The window token 1601 * @return The userId 1602 */ getWindowOwnerUserId(@onNull IBinder windowToken)1603 public int getWindowOwnerUserId(@NonNull IBinder windowToken) { 1604 if (traceWMEnabled()) { 1605 logTraceWM("getWindowOwnerUserId", "token=" + windowToken); 1606 } 1607 return mWindowManagerInternal.getWindowOwnerUserId(windowToken); 1608 } 1609 1610 /** 1611 * Returns windowId of given userId and window token. 1612 * 1613 * @param userId The userId 1614 * @param token The window token 1615 * @return The windowId 1616 */ findWindowIdLocked(int userId, @NonNull IBinder token)1617 public int findWindowIdLocked(int userId, @NonNull IBinder token) { 1618 final int globalIndex = mGlobalWindowTokens.indexOfValue(token); 1619 if (globalIndex >= 0) { 1620 return mGlobalWindowTokens.keyAt(globalIndex); 1621 } 1622 if (isValidUserForWindowTokensLocked(userId)) { 1623 final int userIndex = getWindowTokensForUserLocked(userId).indexOfValue(token); 1624 if (userIndex >= 0) { 1625 return getWindowTokensForUserLocked(userId).keyAt(userIndex); 1626 } 1627 } 1628 return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 1629 } 1630 1631 /** 1632 * Establish the relationship between the host and the embedded view hierarchy. 1633 * 1634 * @param host The token of host hierarchy 1635 * @param embedded The token of the embedded hierarchy 1636 */ associateEmbeddedHierarchyLocked(@onNull IBinder host, @NonNull IBinder embedded)1637 public void associateEmbeddedHierarchyLocked(@NonNull IBinder host, @NonNull IBinder embedded) { 1638 // Use embedded window as key, since one host window may have multiple embedded windows. 1639 associateLocked(embedded, host); 1640 } 1641 1642 /** 1643 * Clear the relationship by given token. 1644 * 1645 * @param token The token 1646 */ disassociateEmbeddedHierarchyLocked(@onNull IBinder token)1647 public void disassociateEmbeddedHierarchyLocked(@NonNull IBinder token) { 1648 disassociateLocked(token); 1649 } 1650 1651 /** 1652 * Gets the parent windowId of the window according to the specified windowId. 1653 * 1654 * @param windowId The windowId to check 1655 * @return The windowId of the parent window, or self if no parent exists 1656 */ resolveParentWindowIdLocked(int windowId)1657 public int resolveParentWindowIdLocked(int windowId) { 1658 final IBinder token = getLeashTokenLocked(windowId); 1659 if (token == null) { 1660 return windowId; 1661 } 1662 final IBinder resolvedToken = resolveTopParentTokenLocked(token); 1663 final int resolvedWindowId = getWindowIdLocked(resolvedToken); 1664 return resolvedWindowId != -1 ? resolvedWindowId : windowId; 1665 } 1666 resolveTopParentTokenLocked(IBinder token)1667 private IBinder resolveTopParentTokenLocked(IBinder token) { 1668 final IBinder hostToken = getHostTokenLocked(token); 1669 if (hostToken == null) { 1670 return token; 1671 } 1672 return resolveTopParentTokenLocked(hostToken); 1673 } 1674 1675 /** 1676 * Computes partial interactive region of given windowId. 1677 * 1678 * @param windowId The windowId 1679 * @param outRegion The output to which to write the bounds. 1680 * @return true if outRegion is not empty. 1681 */ computePartialInteractiveRegionForWindowLocked(int windowId, @NonNull Region outRegion)1682 public boolean computePartialInteractiveRegionForWindowLocked(int windowId, 1683 @NonNull Region outRegion) { 1684 final int parentWindowId = resolveParentWindowIdLocked(windowId); 1685 final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked( 1686 parentWindowId); 1687 1688 if (observer != null) { 1689 return observer.computePartialInteractiveRegionForWindowLocked(parentWindowId, 1690 parentWindowId != windowId, outRegion); 1691 } 1692 1693 return false; 1694 } 1695 1696 /** 1697 * Updates active windowId and accessibility focused windowId according to given accessibility 1698 * event and action. 1699 * 1700 * @param userId The userId 1701 * @param windowId The windowId of accessibility event 1702 * @param nodeId The accessibility node id of accessibility event 1703 * @param eventType The accessibility event type 1704 * @param eventAction The accessibility event action 1705 */ updateActiveAndAccessibilityFocusedWindowLocked(int userId, int windowId, long nodeId, int eventType, int eventAction)1706 public void updateActiveAndAccessibilityFocusedWindowLocked(int userId, int windowId, 1707 long nodeId, int eventType, int eventAction) { 1708 // The active window is either the window that has input focus or 1709 // the window that the user is currently touching. If the user is 1710 // touching a window that does not have input focus as soon as the 1711 // the user stops touching that window the focused window becomes 1712 // the active one. Here we detect the touched window and make it 1713 // active. In updateWindowsLocked() we update the focused window 1714 // and if the user is not touching the screen, we make the focused 1715 // window the active one. 1716 switch (eventType) { 1717 case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: { 1718 // If no service has the capability to introspect screen, 1719 // we do not register callback in the window manager for 1720 // window changes, so we have to ask the window manager 1721 // what the focused window is to update the active one. 1722 // The active window also determined events from which 1723 // windows are delivered. 1724 synchronized (mLock) { 1725 if (!isTrackingWindowsLocked()) { 1726 mTopFocusedWindowId = findFocusedWindowId(userId); 1727 if (windowId == mTopFocusedWindowId) { 1728 mActiveWindowId = windowId; 1729 } 1730 } 1731 } 1732 } break; 1733 1734 case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: { 1735 // Do not allow delayed hover events to confuse us 1736 // which the active window is. 1737 synchronized (mLock) { 1738 if (mTouchInteractionInProgress && mActiveWindowId != windowId) { 1739 setActiveWindowLocked(windowId); 1740 } 1741 } 1742 } break; 1743 1744 case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { 1745 synchronized (mLock) { 1746 // If window id belongs to a proxy display, then find the display, update the 1747 // observer focus and send WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED events. 1748 if (mHasProxy && setProxyFocusLocked(windowId)) { 1749 return; 1750 } 1751 if (mAccessibilityFocusedWindowId != windowId) { 1752 clearAccessibilityFocusLocked(mAccessibilityFocusedWindowId); 1753 setAccessibilityFocusedWindowLocked(windowId); 1754 } 1755 mAccessibilityFocusNodeId = nodeId; 1756 } 1757 } break; 1758 1759 case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: { 1760 synchronized (mLock) { 1761 // If cleared happened on the proxy display, then clear the tracked focus. 1762 if (mHasProxy && clearProxyFocusLocked(windowId, eventAction)) { 1763 return; 1764 } 1765 if (mAccessibilityFocusNodeId == nodeId) { 1766 mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; 1767 } 1768 // Clear the window with focus if it no longer has focus and we aren't 1769 // just moving focus from one view to the other in the same window. 1770 if ((mAccessibilityFocusNodeId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) 1771 && (mAccessibilityFocusedWindowId == windowId) 1772 && (eventAction != AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS)) { 1773 mAccessibilityFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 1774 mAccessibilityFocusedDisplayId = Display.INVALID_DISPLAY; 1775 } 1776 } 1777 } break; 1778 } 1779 } 1780 1781 /** 1782 * Callbacks from AccessibilityManagerService when touch explorer turn on and 1783 * motion down detected. 1784 */ onTouchInteractionStart()1785 public void onTouchInteractionStart() { 1786 synchronized (mLock) { 1787 mTouchInteractionInProgress = true; 1788 } 1789 } 1790 1791 /** 1792 * Callbacks from AccessibilityManagerService when touch explorer turn on and 1793 * gesture or motion up detected. 1794 */ onTouchInteractionEnd()1795 public void onTouchInteractionEnd() { 1796 synchronized (mLock) { 1797 mTouchInteractionInProgress = false; 1798 // We want to set the active window to be current immediately 1799 // after the user has stopped touching the screen since if the 1800 // user types with the IME they should get a feedback for the 1801 // letter typed in the text view which is in the input focused 1802 // window. Note that we always deliver hover accessibility events 1803 // (they are a result of user touching the screen) so change of 1804 // the active window before all hover accessibility events from 1805 // the touched window are delivered is fine. 1806 final int oldActiveWindow = mActiveWindowId; 1807 setActiveWindowLocked(mTopFocusedWindowId); 1808 if (oldActiveWindow != mActiveWindowId 1809 && mAccessibilityFocusedWindowId == oldActiveWindow 1810 && accessibilityFocusOnlyInActiveWindowLocked()) { 1811 clearAccessibilityFocusLocked(oldActiveWindow); 1812 } 1813 } 1814 } 1815 1816 /** 1817 * Gets the id of the current active window. 1818 * 1819 * @return The userId 1820 */ getActiveWindowId(int userId)1821 public int getActiveWindowId(int userId) { 1822 if (mActiveWindowId == AccessibilityWindowInfo.UNDEFINED_WINDOW_ID 1823 && !mTouchInteractionInProgress) { 1824 mActiveWindowId = findFocusedWindowId(userId); 1825 } 1826 return mActiveWindowId; 1827 } 1828 setActiveWindowLocked(int windowId)1829 private void setActiveWindowLocked(int windowId) { 1830 if (mActiveWindowId != windowId) { 1831 List<AccessibilityEvent> events = new ArrayList<>(2); 1832 if (mActiveWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) { 1833 final DisplayWindowsObserver observer = 1834 getDisplayWindowObserverByWindowIdLocked(mActiveWindowId); 1835 if (observer != null) { 1836 events.add(AccessibilityEvent.obtainWindowsChangedEvent(observer.mDisplayId, 1837 mActiveWindowId, AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)); 1838 } 1839 } 1840 1841 mActiveWindowId = windowId; 1842 // Goes through all windows for each display. 1843 final int count = mDisplayWindowsObservers.size(); 1844 for (int i = 0; i < count; i++) { 1845 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); 1846 if (observer != null && observer.setActiveWindowLocked(windowId)) { 1847 events.add(AccessibilityEvent.obtainWindowsChangedEvent(observer.mDisplayId, 1848 windowId, AccessibilityEvent.WINDOWS_CHANGE_ACTIVE)); 1849 } 1850 } 1851 1852 for (final AccessibilityEvent event : events) { 1853 mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(event); 1854 } 1855 } 1856 } 1857 setAccessibilityFocusedWindowLocked(int windowId)1858 private void setAccessibilityFocusedWindowLocked(int windowId) { 1859 if (mAccessibilityFocusedWindowId != windowId) { 1860 List<AccessibilityEvent> events = new ArrayList<>(2); 1861 if (mAccessibilityFocusedDisplayId != Display.INVALID_DISPLAY 1862 && mAccessibilityFocusedWindowId 1863 != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) { 1864 // Previously focused window -> send a focused event for losing focus 1865 events.add(AccessibilityEvent.obtainWindowsChangedEvent( 1866 mAccessibilityFocusedDisplayId, mAccessibilityFocusedWindowId, 1867 WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)); 1868 } 1869 1870 mAccessibilityFocusedWindowId = windowId; 1871 // Goes through all windows for each display. 1872 final int count = mDisplayWindowsObservers.size(); 1873 for (int i = 0; i < count; i++) { 1874 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); 1875 if (observer != null && observer.setAccessibilityFocusedWindowLocked(windowId)) { 1876 mAccessibilityFocusedDisplayId = observer.mDisplayId; 1877 // Newly focused window -> send a focused event for gaining focus 1878 events.add(AccessibilityEvent.obtainWindowsChangedEvent(observer.mDisplayId, 1879 windowId, WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)); 1880 } 1881 } 1882 1883 for (final AccessibilityEvent event : events) { 1884 mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(event); 1885 } 1886 } 1887 } 1888 1889 /** 1890 * Returns accessibility window info according to given windowId. 1891 * 1892 * @param windowId The windowId 1893 * @return The accessibility window info 1894 */ 1895 @Nullable findA11yWindowInfoByIdLocked(int windowId)1896 public AccessibilityWindowInfo findA11yWindowInfoByIdLocked(int windowId) { 1897 windowId = resolveParentWindowIdLocked(windowId); 1898 final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId); 1899 if (observer != null) { 1900 return observer.findA11yWindowInfoByIdLocked(windowId); 1901 } 1902 return null; 1903 } 1904 1905 /** 1906 * Returns the window info according to given windowId. 1907 * 1908 * @param windowId The windowId 1909 * @return The window info 1910 */ 1911 @Nullable findWindowInfoByIdLocked(int windowId)1912 public WindowInfo findWindowInfoByIdLocked(int windowId) { 1913 windowId = resolveParentWindowIdLocked(windowId); 1914 final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId); 1915 if (observer != null) { 1916 return observer.findWindowInfoByIdLocked(windowId); 1917 } 1918 return null; 1919 } 1920 1921 /** 1922 * Returns focused windowId or accessibility focused windowId according to given focusType. 1923 * 1924 * @param focusType {@link AccessibilityNodeInfo#FOCUS_INPUT} or 1925 * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY} 1926 * @return The focused windowId 1927 */ getFocusedWindowId(int focusType)1928 public int getFocusedWindowId(int focusType) { 1929 return getFocusedWindowId(focusType, Display.INVALID_DISPLAY); 1930 } 1931 1932 /** 1933 * Returns focused windowId or accessibility focused windowId according to given focusType and 1934 * display id. 1935 * @param focusType {@link AccessibilityNodeInfo#FOCUS_INPUT} or 1936 * {@link AccessibilityNodeInfo#FOCUS_ACCESSIBILITY} 1937 * @param displayId the display id to check. If this display is proxy-ed, the proxy's a11y focus 1938 * will be returned. 1939 * @return The focused windowId 1940 */ getFocusedWindowId(int focusType, int displayId)1941 public int getFocusedWindowId(int focusType, int displayId) { 1942 if (displayId == Display.INVALID_DISPLAY || displayId == Display.DEFAULT_DISPLAY 1943 || !mHasProxy) { 1944 return getDefaultFocus(focusType); 1945 } 1946 1947 final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId); 1948 if (observer != null && observer.mIsProxy) { 1949 return getProxyFocus(focusType, observer); 1950 } else { 1951 return getDefaultFocus(focusType); 1952 } 1953 } 1954 getDefaultFocus(int focusType)1955 private int getDefaultFocus(int focusType) { 1956 if (focusType == AccessibilityNodeInfo.FOCUS_INPUT) { 1957 return mTopFocusedWindowId; 1958 } else if (focusType == AccessibilityNodeInfo.FOCUS_ACCESSIBILITY) { 1959 return mAccessibilityFocusedWindowId; 1960 } 1961 return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 1962 } 1963 getProxyFocus(int focusType, DisplayWindowsObserver observer)1964 private int getProxyFocus(int focusType, DisplayWindowsObserver observer) { 1965 if (focusType == AccessibilityNodeInfo.FOCUS_INPUT) { 1966 return mTopFocusedWindowId; 1967 } else if (focusType == AccessibilityNodeInfo.FOCUS_ACCESSIBILITY) { 1968 return observer.mProxyDisplayAccessibilityFocusedWindow; 1969 } else { 1970 return AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 1971 } 1972 } 1973 1974 /** 1975 * Returns {@link AccessibilityWindowInfo} of PIP window. 1976 * 1977 * @return PIP accessibility window info 1978 */ 1979 @Nullable getPictureInPictureWindowLocked()1980 public AccessibilityWindowInfo getPictureInPictureWindowLocked() { 1981 AccessibilityWindowInfo windowInfo = null; 1982 final int count = mDisplayWindowsObservers.size(); 1983 for (int i = 0; i < count; i++) { 1984 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); 1985 if (observer != null) { 1986 if ((windowInfo = observer.getPictureInPictureWindowLocked()) != null) { 1987 break; 1988 } 1989 } 1990 } 1991 return windowInfo; 1992 } 1993 1994 /** 1995 * Sets an IAccessibilityInteractionConnection to replace the actions of a picture-in-picture 1996 * window. 1997 */ setPictureInPictureActionReplacingConnection( @ullable IAccessibilityInteractionConnection connection)1998 public void setPictureInPictureActionReplacingConnection( 1999 @Nullable IAccessibilityInteractionConnection connection) throws RemoteException { 2000 synchronized (mLock) { 2001 if (mPictureInPictureActionReplacingConnection != null) { 2002 mPictureInPictureActionReplacingConnection.unlinkToDeath(); 2003 mPictureInPictureActionReplacingConnection = null; 2004 } 2005 if (connection != null) { 2006 RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection( 2007 AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID, 2008 connection, "foo.bar.baz", Process.SYSTEM_UID, UserHandle.USER_ALL); 2009 mPictureInPictureActionReplacingConnection = wrapper; 2010 wrapper.linkToDeath(); 2011 } 2012 } 2013 } 2014 2015 /** 2016 * Returns accessibility interaction connection for picture-in-picture window. 2017 */ 2018 @Nullable getPictureInPictureActionReplacingConnection()2019 public RemoteAccessibilityConnection getPictureInPictureActionReplacingConnection() { 2020 return mPictureInPictureActionReplacingConnection; 2021 } 2022 2023 /** 2024 * Invokes {@link IAccessibilityInteractionConnection#notifyOutsideTouch()} for windows that 2025 * have watch outside touch flag and its layer is upper than target window. 2026 */ notifyOutsideTouch(int userId, int targetWindowId)2027 public void notifyOutsideTouch(int userId, int targetWindowId) { 2028 final List<Integer> outsideWindowsIds; 2029 final List<RemoteAccessibilityConnection> connectionList = new ArrayList<>(); 2030 synchronized (mLock) { 2031 final DisplayWindowsObserver observer = 2032 getDisplayWindowObserverByWindowIdLocked(targetWindowId); 2033 if (observer != null) { 2034 outsideWindowsIds = observer.getWatchOutsideTouchWindowIdLocked(targetWindowId); 2035 for (int i = 0; i < outsideWindowsIds.size(); i++) { 2036 connectionList.add(getConnectionLocked(userId, outsideWindowsIds.get(i))); 2037 } 2038 } 2039 } 2040 for (int i = 0; i < connectionList.size(); i++) { 2041 final RemoteAccessibilityConnection connection = connectionList.get(i); 2042 if (connection != null) { 2043 if (traceIntConnEnabled()) { 2044 logTraceIntConn("notifyOutsideTouch"); 2045 } 2046 2047 try { 2048 connection.getRemote().notifyOutsideTouch(); 2049 } catch (RemoteException re) { 2050 if (DEBUG) { 2051 Slog.e(LOG_TAG, "Error calling notifyOutsideTouch()"); 2052 } 2053 } 2054 } 2055 } 2056 } 2057 2058 /** 2059 * Returns the display ID according to given userId and windowId. 2060 * 2061 * @param userId The userId 2062 * @param windowId The windowId 2063 * @return The display ID 2064 */ getDisplayIdByUserIdAndWindowId(int userId, int windowId)2065 public int getDisplayIdByUserIdAndWindowId(int userId, int windowId) { 2066 final IBinder windowToken; 2067 synchronized (mLock) { 2068 windowToken = getWindowTokenForUserAndWindowIdLocked(userId, windowId); 2069 } 2070 if (traceWMEnabled()) { 2071 logTraceWM("getDisplayIdForWindow", "token=" + windowToken); 2072 } 2073 final int displayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken); 2074 return displayId; 2075 } 2076 2077 /** 2078 * Returns the display list including all displays which are tracking windows. 2079 * 2080 * @param displayTypes the types of displays to retrieve 2081 * @return The display list. 2082 */ getDisplayListLocked( @bstractAccessibilityServiceConnection.DisplayTypes int displayTypes)2083 public ArrayList<Integer> getDisplayListLocked( 2084 @AbstractAccessibilityServiceConnection.DisplayTypes int displayTypes) { 2085 final ArrayList<Integer> displayList = new ArrayList<>(); 2086 final int count = mDisplayWindowsObservers.size(); 2087 for (int i = 0; i < count; i++) { 2088 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); 2089 if (observer != null) { 2090 if (!observer.mIsProxy && (displayTypes & DISPLAY_TYPE_DEFAULT) != 0) { 2091 displayList.add(observer.mDisplayId); 2092 } else if (observer.mIsProxy && (displayTypes & DISPLAY_TYPE_PROXY) != 0) { 2093 displayList.add(observer.mDisplayId); 2094 } 2095 } 2096 } 2097 return displayList; 2098 } 2099 2100 // If there is no service that can operate with interactive windows 2101 // then a window loses accessibility focus if it is no longer active. 2102 // This inspection happens when the user interaction is ended. 2103 // Note that to allow a service to work across windows, 2104 // we have to allow accessibility focus stay in any of them. accessibilityFocusOnlyInActiveWindowLocked()2105 boolean accessibilityFocusOnlyInActiveWindowLocked() { 2106 return !isTrackingWindowsLocked(); 2107 } 2108 2109 /** 2110 * Gets current input focused window token from window manager, and returns its windowId. 2111 * 2112 * @param userId The userId 2113 * @return The input focused windowId, or -1 if not found 2114 */ findFocusedWindowId(int userId)2115 private int findFocusedWindowId(int userId) { 2116 if (traceWMEnabled()) { 2117 logTraceWM("getFocusedWindowToken", ""); 2118 } 2119 final IBinder token = mWindowManagerInternal.getFocusedWindowTokenFromWindowStates(); 2120 synchronized (mLock) { 2121 return findWindowIdLocked(userId, token); 2122 } 2123 } 2124 isValidUserForInteractionConnectionsLocked(int userId)2125 private boolean isValidUserForInteractionConnectionsLocked(int userId) { 2126 return mInteractionConnections.indexOfKey(userId) >= 0; 2127 } 2128 isValidUserForWindowTokensLocked(int userId)2129 private boolean isValidUserForWindowTokensLocked(int userId) { 2130 return mWindowTokens.indexOfKey(userId) >= 0; 2131 } 2132 getInteractionConnectionsForUserLocked( int userId)2133 private SparseArray<RemoteAccessibilityConnection> getInteractionConnectionsForUserLocked( 2134 int userId) { 2135 SparseArray<RemoteAccessibilityConnection> connection = mInteractionConnections.get( 2136 userId); 2137 if (connection == null) { 2138 connection = new SparseArray<>(); 2139 mInteractionConnections.put(userId, connection); 2140 } 2141 return connection; 2142 } 2143 getWindowTokensForUserLocked(int userId)2144 private SparseArray<IBinder> getWindowTokensForUserLocked(int userId) { 2145 SparseArray<IBinder> windowTokens = mWindowTokens.get(userId); 2146 if (windowTokens == null) { 2147 windowTokens = new SparseArray<>(); 2148 mWindowTokens.put(userId, windowTokens); 2149 } 2150 return windowTokens; 2151 } 2152 clearAccessibilityFocusLocked(int windowId)2153 private void clearAccessibilityFocusLocked(int windowId) { 2154 mHandler.sendMessage(obtainMessage( 2155 AccessibilityWindowManager::clearAccessibilityFocusMainThread, 2156 AccessibilityWindowManager.this, 2157 mAccessibilityUserManager.getCurrentUserIdLocked(), windowId)); 2158 } 2159 clearAccessibilityFocusMainThread(int userId, int windowId)2160 private void clearAccessibilityFocusMainThread(int userId, int windowId) { 2161 final RemoteAccessibilityConnection connection; 2162 synchronized (mLock) { 2163 connection = getConnectionLocked(userId, windowId); 2164 if (connection == null) { 2165 return; 2166 } 2167 } 2168 if (traceIntConnEnabled()) { 2169 logTraceIntConn("notifyOutsideTouch"); 2170 } 2171 try { 2172 connection.getRemote().clearAccessibilityFocus(); 2173 } catch (RemoteException re) { 2174 if (DEBUG) { 2175 Slog.e(LOG_TAG, "Error calling clearAccessibilityFocus()"); 2176 } 2177 } 2178 } 2179 getDisplayWindowObserverByWindowIdLocked(int windowId)2180 private DisplayWindowsObserver getDisplayWindowObserverByWindowIdLocked(int windowId) { 2181 final int count = mDisplayWindowsObservers.size(); 2182 for (int i = 0; i < count; i++) { 2183 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); 2184 if (observer != null) { 2185 if (observer.findWindowInfoByIdLocked(windowId) != null) { 2186 return mDisplayWindowsObservers.get(observer.mDisplayId); 2187 } 2188 } 2189 } 2190 return null; 2191 } 2192 traceWMEnabled()2193 private boolean traceWMEnabled() { 2194 return mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL); 2195 } 2196 logTraceWM(String methodName, String params)2197 private void logTraceWM(String methodName, String params) { 2198 mTraceManager.logTrace("WindowManagerInternal." + methodName, 2199 FLAGS_WINDOW_MANAGER_INTERNAL, params); 2200 } 2201 traceIntConnEnabled()2202 private boolean traceIntConnEnabled() { 2203 return mTraceManager.isA11yTracingEnabledForTypes( 2204 FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION); 2205 } 2206 logTraceIntConn(String methodName)2207 private void logTraceIntConn(String methodName) { 2208 mTraceManager.logTrace( 2209 LOG_TAG + "." + methodName, FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION); 2210 } 2211 2212 /** 2213 * Associate the token of the embedded view hierarchy to the host view hierarchy. 2214 * 2215 * @param embedded The leash token from the view root of embedded hierarchy 2216 * @param host The leash token from the view root of host hierarchy 2217 */ associateLocked(IBinder embedded, IBinder host)2218 void associateLocked(IBinder embedded, IBinder host) { 2219 mHostEmbeddedMap.put(embedded, host); 2220 } 2221 2222 /** 2223 * Clear the relationship of given token. 2224 * 2225 * @param token The leash token 2226 */ disassociateLocked(IBinder token)2227 void disassociateLocked(IBinder token) { 2228 mHostEmbeddedMap.remove(token); 2229 for (int i = mHostEmbeddedMap.size() - 1; i >= 0; i--) { 2230 if (mHostEmbeddedMap.valueAt(i).equals(token)) { 2231 mHostEmbeddedMap.removeAt(i); 2232 } 2233 } 2234 } 2235 2236 /** 2237 * Register the leash token with its windowId. 2238 * 2239 * @param token The token. 2240 * @param windowId The windowID. 2241 */ registerIdLocked(IBinder token, int windowId)2242 void registerIdLocked(IBinder token, int windowId) { 2243 mWindowIdMap.put(windowId, token); 2244 } 2245 2246 /** 2247 * Unregister the windowId and also disassociate its token. 2248 * 2249 * @param windowId The windowID 2250 */ unregisterIdLocked(int windowId)2251 void unregisterIdLocked(int windowId) { 2252 final IBinder token = mWindowIdMap.get(windowId); 2253 if (token == null) { 2254 return; 2255 } 2256 disassociateLocked(token); 2257 mWindowIdMap.remove(windowId); 2258 } 2259 2260 /** 2261 * Get the leash token by given windowID. 2262 * 2263 * @param windowId The windowID. 2264 * @return The token, or {@code NULL} if this windowID doesn't exist 2265 */ getLeashTokenLocked(int windowId)2266 IBinder getLeashTokenLocked(int windowId) { 2267 return mWindowIdMap.get(windowId); 2268 } 2269 2270 /** 2271 * Get the windowId by given leash token. 2272 * 2273 * @param token The token 2274 * @return The windowID, or -1 if the token doesn't exist 2275 */ getWindowIdLocked(IBinder token)2276 int getWindowIdLocked(IBinder token) { 2277 final int index = mWindowIdMap.indexOfValue(token); 2278 if (index == -1) { 2279 return index; 2280 } 2281 return mWindowIdMap.keyAt(index); 2282 } 2283 2284 /** 2285 * Get the leash token of the host hierarchy by given token. 2286 * 2287 * @param token The token 2288 * @return The token of host hierarchy, or {@code NULL} if no host exists 2289 */ getHostTokenLocked(IBinder token)2290 IBinder getHostTokenLocked(IBinder token) { 2291 return mHostEmbeddedMap.get(token); 2292 } 2293 2294 /** 2295 * Checks if the window is embedded into another window so that the window should be excluded 2296 * from the exposed accessibility windows, and the node tree should be embedded in the host. 2297 */ isEmbeddedHierarchyWindowsLocked(int windowId)2298 boolean isEmbeddedHierarchyWindowsLocked(int windowId) { 2299 if (mHostEmbeddedMap.size() == 0) { 2300 return false; 2301 } 2302 2303 final IBinder leashToken = getLeashTokenLocked(windowId); 2304 if (leashToken == null) { 2305 return false; 2306 } 2307 2308 return mHostEmbeddedMap.containsKey(leashToken); 2309 } 2310 2311 /** 2312 * Checks if the window belongs to a proxy display and if so clears the focused window id. 2313 * @param focusClearedWindowId the cleared window id. 2314 * @return true if an observer is proxy-ed and has cleared its focused window id. 2315 */ clearProxyFocusLocked(int focusClearedWindowId, int eventAction)2316 private boolean clearProxyFocusLocked(int focusClearedWindowId, int eventAction) { 2317 // If we are just moving focus from one view to the other in the same window, do nothing. 2318 if (eventAction == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) { 2319 return false; 2320 } 2321 for (int i = 0; i < mDisplayWindowsObservers.size(); i++) { 2322 final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(i); 2323 if (observer != null && observer.mWindows != null && observer.mIsProxy) { 2324 final int windowCount = observer.mWindows.size(); 2325 for (int j = 0; j < windowCount; j++) { 2326 AccessibilityWindowInfo window = observer.mWindows.get(j); 2327 if (window.getId() == focusClearedWindowId) { 2328 observer.mProxyDisplayAccessibilityFocusedWindow = 2329 AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 2330 // TODO(268754409): Look into sending a WINDOW_FOCUS_CHANGED event since 2331 // window no longer has focus (default window logic doesn't), and 2332 // whether the node id needs to be cached (default window logic does). 2333 return true; 2334 } 2335 } 2336 } 2337 } 2338 return false; 2339 } 2340 2341 /** 2342 * Checks if the window belongs to a proxy display and if so sends 2343 * WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED for that window and the previously focused window. 2344 * @param focusedWindowId the focused window id. 2345 * @return true if an observer is proxy-ed and contains the focused window. 2346 */ setProxyFocusLocked(int focusedWindowId)2347 private boolean setProxyFocusLocked(int focusedWindowId) { 2348 for (int i = 0; i < mDisplayWindowsObservers.size(); i++) { 2349 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); 2350 if (observer != null && observer.mIsProxy 2351 && observer.setAccessibilityFocusedWindowLocked(focusedWindowId)) { 2352 final int previouslyFocusedWindowId = 2353 observer.mProxyDisplayAccessibilityFocusedWindow; 2354 2355 if (previouslyFocusedWindowId == focusedWindowId) { 2356 // Don't send a focus event if the window is already focused. 2357 return true; 2358 } 2359 2360 // Previously focused window -> Clear focus on UI thread and send a focused event 2361 // for losing focus 2362 if (previouslyFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) { 2363 clearAccessibilityFocusLocked(previouslyFocusedWindowId); 2364 mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked( 2365 AccessibilityEvent.obtainWindowsChangedEvent( 2366 observer.mDisplayId, previouslyFocusedWindowId, 2367 WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)); 2368 } 2369 observer.mProxyDisplayAccessibilityFocusedWindow = focusedWindowId; 2370 // Newly focused window -> send a focused event for it gaining focus 2371 mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked( 2372 AccessibilityEvent.obtainWindowsChangedEvent( 2373 observer.mDisplayId, 2374 observer.mProxyDisplayAccessibilityFocusedWindow, 2375 WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED)); 2376 2377 return true; 2378 } 2379 } 2380 return false; 2381 } 2382 2383 /** 2384 * Dumps all {@link AccessibilityWindowInfo}s here. 2385 */ dump(FileDescriptor fd, final PrintWriter pw, String[] args)2386 public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { 2387 pw.append("Global Info [ "); 2388 pw.println("Top focused display Id = " + mTopFocusedDisplayId); 2389 pw.println(" Active Window Id = " + mActiveWindowId); 2390 pw.println(" Top Focused Window Id = " + mTopFocusedWindowId); 2391 pw.println(" Accessibility Focused Window Id = " + mAccessibilityFocusedWindowId 2392 + " ]"); 2393 pw.println(); 2394 final int count = mDisplayWindowsObservers.size(); 2395 for (int i = 0; i < count; i++) { 2396 final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i); 2397 if (observer != null) { 2398 observer.dumpLocked(fd, pw, args); 2399 } 2400 } 2401 pw.println(); 2402 pw.append("Window attributes:["); 2403 pw.append(mWindowAttributes.toString()); 2404 pw.append("]"); 2405 pw.println(); 2406 } 2407 } 2408