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