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