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