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 com.android.server.accessibility.AbstractAccessibilityServiceConnection.DISPLAY_TYPE_DEFAULT; 20 import static com.android.server.accessibility.AccessibilityWindowManagerTest.DisplayIdMatcher.displayId; 21 import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowChangesMatcher.a11yWindowChanges; 22 import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowIdMatcher.a11yWindowId; 23 24 import static junit.framework.Assert.assertFalse; 25 import static junit.framework.Assert.assertNotNull; 26 import static junit.framework.Assert.assertNull; 27 import static junit.framework.Assert.assertTrue; 28 29 import static org.hamcrest.Matchers.allOf; 30 import static org.hamcrest.Matchers.is; 31 import static org.hamcrest.Matchers.not; 32 import static org.junit.Assert.assertEquals; 33 import static org.junit.Assert.assertNotEquals; 34 import static org.junit.Assert.assertThat; 35 import static org.mockito.ArgumentMatchers.anyInt; 36 import static org.mockito.ArgumentMatchers.anyString; 37 import static org.mockito.ArgumentMatchers.eq; 38 import static org.mockito.ArgumentMatchers.isNull; 39 import static org.mockito.Mockito.doAnswer; 40 import static org.mockito.Mockito.times; 41 import static org.mockito.Mockito.verify; 42 import static org.mockito.Mockito.when; 43 44 import android.graphics.Region; 45 import android.os.IBinder; 46 import android.os.LocaleList; 47 import android.os.RemoteException; 48 import android.os.UserHandle; 49 import android.text.TextUtils; 50 import android.util.SparseArray; 51 import android.view.Display; 52 import android.view.IWindow; 53 import android.view.WindowInfo; 54 import android.view.WindowManager; 55 import android.view.accessibility.AccessibilityEvent; 56 import android.view.accessibility.AccessibilityNodeInfo; 57 import android.view.accessibility.AccessibilityWindowAttributes; 58 import android.view.accessibility.AccessibilityWindowInfo; 59 import android.view.accessibility.IAccessibilityInteractionConnection; 60 61 import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection; 62 import com.android.server.accessibility.test.MessageCapturingHandler; 63 import com.android.server.wm.WindowManagerInternal; 64 import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; 65 66 import org.hamcrest.Description; 67 import org.hamcrest.TypeSafeMatcher; 68 import org.junit.After; 69 import org.junit.Before; 70 import org.junit.Test; 71 import org.mockito.ArgumentCaptor; 72 import org.mockito.Mock; 73 import org.mockito.Mockito; 74 import org.mockito.MockitoAnnotations; 75 76 import java.util.ArrayList; 77 import java.util.Arrays; 78 import java.util.List; 79 80 /** 81 * Tests for the AccessibilityWindowManager 82 */ 83 public class AccessibilityWindowManagerTest { 84 private static final String PACKAGE_NAME = "com.android.server.accessibility"; 85 private static final boolean FORCE_SEND = true; 86 private static final boolean SEND_ON_WINDOW_CHANGES = false; 87 private static final int USER_SYSTEM_ID = UserHandle.USER_SYSTEM; 88 private static final int USER_PROFILE = 11; 89 private static final int USER_PROFILE_PARENT = 1; 90 private static final int SECONDARY_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1; 91 private static final int NUM_GLOBAL_WINDOWS = 4; 92 private static final int NUM_APP_WINDOWS = 4; 93 private static final int NUM_OF_WINDOWS = (NUM_GLOBAL_WINDOWS + NUM_APP_WINDOWS); 94 private static final int DEFAULT_FOCUSED_INDEX = 1; 95 private static final int SCREEN_WIDTH = 1080; 96 private static final int SCREEN_HEIGHT = 1920; 97 private static final int INVALID_ID = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 98 private static final int HOST_WINDOW_ID = 10; 99 private static final int EMBEDDED_WINDOW_ID = 11; 100 private static final int OTHER_WINDOW_ID = 12; 101 102 private AccessibilityWindowManager mA11yWindowManager; 103 // Window manager will support multiple focused window if config_perDisplayFocusEnabled is true, 104 // i.e., each display would have its current focused window, and one of all focused windows 105 // would be top focused window. Otherwise, window manager only supports one focused window 106 // at all displays, and that focused window would be top focused window. 107 private boolean mSupportPerDisplayFocus = false; 108 private int mTopFocusedDisplayId = Display.INVALID_DISPLAY; 109 private IBinder mTopFocusedWindowToken = null; 110 111 // List of window token, mapping from windowId -> window token. 112 private final SparseArray<IWindow> mA11yWindowTokens = new SparseArray<>(); 113 // List of window info lists, mapping from displayId -> window info lists. 114 private final SparseArray<ArrayList<WindowInfo>> mWindowInfos = 115 new SparseArray<>(); 116 // List of callback, mapping from displayId -> callback. 117 private final SparseArray<WindowsForAccessibilityCallback> mCallbackOfWindows = 118 new SparseArray<>(); 119 // List of display ID. 120 private final ArrayList<Integer> mExpectedDisplayList = new ArrayList<>(Arrays.asList( 121 Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID)); 122 123 private final MessageCapturingHandler mHandler = new MessageCapturingHandler(null); 124 125 @Mock private WindowManagerInternal mMockWindowManagerInternal; 126 @Mock private AccessibilityWindowManager.AccessibilityEventSender mMockA11yEventSender; 127 @Mock private AccessibilitySecurityPolicy mMockA11ySecurityPolicy; 128 @Mock private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager; 129 @Mock private AccessibilityTraceManager mMockA11yTraceManager; 130 131 @Mock private IBinder mMockHostToken; 132 @Mock private IBinder mMockEmbeddedToken; 133 @Mock private IBinder mMockInvalidToken; 134 135 @Before setUp()136 public void setUp() throws RemoteException { 137 MockitoAnnotations.initMocks(this); 138 when(mMockA11yUserManager.getCurrentUserIdLocked()).thenReturn(USER_SYSTEM_ID); 139 when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked( 140 USER_PROFILE)).thenReturn(USER_PROFILE_PARENT); 141 when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked( 142 USER_SYSTEM_ID)).thenReturn(USER_SYSTEM_ID); 143 when(mMockA11ySecurityPolicy.resolveValidReportedPackageLocked( 144 anyString(), anyInt(), anyInt(), anyInt())).thenReturn(PACKAGE_NAME); 145 146 doAnswer((invocation) -> { 147 onWindowsForAccessibilityChanged(invocation.getArgument(0), false); 148 return null; 149 }).when(mMockWindowManagerInternal).computeWindowsForAccessibility(anyInt()); 150 151 mA11yWindowManager = new AccessibilityWindowManager(new Object(), mHandler, 152 mMockWindowManagerInternal, 153 mMockA11yEventSender, 154 mMockA11ySecurityPolicy, 155 mMockA11yUserManager, 156 mMockA11yTraceManager); 157 // Starts tracking window of default display and sets the default display 158 // as top focused display before each testing starts. 159 startTrackingPerDisplay(Display.DEFAULT_DISPLAY); 160 161 // AccessibilityEventSender is invoked during onWindowsForAccessibilityChanged. 162 // Resets it for mockito verify of further test case. 163 Mockito.reset(mMockA11yEventSender); 164 165 registerLeashedTokenAndWindowId(); 166 } 167 168 @After tearDown()169 public void tearDown() { 170 mHandler.removeAllMessages(); 171 } 172 173 @Test startTrackingWindows_shouldEnableWindowManagerCallback()174 public void startTrackingWindows_shouldEnableWindowManagerCallback() { 175 // AccessibilityWindowManager#startTrackingWindows already invoked in setup. 176 assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); 177 final WindowsForAccessibilityCallback callbacks = 178 mCallbackOfWindows.get(Display.DEFAULT_DISPLAY); 179 verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback( 180 eq(Display.DEFAULT_DISPLAY), eq(callbacks)); 181 } 182 183 @Test stopTrackingWindows_shouldDisableWindowManagerCallback()184 public void stopTrackingWindows_shouldDisableWindowManagerCallback() { 185 assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); 186 Mockito.reset(mMockWindowManagerInternal); 187 188 mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); 189 assertFalse(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); 190 verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback( 191 eq(Display.DEFAULT_DISPLAY), isNull()); 192 193 } 194 195 @Test stopTrackingWindows_shouldClearWindows()196 public void stopTrackingWindows_shouldClearWindows() { 197 assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); 198 final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); 199 200 mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); 201 assertNull(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY)); 202 assertEquals(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), 203 AccessibilityWindowInfo.UNDEFINED_WINDOW_ID); 204 assertEquals(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), 205 activeWindowId); 206 } 207 208 @Test stopTrackingWindows_onNonTopFocusedDisplay_shouldNotResetTopFocusWindow()209 public void stopTrackingWindows_onNonTopFocusedDisplay_shouldNotResetTopFocusWindow() 210 throws RemoteException { 211 // At setup, the default display sets be the top focused display and 212 // its current focused window sets be the top focused window. 213 // Starts tracking window of second display. 214 startTrackingPerDisplay(SECONDARY_DISPLAY_ID); 215 assertTrue(mA11yWindowManager.isTrackingWindowsLocked(SECONDARY_DISPLAY_ID)); 216 // Stops tracking windows of second display. 217 mA11yWindowManager.stopTrackingWindows(SECONDARY_DISPLAY_ID); 218 assertNotEquals(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), 219 AccessibilityWindowInfo.UNDEFINED_WINDOW_ID); 220 } 221 222 @Test onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow()223 public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() { 224 final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); 225 WindowInfo focusedWindowInfo = 226 mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX); 227 assertEquals(activeWindowId, mA11yWindowManager.findWindowIdLocked( 228 USER_SYSTEM_ID, focusedWindowInfo.token)); 229 230 focusedWindowInfo.focused = false; 231 focusedWindowInfo = 232 mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1); 233 focusedWindowInfo.focused = true; 234 235 mA11yWindowManager.onTouchInteractionStart(); 236 setTopFocusedWindowAndDisplay(Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX + 1); 237 onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); 238 239 assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); 240 } 241 242 @Test 243 public void onWindowsChanged_focusChangeOnNonTopFocusedDisplay_perDisplayFocusOn_notChangeWindow()244 onWindowsChanged_focusChangeOnNonTopFocusedDisplay_perDisplayFocusOn_notChangeWindow() 245 throws RemoteException { 246 // At setup, the default display sets be the top focused display and 247 // its current focused window sets be the top focused window. 248 // Sets supporting multiple focused window, i.e., config_perDisplayFocusEnabled is true. 249 mSupportPerDisplayFocus = true; 250 // Starts tracking window of second display. 251 startTrackingPerDisplay(SECONDARY_DISPLAY_ID); 252 // Gets the active window. 253 final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); 254 // Gets the top focused window. 255 final int topFocusedWindowId = 256 mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT); 257 // Changes the current focused window at second display. 258 changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID, 259 DEFAULT_FOCUSED_INDEX + 1, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX); 260 261 onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); 262 // The active window should not be changed. 263 assertEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); 264 // The top focused window should not be changed. 265 assertEquals(topFocusedWindowId, 266 mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT)); 267 } 268 269 @Test 270 public void onWindowChange_focusChangeToNonTopFocusedDisplay_perDisplayFocusOff_shouldChangeWindow()271 onWindowChange_focusChangeToNonTopFocusedDisplay_perDisplayFocusOff_shouldChangeWindow() 272 throws RemoteException { 273 // At setup, the default display sets be the top focused display and 274 // its current focused window sets be the top focused window. 275 // Sets not supporting multiple focused window, i.e., config_perDisplayFocusEnabled is 276 // false. 277 mSupportPerDisplayFocus = false; 278 // Starts tracking window of second display. 279 startTrackingPerDisplay(SECONDARY_DISPLAY_ID); 280 // Gets the active window. 281 final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); 282 // Gets the top focused window. 283 final int topFocusedWindowId = 284 mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT); 285 // Changes the current focused window from default display to second display. 286 changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID, 287 DEFAULT_FOCUSED_INDEX, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX); 288 289 onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); 290 onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); 291 // The active window should be changed. 292 assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); 293 // The top focused window should be changed. 294 assertNotEquals(topFocusedWindowId, 295 mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT)); 296 } 297 298 @Test onWindowsChanged_shouldReportCorrectLayer()299 public void onWindowsChanged_shouldReportCorrectLayer() { 300 // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup. 301 List<AccessibilityWindowInfo> a11yWindows = 302 mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); 303 for (int i = 0; i < a11yWindows.size(); i++) { 304 final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i); 305 final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i); 306 assertThat(mWindowInfos.get(Display.DEFAULT_DISPLAY).size() - windowInfo.layer - 1, 307 is(a11yWindow.getLayer())); 308 } 309 } 310 311 @Test onWindowsChanged_shouldReportCorrectOrder()312 public void onWindowsChanged_shouldReportCorrectOrder() { 313 // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup. 314 List<AccessibilityWindowInfo> a11yWindows = 315 mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); 316 for (int i = 0; i < a11yWindows.size(); i++) { 317 final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i); 318 final IBinder windowToken = mA11yWindowManager 319 .getWindowTokenForUserAndWindowIdLocked(USER_SYSTEM_ID, a11yWindow.getId()); 320 final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i); 321 assertThat(windowToken, is(windowInfo.token)); 322 } 323 } 324 325 @Test onWindowsChangedAndForceSend_shouldUpdateWindows()326 public void onWindowsChangedAndForceSend_shouldUpdateWindows() { 327 final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); 328 final int correctLayer = 329 mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer(); 330 windowInfo.layer += 1; 331 332 onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); 333 assertNotEquals(correctLayer, 334 mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer()); 335 } 336 337 @Test onWindowsChangedNoForceSend_layerChanged_shouldNotUpdateWindows()338 public void onWindowsChangedNoForceSend_layerChanged_shouldNotUpdateWindows() { 339 final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); 340 final int correctLayer = 341 mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer(); 342 windowInfo.layer += 1; 343 344 onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); 345 assertEquals(correctLayer, 346 mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer()); 347 } 348 349 @Test onWindowsChangedNoForceSend_windowChanged_shouldUpdateWindows()350 public void onWindowsChangedNoForceSend_windowChanged_shouldUpdateWindows() 351 throws RemoteException { 352 final AccessibilityWindowInfo oldWindow = 353 mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0); 354 final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, 355 true, USER_SYSTEM_ID); 356 final WindowInfo windowInfo = WindowInfo.obtain(); 357 windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION; 358 windowInfo.token = token.asBinder(); 359 windowInfo.layer = 0; 360 windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); 361 mWindowInfos.get(Display.DEFAULT_DISPLAY).set(0, windowInfo); 362 363 onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); 364 assertNotEquals(oldWindow, 365 mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0)); 366 } 367 368 @Test onWindowsChangedNoForceSend_focusChanged_shouldUpdateWindows()369 public void onWindowsChangedNoForceSend_focusChanged_shouldUpdateWindows() { 370 final WindowInfo focusedWindowInfo = 371 mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX); 372 final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); 373 focusedWindowInfo.focused = false; 374 windowInfo.focused = true; 375 376 onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); 377 assertTrue(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0) 378 .isFocused()); 379 } 380 381 @Test removeAccessibilityInteractionConnection_byWindowToken_shouldRemoved()382 public void removeAccessibilityInteractionConnection_byWindowToken_shouldRemoved() { 383 for (int i = 0; i < NUM_OF_WINDOWS; i++) { 384 final int windowId = mA11yWindowTokens.keyAt(i); 385 final IWindow windowToken = mA11yWindowTokens.valueAt(i); 386 assertNotNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId)); 387 388 mA11yWindowManager.removeAccessibilityInteractionConnection(windowToken); 389 assertNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId)); 390 } 391 } 392 393 @Test remoteAccessibilityConnection_binderDied_shouldRemoveConnection()394 public void remoteAccessibilityConnection_binderDied_shouldRemoveConnection() { 395 for (int i = 0; i < NUM_OF_WINDOWS; i++) { 396 final int windowId = mA11yWindowTokens.keyAt(i); 397 final RemoteAccessibilityConnection remoteA11yConnection = 398 mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId); 399 assertNotNull(remoteA11yConnection); 400 401 remoteA11yConnection.binderDied(); 402 assertNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId)); 403 } 404 } 405 406 @Test getWindowTokenForUserAndWindowId_shouldNotNull()407 public void getWindowTokenForUserAndWindowId_shouldNotNull() { 408 final List<AccessibilityWindowInfo> windows = 409 mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); 410 for (int i = 0; i < windows.size(); i++) { 411 final int windowId = windows.get(i).getId(); 412 413 assertNotNull(mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked( 414 USER_SYSTEM_ID, windowId)); 415 } 416 } 417 418 @Test findWindowId()419 public void findWindowId() { 420 final List<AccessibilityWindowInfo> windows = 421 mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); 422 for (int i = 0; i < windows.size(); i++) { 423 final int windowId = windows.get(i).getId(); 424 final IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked( 425 USER_SYSTEM_ID, windowId); 426 427 assertEquals(mA11yWindowManager.findWindowIdLocked( 428 USER_SYSTEM_ID, windowToken), windowId); 429 } 430 } 431 432 @Test resolveParentWindowId_windowIsNotEmbedded_shouldReturnGivenId()433 public void resolveParentWindowId_windowIsNotEmbedded_shouldReturnGivenId() 434 throws RemoteException { 435 final int windowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, false, 436 Mockito.mock(IBinder.class), USER_SYSTEM_ID); 437 assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId)); 438 } 439 440 @Test resolveParentWindowId_windowIsNotRegistered_shouldReturnGivenId()441 public void resolveParentWindowId_windowIsNotRegistered_shouldReturnGivenId() { 442 final int windowId = -1; 443 assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId)); 444 } 445 446 @Test resolveParentWindowId_windowIsAssociated_shouldReturnParentWindowId()447 public void resolveParentWindowId_windowIsAssociated_shouldReturnParentWindowId() 448 throws RemoteException { 449 final IBinder mockHostToken = Mockito.mock(IBinder.class); 450 final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class); 451 final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, 452 false, mockHostToken, USER_SYSTEM_ID); 453 final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, 454 false, mockEmbeddedToken, USER_SYSTEM_ID); 455 mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken); 456 final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked( 457 embeddedWindowId); 458 assertEquals(hostWindowId, resolvedWindowId); 459 } 460 461 @Test resolveParentWindowId_windowIsDisassociated_shouldReturnGivenId()462 public void resolveParentWindowId_windowIsDisassociated_shouldReturnGivenId() 463 throws RemoteException { 464 final IBinder mockHostToken = Mockito.mock(IBinder.class); 465 final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class); 466 final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, 467 false, mockHostToken, USER_SYSTEM_ID); 468 final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, 469 false, mockEmbeddedToken, USER_SYSTEM_ID); 470 mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken); 471 mA11yWindowManager.disassociateEmbeddedHierarchyLocked(mockEmbeddedToken); 472 final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked( 473 embeddedWindowId); 474 assertEquals(embeddedWindowId, resolvedWindowId); 475 } 476 477 @Test computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion()478 public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() { 479 // Updates top 2 z-order WindowInfo are whole visible. 480 WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); 481 windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2); 482 windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1); 483 windowInfo.regionInScreen.set(0, SCREEN_HEIGHT / 2, 484 SCREEN_WIDTH, SCREEN_HEIGHT); 485 onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); 486 487 final List<AccessibilityWindowInfo> a11yWindows = 488 mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); 489 final Region outBounds = new Region(); 490 int windowId = a11yWindows.get(0).getId(); 491 492 mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); 493 assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); 494 assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2)); 495 496 windowId = a11yWindows.get(1).getId(); 497 498 mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); 499 assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); 500 assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2)); 501 } 502 503 @Test computePartialInteractiveRegionForWindow_halfVisible_returnHalfRegion()504 public void computePartialInteractiveRegionForWindow_halfVisible_returnHalfRegion() { 505 // Updates z-order #1 WindowInfo is half visible. 506 WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); 507 windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2); 508 509 onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); 510 final List<AccessibilityWindowInfo> a11yWindows = 511 mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); 512 final Region outBounds = new Region(); 513 int windowId = a11yWindows.get(1).getId(); 514 515 mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); 516 assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); 517 assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2)); 518 } 519 520 @Test computePartialInteractiveRegionForWindow_notVisible_returnEmptyRegion()521 public void computePartialInteractiveRegionForWindow_notVisible_returnEmptyRegion() { 522 // Since z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible. 523 final List<AccessibilityWindowInfo> a11yWindows = 524 mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); 525 final Region outBounds = new Region(); 526 int windowId = a11yWindows.get(1).getId(); 527 528 mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); 529 assertTrue(outBounds.getBounds().isEmpty()); 530 } 531 532 @Test computePartialInteractiveRegionForWindow_partialVisible_returnVisibleRegion()533 public void computePartialInteractiveRegionForWindow_partialVisible_returnVisibleRegion() { 534 // Updates z-order #0 WindowInfo to have two interact-able areas. 535 Region region = new Region(0, 0, SCREEN_WIDTH, 200); 536 region.op(0, SCREEN_HEIGHT - 200, SCREEN_WIDTH, SCREEN_HEIGHT, Region.Op.UNION); 537 WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); 538 windowInfo.regionInScreen.set(region); 539 onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); 540 541 final List<AccessibilityWindowInfo> a11yWindows = 542 mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); 543 final Region outBounds = new Region(); 544 int windowId = a11yWindows.get(1).getId(); 545 546 mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); 547 assertFalse(outBounds.getBounds().isEmpty()); 548 assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); 549 assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT - 400)); 550 } 551 552 @Test updateActiveAndA11yFocusedWindow_windowStateChangedEvent_noTracking_shouldUpdate()553 public void updateActiveAndA11yFocusedWindow_windowStateChangedEvent_noTracking_shouldUpdate() { 554 final IBinder eventWindowToken = 555 mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1).token; 556 final int eventWindowId = mA11yWindowManager.findWindowIdLocked( 557 USER_SYSTEM_ID, eventWindowToken); 558 when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates()) 559 .thenReturn(eventWindowToken); 560 561 final int noUse = 0; 562 mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); 563 mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, 564 eventWindowId, 565 noUse, 566 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, 567 noUse); 568 assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId)); 569 assertThat(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), 570 is(eventWindowId)); 571 } 572 573 @Test updateActiveAndA11yFocusedWindow_hoverEvent_touchInteract_shouldSetActiveWindow()574 public void updateActiveAndA11yFocusedWindow_hoverEvent_touchInteract_shouldSetActiveWindow() { 575 final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 576 DEFAULT_FOCUSED_INDEX + 1); 577 final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); 578 assertThat(currentActiveWindowId, is(not(eventWindowId))); 579 580 final int noUse = 0; 581 mA11yWindowManager.onTouchInteractionStart(); 582 mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, 583 eventWindowId, 584 noUse, 585 AccessibilityEvent.TYPE_VIEW_HOVER_ENTER, 586 noUse); 587 assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId)); 588 final ArgumentCaptor<AccessibilityEvent> captor = 589 ArgumentCaptor.forClass(AccessibilityEvent.class); 590 verify(mMockA11yEventSender, times(2)) 591 .sendAccessibilityEventForCurrentUserLocked(captor.capture()); 592 assertThat(captor.getAllValues().get(0), 593 allOf(displayId(Display.DEFAULT_DISPLAY), 594 a11yWindowId(currentActiveWindowId), 595 a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); 596 assertThat(captor.getAllValues().get(1), 597 allOf(displayId(Display.DEFAULT_DISPLAY), 598 a11yWindowId(eventWindowId), 599 a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); 600 } 601 602 @Test updateActiveAndA11yFocusedWindow_a11yFocusEvent_shouldUpdateA11yFocus()603 public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_shouldUpdateA11yFocus() { 604 final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 605 DEFAULT_FOCUSED_INDEX); 606 final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId( 607 AccessibilityNodeInfo.FOCUS_ACCESSIBILITY); 608 assertThat(currentA11yFocusedWindowId, is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)); 609 610 final int noUse = 0; 611 mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, 612 eventWindowId, 613 AccessibilityNodeInfo.ROOT_NODE_ID, 614 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, 615 noUse); 616 assertThat(mA11yWindowManager.getFocusedWindowId( 617 AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId)); 618 final ArgumentCaptor<AccessibilityEvent> captor = 619 ArgumentCaptor.forClass(AccessibilityEvent.class); 620 verify(mMockA11yEventSender, times(1)) 621 .sendAccessibilityEventForCurrentUserLocked(captor.capture()); 622 assertThat(captor.getAllValues().get(0), 623 allOf(displayId(Display.DEFAULT_DISPLAY), 624 a11yWindowId(eventWindowId), 625 a11yWindowChanges( 626 AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); 627 } 628 629 @Test updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_defaultToSecondary()630 public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_defaultToSecondary() 631 throws RemoteException { 632 runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest( 633 Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID); 634 } 635 636 @Test updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_SecondaryToDefault()637 public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_SecondaryToDefault() 638 throws RemoteException { 639 runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest( 640 SECONDARY_DISPLAY_ID, Display.DEFAULT_DISPLAY); 641 } 642 runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest( int initialDisplayId, int eventDisplayId)643 private void runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest( 644 int initialDisplayId, int eventDisplayId) throws RemoteException { 645 startTrackingPerDisplay(SECONDARY_DISPLAY_ID); 646 final int initialWindowId = getWindowIdFromWindowInfosForDisplay( 647 initialDisplayId, DEFAULT_FOCUSED_INDEX); 648 final int noUse = 0; 649 mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, 650 initialWindowId, 651 AccessibilityNodeInfo.ROOT_NODE_ID, 652 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, 653 noUse); 654 assertThat(mA11yWindowManager.getFocusedWindowId( 655 AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(initialWindowId)); 656 Mockito.reset(mMockA11yEventSender); 657 658 final int eventWindowId = getWindowIdFromWindowInfosForDisplay( 659 eventDisplayId, DEFAULT_FOCUSED_INDEX); 660 mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, 661 eventWindowId, 662 AccessibilityNodeInfo.ROOT_NODE_ID, 663 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, 664 noUse); 665 assertThat(mA11yWindowManager.getFocusedWindowId( 666 AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId)); 667 final ArgumentCaptor<AccessibilityEvent> captor = 668 ArgumentCaptor.forClass(AccessibilityEvent.class); 669 verify(mMockA11yEventSender, times(2)) 670 .sendAccessibilityEventForCurrentUserLocked(captor.capture()); 671 assertThat(captor.getAllValues().get(0), 672 allOf(displayId(initialDisplayId), 673 a11yWindowId(initialWindowId), 674 a11yWindowChanges( 675 AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); 676 assertThat(captor.getAllValues().get(1), 677 allOf(displayId(eventDisplayId), 678 a11yWindowId(eventWindowId), 679 a11yWindowChanges( 680 AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); 681 } 682 683 @Test updateActiveAndA11yFocusedWindow_clearA11yFocusEvent_shouldClearA11yFocus()684 public void updateActiveAndA11yFocusedWindow_clearA11yFocusEvent_shouldClearA11yFocus() { 685 final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 686 DEFAULT_FOCUSED_INDEX); 687 final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId( 688 AccessibilityNodeInfo.FOCUS_ACCESSIBILITY); 689 assertThat(currentA11yFocusedWindowId, is(not(eventWindowId))); 690 691 final int noUse = 0; 692 mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, 693 eventWindowId, 694 AccessibilityNodeInfo.ROOT_NODE_ID, 695 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, 696 noUse); 697 assertThat(mA11yWindowManager.getFocusedWindowId( 698 AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId)); 699 mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, 700 eventWindowId, 701 AccessibilityNodeInfo.ROOT_NODE_ID, 702 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, 703 noUse); 704 assertThat(mA11yWindowManager.getFocusedWindowId( 705 AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), 706 is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)); 707 } 708 709 @Test onTouchInteractionEnd_shouldRollbackActiveWindow()710 public void onTouchInteractionEnd_shouldRollbackActiveWindow() { 711 final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 712 DEFAULT_FOCUSED_INDEX + 1); 713 final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); 714 assertThat(currentActiveWindowId, is(not(eventWindowId))); 715 716 final int noUse = 0; 717 mA11yWindowManager.onTouchInteractionStart(); 718 mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, 719 eventWindowId, 720 noUse, 721 AccessibilityEvent.TYPE_VIEW_HOVER_ENTER, 722 noUse); 723 assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId)); 724 // AccessibilityEventSender is invoked after active window changed. Reset it. 725 Mockito.reset(mMockA11yEventSender); 726 727 mA11yWindowManager.onTouchInteractionEnd(); 728 final ArgumentCaptor<AccessibilityEvent> captor = 729 ArgumentCaptor.forClass(AccessibilityEvent.class); 730 verify(mMockA11yEventSender, times(2)) 731 .sendAccessibilityEventForCurrentUserLocked(captor.capture()); 732 assertThat(captor.getAllValues().get(0), 733 allOf(displayId(Display.DEFAULT_DISPLAY), 734 a11yWindowId(eventWindowId), 735 a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); 736 assertThat(captor.getAllValues().get(1), 737 allOf(displayId(Display.DEFAULT_DISPLAY), 738 a11yWindowId(currentActiveWindowId), 739 a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); 740 } 741 742 @Test onTouchInteractionEnd_noServiceInteractiveWindow_shouldClearA11yFocus()743 public void onTouchInteractionEnd_noServiceInteractiveWindow_shouldClearA11yFocus() 744 throws RemoteException { 745 final IBinder defaultFocusWinToken = 746 mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).token; 747 final int defaultFocusWindowId = mA11yWindowManager.findWindowIdLocked( 748 USER_SYSTEM_ID, defaultFocusWinToken); 749 when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates()) 750 .thenReturn(defaultFocusWinToken); 751 final int newFocusWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 752 DEFAULT_FOCUSED_INDEX + 1); 753 final IAccessibilityInteractionConnection mockNewFocusConnection = 754 mA11yWindowManager.getConnectionLocked( 755 USER_SYSTEM_ID, newFocusWindowId).getRemote(); 756 757 mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); 758 final int noUse = 0; 759 mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, 760 defaultFocusWindowId, 761 noUse, 762 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, 763 noUse); 764 assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(defaultFocusWindowId)); 765 assertThat(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), 766 is(defaultFocusWindowId)); 767 768 mA11yWindowManager.onTouchInteractionStart(); 769 mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, 770 newFocusWindowId, 771 noUse, 772 AccessibilityEvent.TYPE_VIEW_HOVER_ENTER, 773 noUse); 774 mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, 775 newFocusWindowId, 776 AccessibilityNodeInfo.ROOT_NODE_ID, 777 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, 778 noUse); 779 assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(newFocusWindowId)); 780 assertThat(mA11yWindowManager.getFocusedWindowId( 781 AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(newFocusWindowId)); 782 783 mA11yWindowManager.onTouchInteractionEnd(); 784 mHandler.sendLastMessage(); 785 verify(mockNewFocusConnection).clearAccessibilityFocus(); 786 } 787 788 @Test getPictureInPictureWindow_shouldNotNull()789 public void getPictureInPictureWindow_shouldNotNull() { 790 assertNull(mA11yWindowManager.getPictureInPictureWindowLocked()); 791 mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1).inPictureInPicture = true; 792 onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); 793 794 assertNotNull(mA11yWindowManager.getPictureInPictureWindowLocked()); 795 } 796 797 @Test notifyOutsideTouch()798 public void notifyOutsideTouch() throws RemoteException { 799 final int targetWindowId = 800 getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 1); 801 final int outsideWindowId = 802 getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); 803 final IAccessibilityInteractionConnection mockRemoteConnection = 804 mA11yWindowManager.getConnectionLocked( 805 USER_SYSTEM_ID, outsideWindowId).getRemote(); 806 mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0).hasFlagWatchOutsideTouch = true; 807 onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); 808 809 mA11yWindowManager.notifyOutsideTouch(USER_SYSTEM_ID, targetWindowId); 810 verify(mockRemoteConnection).notifyOutsideTouch(); 811 } 812 813 @Test addAccessibilityInteractionConnection_profileUser_findInParentUser()814 public void addAccessibilityInteractionConnection_profileUser_findInParentUser() 815 throws RemoteException { 816 final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, 817 false, USER_PROFILE); 818 final int windowId = mA11yWindowManager.findWindowIdLocked( 819 USER_PROFILE_PARENT, token.asBinder()); 820 assertTrue(windowId >= 0); 821 } 822 823 @Test getDisplayList()824 public void getDisplayList() throws RemoteException { 825 // Starts tracking window of second display. 826 startTrackingPerDisplay(SECONDARY_DISPLAY_ID); 827 828 final ArrayList<Integer> displayList = mA11yWindowManager.getDisplayListLocked( 829 DISPLAY_TYPE_DEFAULT); 830 assertTrue(displayList.equals(mExpectedDisplayList)); 831 } 832 833 @Test setAccessibilityWindowIdToSurfaceMetadata()834 public void setAccessibilityWindowIdToSurfaceMetadata() 835 throws RemoteException { 836 final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, 837 true, USER_SYSTEM_ID); 838 int windowId = -1; 839 for (int i = 0; i < mA11yWindowTokens.size(); i++) { 840 if (mA11yWindowTokens.valueAt(i).equals(token)) { 841 windowId = mA11yWindowTokens.keyAt(i); 842 } 843 } 844 assertNotEquals("Returned token is not found in mA11yWindowTokens", -1, windowId); 845 verify(mMockWindowManagerInternal, times(1)).setAccessibilityIdToSurfaceMetadata( 846 token.asBinder(), windowId); 847 848 mA11yWindowManager.removeAccessibilityInteractionConnection(token); 849 verify(mMockWindowManagerInternal, times(1)).setAccessibilityIdToSurfaceMetadata( 850 token.asBinder(), -1); 851 } 852 853 @Test getHostTokenLocked_hierarchiesAreAssociated_shouldReturnHostToken()854 public void getHostTokenLocked_hierarchiesAreAssociated_shouldReturnHostToken() { 855 mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken); 856 final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken); 857 assertEquals(hostToken, mMockHostToken); 858 } 859 860 @Test getHostTokenLocked_hierarchiesAreNotAssociated_shouldReturnNull()861 public void getHostTokenLocked_hierarchiesAreNotAssociated_shouldReturnNull() { 862 final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken); 863 assertNull(hostToken); 864 } 865 866 @Test getHostTokenLocked_embeddedHierarchiesAreDisassociated_shouldReturnNull()867 public void getHostTokenLocked_embeddedHierarchiesAreDisassociated_shouldReturnNull() { 868 mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken); 869 mA11yWindowManager.disassociateLocked(mMockEmbeddedToken); 870 final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken); 871 assertNull(hostToken); 872 } 873 874 @Test getHostTokenLocked_hostHierarchiesAreDisassociated_shouldReturnNull()875 public void getHostTokenLocked_hostHierarchiesAreDisassociated_shouldReturnNull() { 876 mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken); 877 mA11yWindowManager.disassociateLocked(mMockHostToken); 878 final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockHostToken); 879 assertNull(hostToken); 880 } 881 882 @Test getWindowIdLocked_windowIsRegistered_shouldReturnWindowId()883 public void getWindowIdLocked_windowIsRegistered_shouldReturnWindowId() { 884 final int windowId = mA11yWindowManager.getWindowIdLocked(mMockHostToken); 885 assertEquals(windowId, HOST_WINDOW_ID); 886 } 887 888 @Test getWindowIdLocked_windowIsNotRegistered_shouldReturnInvalidWindowId()889 public void getWindowIdLocked_windowIsNotRegistered_shouldReturnInvalidWindowId() { 890 final int windowId = mA11yWindowManager.getWindowIdLocked(mMockInvalidToken); 891 assertEquals(windowId, INVALID_ID); 892 } 893 894 @Test getTokenLocked_windowIsRegistered_shouldReturnToken()895 public void getTokenLocked_windowIsRegistered_shouldReturnToken() { 896 final IBinder token = mA11yWindowManager.getTokenLocked(HOST_WINDOW_ID); 897 assertEquals(token, mMockHostToken); 898 } 899 900 @Test getTokenLocked_windowIsNotRegistered_shouldReturnNull()901 public void getTokenLocked_windowIsNotRegistered_shouldReturnNull() { 902 final IBinder token = mA11yWindowManager.getTokenLocked(OTHER_WINDOW_ID); 903 assertNull(token); 904 } 905 906 @Test setAccessibilityWindowAttributes_windowIsNotRegistered_titleIsChanged()907 public void setAccessibilityWindowAttributes_windowIsNotRegistered_titleIsChanged() { 908 final int windowId = 909 getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); 910 final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); 911 layoutParams.accessibilityTitle = "accessibility window title"; 912 final AccessibilityWindowAttributes attributes = new AccessibilityWindowAttributes( 913 layoutParams, new LocaleList()); 914 915 mA11yWindowManager.setAccessibilityWindowAttributes(Display.DEFAULT_DISPLAY, windowId, 916 USER_SYSTEM_ID, attributes); 917 918 final AccessibilityWindowInfo a11yWindow = mA11yWindowManager.findA11yWindowInfoByIdLocked( 919 windowId); 920 assertTrue(TextUtils.equals(layoutParams.accessibilityTitle, a11yWindow.getTitle())); 921 } 922 923 @Test sendAccessibilityEventOnWindowRemoval()924 public void sendAccessibilityEventOnWindowRemoval() { 925 final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY); 926 927 // Removing index 0 because it's not focused, and avoids unnecessary layer change. 928 final int windowId = 929 getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); 930 infos.remove(0); 931 for (WindowInfo info : infos) { 932 // Adjust layer number because it should start from 0. 933 info.layer--; 934 } 935 936 onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); 937 938 final ArgumentCaptor<AccessibilityEvent> captor = 939 ArgumentCaptor.forClass(AccessibilityEvent.class); 940 verify(mMockA11yEventSender, times(1)) 941 .sendAccessibilityEventForCurrentUserLocked(captor.capture()); 942 assertThat(captor.getAllValues().get(0), 943 allOf(displayId(Display.DEFAULT_DISPLAY), 944 a11yWindowId(windowId), 945 a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_REMOVED))); 946 } 947 948 @Test sendAccessibilityEventOnWindowAddition()949 public void sendAccessibilityEventOnWindowAddition() throws RemoteException { 950 final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY); 951 952 for (WindowInfo info : infos) { 953 // Adjust layer number because new window will have 0 so that layer number in 954 // A11yWindowInfo in window won't be changed. 955 info.layer++; 956 } 957 958 final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, 959 false, USER_SYSTEM_ID); 960 addWindowInfo(infos, token, 0); 961 final int windowId = 962 getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, infos.size() - 1); 963 964 onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); 965 966 final ArgumentCaptor<AccessibilityEvent> captor = 967 ArgumentCaptor.forClass(AccessibilityEvent.class); 968 verify(mMockA11yEventSender, times(1)) 969 .sendAccessibilityEventForCurrentUserLocked(captor.capture()); 970 assertThat(captor.getAllValues().get(0), 971 allOf(displayId(Display.DEFAULT_DISPLAY), 972 a11yWindowId(windowId), 973 a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ADDED))); 974 } 975 976 @Test sendAccessibilityEventOnWindowChange()977 public void sendAccessibilityEventOnWindowChange() { 978 final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY); 979 infos.get(0).title = "new title"; 980 final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); 981 982 onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); 983 984 final ArgumentCaptor<AccessibilityEvent> captor = 985 ArgumentCaptor.forClass(AccessibilityEvent.class); 986 verify(mMockA11yEventSender, times(1)) 987 .sendAccessibilityEventForCurrentUserLocked(captor.capture()); 988 assertThat(captor.getAllValues().get(0), 989 allOf(displayId(Display.DEFAULT_DISPLAY), 990 a11yWindowId(windowId), 991 a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_TITLE))); 992 } 993 registerLeashedTokenAndWindowId()994 private void registerLeashedTokenAndWindowId() { 995 mA11yWindowManager.registerIdLocked(mMockHostToken, HOST_WINDOW_ID); 996 mA11yWindowManager.registerIdLocked(mMockEmbeddedToken, EMBEDDED_WINDOW_ID); 997 } 998 startTrackingPerDisplay(int displayId)999 private void startTrackingPerDisplay(int displayId) throws RemoteException { 1000 ArrayList<WindowInfo> windowInfosForDisplay = new ArrayList<>(); 1001 // Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy 1002 // mock window token into mA11yWindowTokens. Also, preparing WindowInfo mWindowInfos 1003 // for the test. 1004 int layer = 0; 1005 for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) { 1006 final IWindow token = addAccessibilityInteractionConnection(displayId, 1007 true, USER_SYSTEM_ID); 1008 addWindowInfo(windowInfosForDisplay, token, layer++); 1009 1010 } 1011 for (int i = 0; i < NUM_APP_WINDOWS; i++) { 1012 final IWindow token = addAccessibilityInteractionConnection(displayId, 1013 false, USER_SYSTEM_ID); 1014 addWindowInfo(windowInfosForDisplay, token, layer++); 1015 } 1016 // Sets up current focused window of display. 1017 // Each display has its own current focused window if config_perDisplayFocusEnabled is true. 1018 // Otherwise only default display needs to current focused window. 1019 if (mSupportPerDisplayFocus || displayId == Display.DEFAULT_DISPLAY) { 1020 windowInfosForDisplay.get(DEFAULT_FOCUSED_INDEX).focused = true; 1021 } 1022 // Turns on windows tracking, and update window info. 1023 mA11yWindowManager.startTrackingWindows(displayId, false); 1024 // Puts window lists into array. 1025 mWindowInfos.put(displayId, windowInfosForDisplay); 1026 // Sets the default display is the top focused display and 1027 // its current focused window is the top focused window. 1028 if (displayId == Display.DEFAULT_DISPLAY) { 1029 setTopFocusedWindowAndDisplay(displayId, DEFAULT_FOCUSED_INDEX); 1030 } 1031 // Invokes callback for sending window lists to A11y framework. 1032 onWindowsForAccessibilityChanged(displayId, FORCE_SEND); 1033 1034 assertEquals(mA11yWindowManager.getWindowListLocked(displayId).size(), 1035 windowInfosForDisplay.size()); 1036 } 1037 getWindowsForAccessibilityCallbacks(int displayId)1038 private WindowsForAccessibilityCallback getWindowsForAccessibilityCallbacks(int displayId) { 1039 ArgumentCaptor<WindowsForAccessibilityCallback> windowsForAccessibilityCallbacksCaptor = 1040 ArgumentCaptor.forClass( 1041 WindowManagerInternal.WindowsForAccessibilityCallback.class); 1042 verify(mMockWindowManagerInternal) 1043 .setWindowsForAccessibilityCallback(eq(displayId), 1044 windowsForAccessibilityCallbacksCaptor.capture()); 1045 return windowsForAccessibilityCallbacksCaptor.getValue(); 1046 } 1047 addAccessibilityInteractionConnection(int displayId, boolean bGlobal, int userId)1048 private IWindow addAccessibilityInteractionConnection(int displayId, boolean bGlobal, 1049 int userId) throws RemoteException { 1050 final IWindow mockWindowToken = Mockito.mock(IWindow.class); 1051 final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock( 1052 IAccessibilityInteractionConnection.class); 1053 final IBinder mockConnectionBinder = Mockito.mock(IBinder.class); 1054 final IBinder mockWindowBinder = Mockito.mock(IBinder.class); 1055 final IBinder mockLeashToken = Mockito.mock(IBinder.class); 1056 when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder); 1057 when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder); 1058 when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId)) 1059 .thenReturn(bGlobal); 1060 when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder())) 1061 .thenReturn(displayId); 1062 1063 int windowId = mA11yWindowManager.addAccessibilityInteractionConnection( 1064 mockWindowToken, mockLeashToken, mockA11yConnection, PACKAGE_NAME, userId); 1065 mA11yWindowTokens.put(windowId, mockWindowToken); 1066 return mockWindowToken; 1067 } 1068 addAccessibilityInteractionConnection(int displayId, boolean bGlobal, IBinder leashToken, int userId)1069 private int addAccessibilityInteractionConnection(int displayId, boolean bGlobal, 1070 IBinder leashToken, int userId) throws RemoteException { 1071 final IWindow mockWindowToken = Mockito.mock(IWindow.class); 1072 final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock( 1073 IAccessibilityInteractionConnection.class); 1074 final IBinder mockConnectionBinder = Mockito.mock(IBinder.class); 1075 final IBinder mockWindowBinder = Mockito.mock(IBinder.class); 1076 when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder); 1077 when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder); 1078 when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId)) 1079 .thenReturn(bGlobal); 1080 when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder())) 1081 .thenReturn(displayId); 1082 1083 int windowId = mA11yWindowManager.addAccessibilityInteractionConnection( 1084 mockWindowToken, leashToken, mockA11yConnection, PACKAGE_NAME, userId); 1085 mA11yWindowTokens.put(windowId, mockWindowToken); 1086 return windowId; 1087 } 1088 addWindowInfo(ArrayList<WindowInfo> windowInfos, IWindow windowToken, int layer)1089 private void addWindowInfo(ArrayList<WindowInfo> windowInfos, IWindow windowToken, int layer) { 1090 final WindowInfo windowInfo = WindowInfo.obtain(); 1091 windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION; 1092 windowInfo.token = windowToken.asBinder(); 1093 windowInfo.layer = layer; 1094 windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); 1095 windowInfos.add(windowInfo); 1096 } 1097 getWindowIdFromWindowInfosForDisplay(int displayId, int index)1098 private int getWindowIdFromWindowInfosForDisplay(int displayId, int index) { 1099 final IBinder windowToken = mWindowInfos.get(displayId).get(index).token; 1100 return mA11yWindowManager.findWindowIdLocked( 1101 USER_SYSTEM_ID, windowToken); 1102 } 1103 setTopFocusedWindowAndDisplay(int displayId, int index)1104 private void setTopFocusedWindowAndDisplay(int displayId, int index) { 1105 // Sets the top focus window. 1106 mTopFocusedWindowToken = mWindowInfos.get(displayId).get(index).token; 1107 // Sets the top focused display. 1108 mTopFocusedDisplayId = displayId; 1109 } 1110 onWindowsForAccessibilityChanged(int displayId, boolean forceSend)1111 private void onWindowsForAccessibilityChanged(int displayId, boolean forceSend) { 1112 WindowsForAccessibilityCallback callbacks = mCallbackOfWindows.get(displayId); 1113 if (callbacks == null) { 1114 callbacks = getWindowsForAccessibilityCallbacks(displayId); 1115 mCallbackOfWindows.put(displayId, callbacks); 1116 } 1117 callbacks.onWindowsForAccessibilityChanged(forceSend, mTopFocusedDisplayId, 1118 mTopFocusedWindowToken, mWindowInfos.get(displayId)); 1119 } 1120 changeFocusedWindowOnDisplayPerDisplayFocusConfig( int changeFocusedDisplayId, int newFocusedWindowIndex, int oldTopFocusedDisplayId, int oldFocusedWindowIndex)1121 private void changeFocusedWindowOnDisplayPerDisplayFocusConfig( 1122 int changeFocusedDisplayId, int newFocusedWindowIndex, int oldTopFocusedDisplayId, 1123 int oldFocusedWindowIndex) { 1124 if (mSupportPerDisplayFocus) { 1125 // Gets the old focused window of display which wants to change focused window. 1126 WindowInfo focusedWindowInfo = 1127 mWindowInfos.get(changeFocusedDisplayId).get(oldFocusedWindowIndex); 1128 // Resets the focus of old focused window. 1129 focusedWindowInfo.focused = false; 1130 // Gets the new window of display which wants to change focused window. 1131 focusedWindowInfo = 1132 mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex); 1133 // Sets the focus of new focused window. 1134 focusedWindowInfo.focused = true; 1135 } else { 1136 // Gets the window of display which wants to change focused window. 1137 WindowInfo focusedWindowInfo = 1138 mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex); 1139 // Sets the focus of new focused window. 1140 focusedWindowInfo.focused = true; 1141 // Gets the old focused window of old top focused display. 1142 focusedWindowInfo = 1143 mWindowInfos.get(oldTopFocusedDisplayId).get(oldFocusedWindowIndex); 1144 // Resets the focus of old focused window. 1145 focusedWindowInfo.focused = false; 1146 // Changes the top focused display and window. 1147 setTopFocusedWindowAndDisplay(changeFocusedDisplayId, newFocusedWindowIndex); 1148 } 1149 } 1150 1151 static class DisplayIdMatcher extends TypeSafeMatcher<AccessibilityEvent> { 1152 private final int mDisplayId; 1153 DisplayIdMatcher(int displayId)1154 DisplayIdMatcher(int displayId) { 1155 super(); 1156 mDisplayId = displayId; 1157 } 1158 displayId(int displayId)1159 static DisplayIdMatcher displayId(int displayId) { 1160 return new DisplayIdMatcher(displayId); 1161 } 1162 1163 @Override matchesSafely(AccessibilityEvent event)1164 protected boolean matchesSafely(AccessibilityEvent event) { 1165 return event.getDisplayId() == mDisplayId; 1166 } 1167 1168 @Override describeTo(Description description)1169 public void describeTo(Description description) { 1170 description.appendText("Matching to displayId " + mDisplayId); 1171 } 1172 } 1173 1174 static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> { 1175 private int mWindowId; 1176 WindowIdMatcher(int windowId)1177 WindowIdMatcher(int windowId) { 1178 super(); 1179 mWindowId = windowId; 1180 } 1181 a11yWindowId(int windowId)1182 static WindowIdMatcher a11yWindowId(int windowId) { 1183 return new WindowIdMatcher(windowId); 1184 } 1185 1186 @Override matchesSafely(AccessibilityEvent event)1187 protected boolean matchesSafely(AccessibilityEvent event) { 1188 return event.getWindowId() == mWindowId; 1189 } 1190 1191 @Override describeTo(Description description)1192 public void describeTo(Description description) { 1193 description.appendText("Matching to windowId " + mWindowId); 1194 } 1195 } 1196 1197 static class WindowChangesMatcher extends TypeSafeMatcher<AccessibilityEvent> { 1198 private int mWindowChanges; 1199 WindowChangesMatcher(int windowChanges)1200 WindowChangesMatcher(int windowChanges) { 1201 super(); 1202 mWindowChanges = windowChanges; 1203 } 1204 a11yWindowChanges(int windowChanges)1205 static WindowChangesMatcher a11yWindowChanges(int windowChanges) { 1206 return new WindowChangesMatcher(windowChanges); 1207 } 1208 1209 @Override matchesSafely(AccessibilityEvent event)1210 protected boolean matchesSafely(AccessibilityEvent event) { 1211 return event.getWindowChanges() == mWindowChanges; 1212 } 1213 1214 @Override describeTo(Description description)1215 public void describeTo(Description description) { 1216 description.appendText("Matching to window changes " + mWindowChanges); 1217 } 1218 } 1219 } 1220