• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.accessibility;
18 
19 import static android.accessibilityservice.AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY;
20 import static android.accessibilityservice.AccessibilityService.ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS;
21 import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_HOME;
22 import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_STATUS;
23 import static android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_CONTROL_MAGNIFICATION;
24 import static android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES;
25 import static android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
26 import static android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION;
27 import static android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT;
28 import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
29 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_HAPTIC;
30 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_SPOKEN;
31 import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
32 import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS;
33 import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
34 import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS;
35 import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES;
36 import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
37 import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
38 import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
39 import static android.view.View.FOCUS_DOWN;
40 import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_CLICKED;
41 import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_LONG_CLICKED;
42 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
43 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
44 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
45 import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
46 import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_INPUT;
47 import static android.view.accessibility.AccessibilityNodeInfo.ROOT_NODE_ID;
48 
49 import static com.google.common.truth.Truth.assertThat;
50 import static com.google.common.truth.Truth.assertWithMessage;
51 
52 import static org.junit.Assert.assertEquals;
53 import static org.junit.Assert.assertFalse;
54 import static org.junit.Assert.assertTrue;
55 import static org.mockito.ArgumentMatchers.any;
56 import static org.mockito.ArgumentMatchers.anyBoolean;
57 import static org.mockito.ArgumentMatchers.anyInt;
58 import static org.mockito.ArgumentMatchers.anyLong;
59 import static org.mockito.ArgumentMatchers.eq;
60 import static org.mockito.ArgumentMatchers.nullable;
61 import static org.mockito.Mockito.doAnswer;
62 import static org.mockito.Mockito.doReturn;
63 import static org.mockito.Mockito.mock;
64 import static org.mockito.Mockito.never;
65 import static org.mockito.Mockito.verify;
66 import static org.mockito.Mockito.verifyNoMoreInteractions;
67 import static org.mockito.Mockito.when;
68 
69 import android.accessibilityservice.AccessibilityService;
70 import android.accessibilityservice.AccessibilityServiceInfo;
71 import android.accessibilityservice.AccessibilityTrace;
72 import android.accessibilityservice.IAccessibilityServiceClient;
73 import android.accessibilityservice.MagnificationConfig;
74 import android.content.ComponentName;
75 import android.content.Context;
76 import android.content.pm.ApplicationInfo;
77 import android.content.pm.PackageManager;
78 import android.content.pm.ResolveInfo;
79 import android.content.pm.ServiceInfo;
80 import android.graphics.Region;
81 import android.hardware.display.DisplayManager;
82 import android.os.Build;
83 import android.os.Handler;
84 import android.os.IBinder;
85 import android.os.IPowerManager;
86 import android.os.IThermalService;
87 import android.os.PowerManager;
88 import android.os.Process;
89 import android.os.RemoteCallback;
90 import android.os.RemoteException;
91 import android.os.test.FakePermissionEnforcer;
92 import android.platform.test.annotations.EnableFlags;
93 import android.platform.test.flag.junit.SetFlagsRule;
94 import android.util.Pair;
95 import android.view.Display;
96 import android.view.KeyEvent;
97 import android.view.MagnificationSpec;
98 import android.view.SurfaceControl;
99 import android.view.WindowManager;
100 import android.view.accessibility.AccessibilityNodeInfo;
101 import android.view.accessibility.AccessibilityWindowInfo;
102 import android.view.accessibility.IAccessibilityInteractionConnection;
103 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
104 import android.view.accessibility.IWindowSurfaceInfoCallback;
105 import android.window.ScreenCapture;
106 
107 import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
108 import com.android.server.accessibility.magnification.MagnificationProcessor;
109 import com.android.server.accessibility.test.MessageCapturingHandler;
110 import com.android.server.wm.WindowManagerInternal;
111 
112 import org.junit.Before;
113 import org.junit.Rule;
114 import org.junit.Test;
115 import org.mockito.ArgumentCaptor;
116 import org.mockito.Mock;
117 import org.mockito.Mockito;
118 import org.mockito.MockitoAnnotations;
119 import org.mockito.Spy;
120 
121 import java.util.ArrayList;
122 import java.util.Arrays;
123 import java.util.List;
124 import java.util.concurrent.Callable;
125 
126 /**
127  * Tests for the AbstractAccessibilityServiceConnection
128  */
129 public class AbstractAccessibilityServiceConnectionTest {
130 
131     @Rule
132     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
133 
134     private static final ComponentName COMPONENT_NAME = new ComponentName(
135             "com.android.server.accessibility", ".AbstractAccessibilityServiceConnectionTest");
136     private static final String PACKAGE_NAME1 = "com.android.server.accessibility1";
137     private static final String PACKAGE_NAME2 = "com.android.server.accessibility2";
138     private static final String VIEWID_RESOURCE_NAME = "test_viewid_resource_name";
139     private static final String VIEW_TEXT = "test_view_text";
140     private static final int WINDOWID = 12;
141     private static final int PIP_WINDOWID = 13;
142     private static final int WINDOWID_ONSECONDDISPLAY = 14;
143     private static final int SECONDARY_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1;
144     private static final int SERVICE_ID = 42;
145     private static final int A11Y_SERVICE_CAPABILITY = CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
146             | CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
147             | CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS
148             | CAPABILITY_CAN_CONTROL_MAGNIFICATION
149             | CAPABILITY_CAN_PERFORM_GESTURES;
150     private static final int A11Y_SERVICE_FLAG = DEFAULT
151             | FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
152             | FLAG_REPORT_VIEW_IDS
153             | FLAG_REQUEST_TOUCH_EXPLORATION_MODE
154             | FLAG_REQUEST_FILTER_KEY_EVENTS
155             | FLAG_REQUEST_FINGERPRINT_GESTURES
156             | FLAG_REQUEST_ACCESSIBILITY_BUTTON
157             | FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
158     private static final int USER_ID = 1;
159     private static final int USER_ID2 = 2;
160     private static final int INTERACTION_ID = 199;
161     private static final Pair<float[], MagnificationSpec> FAKE_MATRIX_AND_MAG_SPEC =
162             new Pair<>(new float[9], new MagnificationSpec());
163     private static final int PID = Process.myPid();
164     private static final long TID = Process.myTid();
165     private static final int UID = Process.myUid();
166 
167     private AbstractAccessibilityServiceConnection mServiceConnection;
168     private MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
169     private final List<AccessibilityWindowInfo> mA11yWindowInfos = new ArrayList<>();
170     private final List<AccessibilityWindowInfo> mA11yWindowInfosOnSecondDisplay = new ArrayList<>();
171     private Callable[] mFindA11yNodesFunctions;
172     private Callable<Boolean> mPerformA11yAction;
173     private ArrayList<Integer> mDisplayList = new ArrayList<>(Arrays.asList(
174             Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID));
175 
176     @Mock private Context mMockContext;
177     @Mock private IPowerManager mMockIPowerManager;
178     @Mock private IThermalService mMockIThermalService;
179     @Mock private PackageManager mMockPackageManager;
180     @Spy  private AccessibilityServiceInfo mSpyServiceInfo = new AccessibilityServiceInfo();
181     @Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
182     @Mock private AccessibilityWindowManager mMockA11yWindowManager;
183     @Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
184     @Mock private AccessibilityTrace mMockA11yTrace;
185     @Mock private WindowManagerInternal mMockWindowManagerInternal;
186     @Mock private SystemActionPerformer mMockSystemActionPerformer;
187     @Mock private IBinder mMockClientBinder;
188     @Mock private IAccessibilityServiceClient mMockClient;
189     @Mock private KeyEventDispatcher mMockKeyEventDispatcher;
190     @Mock private IAccessibilityInteractionConnection mMockIA11yInteractionConnection;
191     @Mock private IAccessibilityInteractionConnectionCallback mMockCallback;
192     @Mock private FingerprintGestureDispatcher mMockFingerprintGestureDispatcher;
193     @Mock private MagnificationProcessor mMockMagnificationProcessor;
194     @Mock private RemoteCallback.OnResultListener mMockListener;
195     FakePermissionEnforcer mFakePermissionEnforcer = new FakePermissionEnforcer();
196 
197     @Before
setup()198     public void setup() {
199         MockitoAnnotations.initMocks(this);
200 
201         when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID);
202         when(mMockSystemSupport.getKeyEventDispatcher()).thenReturn(mMockKeyEventDispatcher);
203         when(mMockSystemSupport.getFingerprintGestureDispatcher())
204                 .thenReturn(mMockFingerprintGestureDispatcher);
205         when(mMockSystemSupport.getMagnificationProcessor())
206                 .thenReturn(mMockMagnificationProcessor);
207         when(mMockSystemSupport.getWindowTransformationMatrixAndMagnificationSpec(anyInt()))
208                 .thenReturn(FAKE_MATRIX_AND_MAG_SPEC);
209 
210         PowerManager powerManager =
211                 new PowerManager(mMockContext, mMockIPowerManager, mMockIThermalService, mHandler);
212         when(mMockContext.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
213         when(mMockContext.getSystemService(Context.PERMISSION_ENFORCER_SERVICE))
214                 .thenReturn(mFakePermissionEnforcer);
215         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
216         when(mMockPackageManager.hasSystemFeature(FEATURE_FINGERPRINT)).thenReturn(true);
217 
218         when(mMockA11yTrace.isA11yTracingEnabled()).thenReturn(false);
219         // Fake a11yWindowInfo and remote a11y connection for tests.
220         addA11yWindowInfo(mA11yWindowInfos, WINDOWID, false, Display.DEFAULT_DISPLAY);
221         addA11yWindowInfo(mA11yWindowInfos, PIP_WINDOWID, true, Display.DEFAULT_DISPLAY);
222         addA11yWindowInfo(mA11yWindowInfosOnSecondDisplay, WINDOWID_ONSECONDDISPLAY, false,
223                 SECONDARY_DISPLAY_ID);
224         when(mMockA11yWindowManager.getDisplayListLocked(anyInt())).thenReturn(mDisplayList);
225         when(mMockA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY))
226                 .thenReturn(mA11yWindowInfos);
227         when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID))
228                 .thenReturn(mA11yWindowInfos.get(0));
229         when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(PIP_WINDOWID))
230                 .thenReturn(mA11yWindowInfos.get(1));
231         when(mMockA11yWindowManager.getDisplayIdByUserIdAndWindowId(USER_ID,
232             WINDOWID_ONSECONDDISPLAY)).thenReturn(SECONDARY_DISPLAY_ID);
233         when(mMockA11yWindowManager.getWindowListLocked(SECONDARY_DISPLAY_ID))
234             .thenReturn(mA11yWindowInfosOnSecondDisplay);
235         when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID_ONSECONDDISPLAY))
236             .thenReturn(mA11yWindowInfosOnSecondDisplay.get(0));
237         final RemoteAccessibilityConnection conn = getRemoteA11yConnection(
238                 WINDOWID, mMockIA11yInteractionConnection, PACKAGE_NAME1);
239         final RemoteAccessibilityConnection connPip = getRemoteA11yConnection(
240                 PIP_WINDOWID, mMockIA11yInteractionConnection, PACKAGE_NAME2);
241         when(mMockA11yWindowManager.getConnectionLocked(USER_ID, WINDOWID)).thenReturn(conn);
242         when(mMockA11yWindowManager.getConnectionLocked(USER_ID, PIP_WINDOWID)).thenReturn(connPip);
243         when(mMockA11yWindowManager.getPictureInPictureActionReplacingConnection())
244                 .thenReturn(connPip);
245 
246         // Update a11yServiceInfo to full capability, full flags and target sdk jelly bean
247         final ResolveInfo mockResolveInfo = mock(ResolveInfo.class);
248         mockResolveInfo.serviceInfo = mock(ServiceInfo.class);
249         mockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class);
250         mockResolveInfo.serviceInfo.applicationInfo.targetSdkVersion =
251                 Build.VERSION_CODES.JELLY_BEAN;
252         doReturn(mockResolveInfo).when(mSpyServiceInfo).getResolveInfo();
253         mSpyServiceInfo.setCapabilities(A11Y_SERVICE_CAPABILITY);
254         updateServiceInfo(mSpyServiceInfo, 0, 0, A11Y_SERVICE_FLAG, null, 0);
255 
256         mServiceConnection = new TestAccessibilityServiceConnection(mMockContext, COMPONENT_NAME,
257                 mSpyServiceInfo, SERVICE_ID, mHandler, new Object(), mMockSecurityPolicy,
258                 mMockSystemSupport, mMockA11yTrace, mMockWindowManagerInternal,
259                 mMockSystemActionPerformer, mMockA11yWindowManager);
260         // Assume that the client is connected
261         mServiceConnection.mClientBinder = mMockClientBinder;
262         mServiceConnection.mClient = mMockClient;
263 
264         // Update security policy for this service
265         when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(true);
266         when(mMockSecurityPolicy.canRetrieveWindowsLocked(mServiceConnection)).thenReturn(true);
267         when(mMockSecurityPolicy.canGetAccessibilityNodeInfoLocked(
268                 eq(USER_ID), eq(mServiceConnection), anyInt())).thenReturn(true);
269         when(mMockSecurityPolicy.canControlMagnification(mServiceConnection)).thenReturn(true);
270 
271         // init test functions for accessAccessibilityNodeInfo test case.
272         initTestFunctions();
273     }
274 
275     @Test
getCapabilities()276     public void getCapabilities() {
277         assertThat(mServiceConnection.getCapabilities()).isEqualTo(A11Y_SERVICE_CAPABILITY);
278     }
279 
280     @Test
onKeyEvent()281     public void onKeyEvent() throws RemoteException {
282         final int sequenceNumber = 100;
283         final KeyEvent mockKeyEvent = mock(KeyEvent.class);
284 
285         mServiceConnection.onKeyEvent(mockKeyEvent, sequenceNumber);
286         verify(mMockClient).onKeyEvent(mockKeyEvent, sequenceNumber);
287     }
288 
289     @Test
setServiceInfo_invokeOnClientChange()290     public void setServiceInfo_invokeOnClientChange() {
291         final AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo();
292         updateServiceInfo(serviceInfo,
293                 TYPE_VIEW_CLICKED | TYPE_VIEW_LONG_CLICKED,
294                 FEEDBACK_SPOKEN | FEEDBACK_HAPTIC,
295                 A11Y_SERVICE_FLAG,
296                 new String[] {PACKAGE_NAME1, PACKAGE_NAME2},
297                 1000);
298 
299         mServiceConnection.setServiceInfo(serviceInfo);
300         verify(mMockSystemSupport).onClientChangeLocked(true);
301     }
302 
303     @Test
setServiceInfo_ChangePackageNames_updateSuccess()304     public void setServiceInfo_ChangePackageNames_updateSuccess() {
305         assertTrue(mServiceConnection.mPackageNames.isEmpty());
306 
307         final AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo();
308         updateServiceInfo(serviceInfo, 0, 0, A11Y_SERVICE_FLAG,
309                 new String[] {PACKAGE_NAME1, PACKAGE_NAME2},
310                 1000);
311 
312         mServiceConnection.setServiceInfo(serviceInfo);
313         assertEquals(serviceInfo.packageNames.length, mServiceConnection.mPackageNames.size());
314         assertTrue(mServiceConnection.mPackageNames.containsAll(
315                 Arrays.asList(mServiceConnection.getServiceInfo().packageNames)));
316 
317         updateServiceInfo(serviceInfo, 0, 0, A11Y_SERVICE_FLAG, null, 1000);
318         mServiceConnection.setServiceInfo(serviceInfo);
319         assertTrue(mServiceConnection.mPackageNames.isEmpty());
320     }
321 
322     @Test
setServiceInfo_ChangeAccessibilityTool_updateFails()323     public void setServiceInfo_ChangeAccessibilityTool_updateFails() {
324         assertFalse(mSpyServiceInfo.isAccessibilityTool());
325 
326         final AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo();
327         serviceInfo.setAccessibilityTool(true);
328         mServiceConnection.setServiceInfo(serviceInfo);
329 
330         // isAccessibilityTool should not be dynamically updatable
331         assertFalse(mSpyServiceInfo.isAccessibilityTool());
332     }
333 
334     @Test
canReceiveEvents_hasEventType_returnTrue()335     public void canReceiveEvents_hasEventType_returnTrue() {
336         final AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo();
337         updateServiceInfo(serviceInfo,
338                 TYPE_VIEW_CLICKED | TYPE_VIEW_LONG_CLICKED, 0,
339                 0, null, 0);
340 
341         mServiceConnection.setServiceInfo(serviceInfo);
342         assertThat(mServiceConnection.canReceiveEventsLocked()).isTrue();
343     }
344 
345     @Test
setOnKeyEventResult()346     public void setOnKeyEventResult() {
347         final int sequenceNumber = 100;
348         final boolean handled = true;
349         mServiceConnection.setOnKeyEventResult(handled, sequenceNumber);
350 
351         verify(mMockKeyEventDispatcher).setOnKeyEventResult(
352                 mServiceConnection, handled, sequenceNumber);
353     }
354 
355     @Test
getWindows()356     public void getWindows() {
357         final AccessibilityWindowInfo.WindowListSparseArray allWindows =
358                 mServiceConnection.getWindows();
359 
360         assertEquals(2, allWindows.size());
361         assertThat(allWindows.get(Display.DEFAULT_DISPLAY))
362                 .containsExactlyElementsIn(mA11yWindowInfos);
363         assertEquals(2, allWindows.get(Display.DEFAULT_DISPLAY).size());
364         assertThat(allWindows.get(SECONDARY_DISPLAY_ID))
365                 .containsExactlyElementsIn(mA11yWindowInfosOnSecondDisplay);
366         assertEquals(1, allWindows.get(SECONDARY_DISPLAY_ID).size());
367     }
368 
369     @Test
getWindows_returnNull()370     public void getWindows_returnNull() {
371         // no canRetrieveWindows, should return null
372         when(mMockSecurityPolicy.canRetrieveWindowsLocked(mServiceConnection)).thenReturn(false);
373         assertThat(mServiceConnection.getWindows()).isNull();
374 
375         // no checkAccessibilityAccess, should return null
376         when(mMockSecurityPolicy.canRetrieveWindowsLocked(mServiceConnection)).thenReturn(true);
377         when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(false);
378         assertThat(mServiceConnection.getWindows()).isNull();
379     }
380 
381     @Test
getWindows_notTrackingWindows_invokeOnClientChange()382     public void getWindows_notTrackingWindows_invokeOnClientChange() {
383         when(mMockA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY)).thenReturn(null);
384         when(mMockA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY))
385                 .thenReturn(false);
386 
387         mServiceConnection.getWindows();
388         verify(mMockSystemSupport).onClientChangeLocked(false);
389     }
390 
391     @Test
getWindow()392     public void getWindow() {
393         assertThat(mServiceConnection.getWindow(WINDOWID)).isEqualTo(mA11yWindowInfos.get(0));
394     }
395 
396     @Test
getWindow_returnNull()397     public void getWindow_returnNull() {
398         // no canRetrieveWindows, should return null
399         when(mMockSecurityPolicy.canRetrieveWindowsLocked(mServiceConnection)).thenReturn(false);
400         assertThat(mServiceConnection.getWindow(WINDOWID)).isNull();
401 
402         // no checkAccessibilityAccess, should return null
403         when(mMockSecurityPolicy.canRetrieveWindowsLocked(mServiceConnection)).thenReturn(true);
404         when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(false);
405         assertThat(mServiceConnection.getWindow(WINDOWID)).isNull();
406     }
407 
408     @Test
getWindow_notTrackingWindows_invokeOnClientChange()409     public void getWindow_notTrackingWindows_invokeOnClientChange() {
410         when(mMockA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY)).thenReturn(null);
411         when(mMockA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY))
412                 .thenReturn(false);
413 
414         mServiceConnection.getWindow(WINDOWID);
415         verify(mMockSystemSupport).onClientChangeLocked(false);
416     }
417 
418     @Test
getWindow_onNonDefaultDisplay()419     public void getWindow_onNonDefaultDisplay() {
420         assertThat(mServiceConnection.getWindow(WINDOWID_ONSECONDDISPLAY))
421                 .isEqualTo(mA11yWindowInfosOnSecondDisplay.get(0));
422     }
423 
424     @Test
accessAccessibilityNodeInfo_whenCantGetInfo_returnNullOrFalse()425     public void accessAccessibilityNodeInfo_whenCantGetInfo_returnNullOrFalse()
426             throws Exception {
427         when(mMockSecurityPolicy.canGetAccessibilityNodeInfoLocked(
428                 USER_ID, mServiceConnection, WINDOWID)).thenReturn(false);
429         for (int i = 0; i < mFindA11yNodesFunctions.length; i++) {
430             assertThat(mFindA11yNodesFunctions[i].call()).isNull();
431         }
432         assertThat(mPerformA11yAction.call()).isFalse();
433 
434         verifyNoMoreInteractions(mMockIA11yInteractionConnection);
435         verify(mMockSecurityPolicy, never()).computeValidReportedPackages(any(), anyInt());
436     }
437 
438     @Test
accessAccessibilityNodeInfo_whenNoA11yAccess_returnNullOrFalse()439     public void accessAccessibilityNodeInfo_whenNoA11yAccess_returnNullOrFalse()
440             throws Exception {
441         when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(false);
442         for (int i = 0; i < mFindA11yNodesFunctions.length; i++) {
443             assertThat(mFindA11yNodesFunctions[i].call()).isNull();
444         }
445         assertThat(mPerformA11yAction.call()).isFalse();
446 
447         verifyNoMoreInteractions(mMockIA11yInteractionConnection);
448         verify(mMockSecurityPolicy, never()).computeValidReportedPackages(any(), anyInt());
449     }
450 
451     @Test
accessAccessibilityNodeInfo_whenNoRemoteA11yConnection_returnNullOrFalse()452     public void accessAccessibilityNodeInfo_whenNoRemoteA11yConnection_returnNullOrFalse()
453             throws Exception {
454         when(mMockA11yWindowManager.getConnectionLocked(USER_ID, WINDOWID)).thenReturn(null);
455         for (int i = 0; i < mFindA11yNodesFunctions.length; i++) {
456             assertThat(mFindA11yNodesFunctions[i].call()).isNull();
457         }
458         assertThat(mPerformA11yAction.call()).isFalse();
459 
460         verifyNoMoreInteractions(mMockIA11yInteractionConnection);
461         verify(mMockSecurityPolicy, never()).computeValidReportedPackages(any(), anyInt());
462     }
463 
464     @Test
findAccessibilityNodeInfosByViewId_withPipWindow_shouldReplaceCallback()465     public void findAccessibilityNodeInfosByViewId_withPipWindow_shouldReplaceCallback()
466             throws RemoteException {
467         final ArgumentCaptor<IAccessibilityInteractionConnectionCallback> captor =
468                 ArgumentCaptor.forClass(IAccessibilityInteractionConnectionCallback.class);
469         mServiceConnection.findAccessibilityNodeInfosByViewId(PIP_WINDOWID, ROOT_NODE_ID,
470                 VIEWID_RESOURCE_NAME, INTERACTION_ID, mMockCallback, TID);
471         verify(mMockIA11yInteractionConnection).findAccessibilityNodeInfosByViewId(
472                 eq(ROOT_NODE_ID), eq(VIEWID_RESOURCE_NAME), any(), eq(INTERACTION_ID),
473                 captor.capture(), anyInt(), eq(PID), eq(TID), any(), nullable(float[].class));
474         verify(mMockSecurityPolicy).computeValidReportedPackages(any(), anyInt());
475         verifyReplaceActions(captor.getValue());
476     }
477 
478     @Test
findAccessibilityNodeInfosByText_withPipWindow_shouldReplaceCallback()479     public void findAccessibilityNodeInfosByText_withPipWindow_shouldReplaceCallback()
480             throws RemoteException {
481         final ArgumentCaptor<IAccessibilityInteractionConnectionCallback> captor =
482                 ArgumentCaptor.forClass(IAccessibilityInteractionConnectionCallback.class);
483         mServiceConnection.findAccessibilityNodeInfosByText(PIP_WINDOWID, ROOT_NODE_ID,
484                 VIEW_TEXT, INTERACTION_ID, mMockCallback, TID);
485         verify(mMockIA11yInteractionConnection).findAccessibilityNodeInfosByText(
486                 eq(ROOT_NODE_ID), eq(VIEW_TEXT), any(), eq(INTERACTION_ID),
487                 captor.capture(), anyInt(), eq(PID), eq(TID), any(), nullable(float[].class));
488         verify(mMockSecurityPolicy).computeValidReportedPackages(any(), anyInt());
489         verifyReplaceActions(captor.getValue());
490     }
491 
492     @Test
findAccessibilityNodeInfoByAccessibilityId_withPipWindow_shouldReplaceCallback()493     public void findAccessibilityNodeInfoByAccessibilityId_withPipWindow_shouldReplaceCallback()
494             throws RemoteException {
495         final ArgumentCaptor<IAccessibilityInteractionConnectionCallback> captor =
496                 ArgumentCaptor.forClass(IAccessibilityInteractionConnectionCallback.class);
497         mServiceConnection.findAccessibilityNodeInfoByAccessibilityId(PIP_WINDOWID, ROOT_NODE_ID,
498                 INTERACTION_ID, mMockCallback, 0, TID, null);
499         verify(mMockIA11yInteractionConnection).findAccessibilityNodeInfoByAccessibilityId(
500                 eq(ROOT_NODE_ID), any(), eq(INTERACTION_ID), captor.capture(), anyInt(),
501                 eq(PID), eq(TID), any(),  nullable(float[].class), any());
502         verify(mMockSecurityPolicy).computeValidReportedPackages(any(), anyInt());
503         verifyReplaceActions(captor.getValue());
504     }
505 
506     @Test
findFocus_withPipWindow_shouldReplaceCallback()507     public void findFocus_withPipWindow_shouldReplaceCallback()
508             throws RemoteException {
509         final ArgumentCaptor<IAccessibilityInteractionConnectionCallback> captor =
510                 ArgumentCaptor.forClass(IAccessibilityInteractionConnectionCallback.class);
511         mServiceConnection.findFocus(PIP_WINDOWID, ROOT_NODE_ID, FOCUS_INPUT, INTERACTION_ID,
512                 mMockCallback, TID);
513         verify(mMockIA11yInteractionConnection).findFocus(eq(ROOT_NODE_ID), eq(FOCUS_INPUT),
514                 any(), eq(INTERACTION_ID), captor.capture(), anyInt(), eq(PID), eq(TID), any(),
515                 nullable(float[].class));
516         verify(mMockSecurityPolicy).computeValidReportedPackages(any(), anyInt());
517         verifyReplaceActions(captor.getValue());
518     }
519 
520     @Test
focusSearch_withPipWindow_shouldReplaceCallback()521     public void focusSearch_withPipWindow_shouldReplaceCallback()
522             throws RemoteException {
523         final ArgumentCaptor<IAccessibilityInteractionConnectionCallback> captor =
524                 ArgumentCaptor.forClass(IAccessibilityInteractionConnectionCallback.class);
525         mServiceConnection.focusSearch(PIP_WINDOWID, ROOT_NODE_ID, FOCUS_DOWN, INTERACTION_ID,
526                 mMockCallback, TID);
527         verify(mMockIA11yInteractionConnection).focusSearch(eq(ROOT_NODE_ID), eq(FOCUS_DOWN),
528                 any(), eq(INTERACTION_ID), captor.capture(), anyInt(), eq(PID), eq(TID), any(),
529                 nullable(float[].class));
530         verify(mMockSecurityPolicy).computeValidReportedPackages(any(), anyInt());
531         verifyReplaceActions(captor.getValue());
532     }
533 
534     @Test
performAccessibilityAction_withPipWindow_invokeGetPipReplacingConnection()535     public void performAccessibilityAction_withPipWindow_invokeGetPipReplacingConnection()
536             throws RemoteException {
537         mServiceConnection.performAccessibilityAction(PIP_WINDOWID, ROOT_NODE_ID,
538                 ACTION_ACCESSIBILITY_FOCUS, null, INTERACTION_ID, mMockCallback, TID);
539 
540         verify(mMockIPowerManager).userActivity(eq(Display.DEFAULT_DISPLAY), anyLong(), anyInt(),
541                 anyInt());
542         verify(mMockIA11yInteractionConnection).performAccessibilityAction(eq(ROOT_NODE_ID),
543                 eq(ACTION_ACCESSIBILITY_FOCUS), any(), eq(INTERACTION_ID), eq(mMockCallback),
544                 anyInt(), eq(PID), eq(TID));
545         verify(mMockA11yWindowManager).getPictureInPictureActionReplacingConnection();
546     }
547 
548     @Test
performAccessibilityAction_withClick_shouldNotifyOutsideTouch()549     public void performAccessibilityAction_withClick_shouldNotifyOutsideTouch()
550             throws RemoteException {
551         mServiceConnection.performAccessibilityAction(WINDOWID, ROOT_NODE_ID,
552                 ACTION_CLICK, null, INTERACTION_ID, mMockCallback, TID);
553         mServiceConnection.performAccessibilityAction(PIP_WINDOWID, ROOT_NODE_ID,
554                 ACTION_LONG_CLICK, null, INTERACTION_ID, mMockCallback, TID);
555         verify(mMockA11yWindowManager).notifyOutsideTouch(eq(USER_ID), eq(WINDOWID));
556         verify(mMockA11yWindowManager).notifyOutsideTouch(eq(USER_ID), eq(PIP_WINDOWID));
557     }
558 
559     @Test
performGlobalAction()560     public void performGlobalAction() {
561         mServiceConnection.performGlobalAction(GLOBAL_ACTION_HOME);
562         verify(mMockSystemActionPerformer).performSystemAction(GLOBAL_ACTION_HOME);
563     }
564 
565     @Test
getSystemActions()566     public void getSystemActions() {
567         List<AccessibilityNodeInfo.AccessibilityAction> actions =
568                 mServiceConnection.getSystemActions();
569         verify(mMockSystemActionPerformer).getSystemActions();
570     }
571 
572     @Test
isFingerprintGestureDetectionAvailable_hasFingerPrintSupport_returnTrue()573     public void isFingerprintGestureDetectionAvailable_hasFingerPrintSupport_returnTrue() {
574         when(mMockFingerprintGestureDispatcher.isFingerprintGestureDetectionAvailable())
575                 .thenReturn(true);
576         final boolean result = mServiceConnection.isFingerprintGestureDetectionAvailable();
577         assertThat(result).isTrue();
578     }
579 
580     @Test
isFingerprintGestureDetectionAvailable_noFingerPrintSupport_returnFalse()581     public void isFingerprintGestureDetectionAvailable_noFingerPrintSupport_returnFalse() {
582         when(mMockFingerprintGestureDispatcher.isFingerprintGestureDetectionAvailable())
583                 .thenReturn(true);
584 
585         // Return false if device does not support fingerprint
586         when(mMockPackageManager.hasSystemFeature(FEATURE_FINGERPRINT)).thenReturn(false);
587         boolean result = mServiceConnection.isFingerprintGestureDetectionAvailable();
588         assertThat(result).isFalse();
589 
590         // Return false if service does not have flag
591         when(mMockPackageManager.hasSystemFeature(FEATURE_FINGERPRINT)).thenReturn(true);
592         mSpyServiceInfo.flags = A11Y_SERVICE_FLAG & ~FLAG_REQUEST_FINGERPRINT_GESTURES;
593         mServiceConnection.setServiceInfo(mSpyServiceInfo);
594         result = mServiceConnection.isFingerprintGestureDetectionAvailable();
595         assertThat(result).isFalse();
596     }
597 
598     @Test
getMagnificationScale()599     public void getMagnificationScale() {
600         final int displayId = 1;
601         final float scale = 2.0f;
602         when(mMockMagnificationProcessor.getScale(displayId)).thenReturn(scale);
603 
604         final float result = mServiceConnection.getMagnificationScale(displayId);
605         assertThat(result).isEqualTo(scale);
606     }
607 
608     @Test
getMagnificationScale_serviceNotBelongCurrentUser_returnNoScale()609     public void getMagnificationScale_serviceNotBelongCurrentUser_returnNoScale() {
610         final int displayId = 1;
611         final float scale = 2.0f;
612         when(mMockMagnificationProcessor.getScale(displayId)).thenReturn(scale);
613         when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
614 
615         final float result = mServiceConnection.getMagnificationScale(displayId);
616         assertThat(result).isEqualTo(1.0f);
617     }
618 
619     @Test
getMagnificationRegion_serviceNotBelongCurrentUser_returnEmptyRegion()620     public void getMagnificationRegion_serviceNotBelongCurrentUser_returnEmptyRegion() {
621         final int displayId = 1;
622         final Region region = new Region(10, 20, 100, 200);
623         doAnswer((invocation) -> {
624             ((Region) invocation.getArguments()[1]).set(region);
625             return null;
626         }).when(mMockMagnificationProcessor).getFullscreenMagnificationRegion(eq(displayId), any(),
627                 anyBoolean());
628         when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
629 
630         final Region result = mServiceConnection.getMagnificationRegion(displayId);
631         assertWithMessage("Non-empty region: " + result).that(result.isEmpty()).isTrue();
632     }
633 
634     @Test
getCurrentMagnificationRegion_returnRegion()635     public void getCurrentMagnificationRegion_returnRegion() {
636         final int displayId = 1;
637         final Region region = new Region(10, 20, 100, 200);
638         doAnswer((invocation) -> {
639             ((Region) invocation.getArguments()[1]).set(region);
640             return null;
641         }).when(mMockMagnificationProcessor).getCurrentMagnificationRegion(eq(displayId), any(),
642                 anyBoolean());
643 
644         final Region result = mServiceConnection.getCurrentMagnificationRegion(displayId);
645         assertEquals(result, region);
646     }
647 
648     @Test
getMagnificationCenterX_serviceNotBelongCurrentUser_returnZero()649     public void getMagnificationCenterX_serviceNotBelongCurrentUser_returnZero() {
650         final int displayId = 1;
651         final float centerX = 480.0f;
652         when(mMockMagnificationProcessor.getCenterX(displayId, /* canControlMagnification= */
653                 true)).thenReturn(centerX);
654         when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
655 
656         final float result = mServiceConnection.getMagnificationCenterX(displayId);
657         assertThat(result).isEqualTo(0.0f);
658     }
659 
660     @Test
getMagnificationCenterY_serviceNotBelongCurrentUser_returnZero()661     public void getMagnificationCenterY_serviceNotBelongCurrentUser_returnZero() {
662         final int displayId = 1;
663         final float centerY = 640.0f;
664         when(mMockMagnificationProcessor.getCenterY(displayId, /* canControlMagnification= */
665                 true)).thenReturn(centerY);
666         when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
667 
668         final float result = mServiceConnection.getMagnificationCenterY(displayId);
669         assertThat(result).isEqualTo(0.0f);
670     }
671 
672     @Test
resetMagnification()673     public void resetMagnification() {
674         final int displayId = 1;
675         when(mMockMagnificationProcessor.resetFullscreenMagnification(displayId, true)).thenReturn(
676                 true);
677 
678         final boolean result = mServiceConnection.resetMagnification(displayId, true);
679         assertThat(result).isTrue();
680     }
681 
682     @Test
resetMagnification_cantControlMagnification_returnFalse()683     public void resetMagnification_cantControlMagnification_returnFalse() {
684         final int displayId = 1;
685         when(mMockMagnificationProcessor.resetFullscreenMagnification(displayId, true)).thenReturn(
686                 true);
687         when(mMockSecurityPolicy.canControlMagnification(mServiceConnection)).thenReturn(false);
688 
689         final boolean result = mServiceConnection.resetMagnification(displayId, true);
690         assertThat(result).isFalse();
691     }
692 
693     @Test
resetMagnification_serviceNotBelongCurrentUser_returnFalse()694     public void resetMagnification_serviceNotBelongCurrentUser_returnFalse() {
695         final int displayId = 1;
696         when(mMockMagnificationProcessor.resetFullscreenMagnification(displayId, true)).thenReturn(
697                 true);
698         when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
699 
700         final boolean result = mServiceConnection.resetMagnification(displayId, true);
701         assertThat(result).isFalse();
702     }
703 
704     @Test
setMagnificationConfig_cantControlMagnification_returnFalse()705     public void setMagnificationConfig_cantControlMagnification_returnFalse() {
706         final int displayId = 1;
707         final float scale = 1.8f;
708         final float centerX = 50.5f;
709         final float centerY = 100.5f;
710         MagnificationConfig config = new MagnificationConfig.Builder()
711                 .setScale(scale)
712                 .setCenterX(centerX)
713                 .setCenterY(centerY).build();
714         when(mMockMagnificationProcessor.setMagnificationConfig(displayId, config, true,
715                 SERVICE_ID)).thenReturn(true);
716         when(mMockSecurityPolicy.canControlMagnification(mServiceConnection)).thenReturn(false);
717 
718         final boolean result = mServiceConnection.setMagnificationConfig(displayId, config, true);
719         assertFalse(result);
720     }
721 
722     @Test
setMagnificationConfig_serviceNotBelongCurrentUser_returnFalse()723     public void setMagnificationConfig_serviceNotBelongCurrentUser_returnFalse() {
724         final int displayId = 1;
725         final float scale = 1.8f;
726         final float centerX = 50.5f;
727         final float centerY = 100.5f;
728         MagnificationConfig config = new MagnificationConfig.Builder()
729                 .setScale(scale)
730                 .setCenterX(centerX)
731                 .setCenterY(centerY).build();
732         when(mMockMagnificationProcessor.setMagnificationConfig(displayId, config, true,
733                 SERVICE_ID)).thenReturn(true);
734         when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
735 
736         final boolean result = mServiceConnection.setMagnificationConfig(displayId, config, true);
737         assertFalse(result);
738     }
739 
740     @Test (expected = SecurityException.class)
takeScreenshot_withoutCapability_throwSecurityException()741     public void takeScreenshot_withoutCapability_throwSecurityException() {
742         // no canTakeScreenshot, should throw security exception.
743         when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(false);
744         mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY, new RemoteCallback((result) -> {
745         }));
746     }
747 
748     @Test
takeScreenshot_NoA11yAccess_returnErrorCode()749     public void takeScreenshot_NoA11yAccess_returnErrorCode() throws InterruptedException {
750         // no checkAccessibilityAccess, should return error code.
751         when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true);
752         when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(false);
753 
754         mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY,
755                 new RemoteCallback(mMockListener));
756         mHandler.sendLastMessage();
757 
758         verify(mMockListener).onResult(Mockito.argThat(
759                 bundle -> ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS
760                         == bundle.getInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS)));
761     }
762 
763     @Test
takeScreenshot_invalidDisplay_returnErrorCode()764     public void takeScreenshot_invalidDisplay_returnErrorCode() throws InterruptedException {
765         when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true);
766         when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(true);
767 
768         final DisplayManager displayManager = new DisplayManager(mMockContext);
769         when(mMockContext.getSystemService(Context.DISPLAY_SERVICE)).thenReturn(displayManager);
770 
771         mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY + 1,
772                 new RemoteCallback(mMockListener));
773         mHandler.sendLastMessage();
774 
775         verify(mMockListener).onResult(Mockito.argThat(
776                 bundle -> ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY
777                         == bundle.getInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS)));
778     }
779 
setPreinstalledA11yTool(boolean isPreinstalledA11yTool)780     private void setPreinstalledA11yTool(boolean isPreinstalledA11yTool) {
781         when(mSpyServiceInfo.getResolveInfo().serviceInfo.applicationInfo.isSystemApp())
782                 .thenReturn(isPreinstalledA11yTool);
783         when(mSpyServiceInfo.isAccessibilityTool()).thenReturn(isPreinstalledA11yTool);
784     }
785 
786     @Test
787     @EnableFlags(Flags.FLAG_ALLOW_SECURE_SCREENSHOTS)
takeScreenshot_standardService_cannotCaptureSecureLayers()788     public void takeScreenshot_standardService_cannotCaptureSecureLayers() {
789         setPreinstalledA11yTool(false);
790 
791         takeScreenshotOfDisplay();
792 
793         final ArgumentCaptor<ScreenCapture.CaptureArgs> displayArgsCaptor =
794                 ArgumentCaptor.forClass(ScreenCapture.CaptureArgs.class);
795         verify(mMockWindowManagerInternal).captureDisplay(
796                 eq(Display.DEFAULT_DISPLAY), displayArgsCaptor.capture(), any());
797         assertThat(displayArgsCaptor.getValue().mCaptureSecureLayers).isFalse();
798     }
799 
800     @Test
801     @EnableFlags(Flags.FLAG_ALLOW_SECURE_SCREENSHOTS)
takeScreenshot_preinstalledA11yTool_canCaptureSecureLayers()802     public void takeScreenshot_preinstalledA11yTool_canCaptureSecureLayers() {
803         setPreinstalledA11yTool(true);
804 
805         takeScreenshotOfDisplay();
806 
807         final ArgumentCaptor<ScreenCapture.CaptureArgs> displayArgsCaptor =
808                 ArgumentCaptor.forClass(ScreenCapture.CaptureArgs.class);
809         verify(mMockWindowManagerInternal).captureDisplay(
810                 anyInt(), displayArgsCaptor.capture(), any());
811         assertThat(displayArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
812     }
813 
takeScreenshotOfDisplay()814     private void takeScreenshotOfDisplay() {
815         when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true);
816         when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(true);
817 
818         final DisplayManager displayManager = new DisplayManager(mMockContext);
819         when(mMockContext.getSystemService(Context.DISPLAY_SERVICE)).thenReturn(displayManager);
820 
821         mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY,
822                 new RemoteCallback(mMockListener));
823     }
824 
825     @Test
826     @EnableFlags(Flags.FLAG_ALLOW_SECURE_SCREENSHOTS)
takeScreenshotOfWindow_standardWindow_standardService_cannotCaptureSecureLayers()827     public void takeScreenshotOfWindow_standardWindow_standardService_cannotCaptureSecureLayers()
828             throws Exception {
829         setPreinstalledA11yTool(false);
830 
831         takeScreenshotOfWindow(/*windowFlags=*/0);
832 
833         // Screenshot was allowed
834         final ArgumentCaptor<ScreenCapture.LayerCaptureArgs> layerArgsCaptor =
835                 ArgumentCaptor.forClass(ScreenCapture.LayerCaptureArgs.class);
836         verify(mMockSystemSupport).performScreenCapture(layerArgsCaptor.capture(), any());
837         // ...without secure layers included
838         assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isFalse();
839         // No error sent to callback
840         verifyNoMoreInteractions(mMockCallback);
841     }
842 
843     @Test
844     @EnableFlags(Flags.FLAG_ALLOW_SECURE_SCREENSHOTS)
takeScreenshotOfWindow_standardWindow_preinstalledA11yTool_canCaptureSecureLayers()845     public void takeScreenshotOfWindow_standardWindow_preinstalledA11yTool_canCaptureSecureLayers()
846             throws Exception {
847         setPreinstalledA11yTool(true);
848 
849         takeScreenshotOfWindow(/*windowFlags=*/0);
850 
851         // Screenshot was allowed
852         final ArgumentCaptor<ScreenCapture.LayerCaptureArgs> layerArgsCaptor =
853                 ArgumentCaptor.forClass(ScreenCapture.LayerCaptureArgs.class);
854         verify(mMockSystemSupport).performScreenCapture(layerArgsCaptor.capture(), any());
855         // ...with secure layers included
856         assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
857         // No error sent to callback
858         verifyNoMoreInteractions(mMockCallback);
859     }
860 
861     @Test
862     @EnableFlags(Flags.FLAG_ALLOW_SECURE_SCREENSHOTS)
takeScreenshotOfWindow_secureWindow_standardService_sendsCallbackError()863     public void takeScreenshotOfWindow_secureWindow_standardService_sendsCallbackError()
864             throws Exception {
865         setPreinstalledA11yTool(false);
866 
867         takeScreenshotOfWindow(WindowManager.LayoutParams.FLAG_SECURE);
868 
869         // Screenshot was not allowed
870         verify(mMockSystemSupport, never()).performScreenCapture(any(), any());
871         // Error sent to callback
872         verify(mMockCallback).sendTakeScreenshotOfWindowError(
873                 AccessibilityService.ERROR_TAKE_SCREENSHOT_SECURE_WINDOW, INTERACTION_ID);
874     }
875 
876     @Test
877     @EnableFlags(Flags.FLAG_ALLOW_SECURE_SCREENSHOTS)
takeScreenshotOfWindow_secureWindow_preinstalledA11yTool_canCaptureSecureLayers()878     public void takeScreenshotOfWindow_secureWindow_preinstalledA11yTool_canCaptureSecureLayers()
879             throws Exception {
880         setPreinstalledA11yTool(true);
881 
882         takeScreenshotOfWindow(WindowManager.LayoutParams.FLAG_SECURE);
883 
884         // Screenshot was allowed
885         final ArgumentCaptor<ScreenCapture.LayerCaptureArgs> layerArgsCaptor =
886                 ArgumentCaptor.forClass(ScreenCapture.LayerCaptureArgs.class);
887         verify(mMockSystemSupport).performScreenCapture(layerArgsCaptor.capture(), any());
888         // ...with secure layers included
889         assertThat(layerArgsCaptor.getValue().mCaptureSecureLayers).isTrue();
890         // No error sent to callback
891         verifyNoMoreInteractions(mMockCallback);
892     }
893 
takeScreenshotOfWindow(int windowFlags)894     private void takeScreenshotOfWindow(int windowFlags) throws Exception {
895         when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true);
896         when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(true);
897 
898         mServiceConnection.takeScreenshotOfWindow(
899                 WINDOWID, INTERACTION_ID, /*listener=*/null, mMockCallback);
900         final ArgumentCaptor<IWindowSurfaceInfoCallback> windowSurfaceCallbackCaptor =
901                 ArgumentCaptor.forClass(IWindowSurfaceInfoCallback.class);
902         verify(mMockIA11yInteractionConnection).getWindowSurfaceInfo(
903                 windowSurfaceCallbackCaptor.capture());
904         windowSurfaceCallbackCaptor.getValue().provideWindowSurfaceInfo(
905                 windowFlags, /*appUid=*/0, new SurfaceControl());
906     }
907 
updateServiceInfo(AccessibilityServiceInfo serviceInfo, int eventType, int feedbackType, int flags, String[] packageNames, int notificationTimeout)908     private void updateServiceInfo(AccessibilityServiceInfo serviceInfo, int eventType,
909             int feedbackType, int flags, String[] packageNames, int notificationTimeout) {
910         serviceInfo.eventTypes = eventType;
911         serviceInfo.feedbackType = feedbackType;
912         serviceInfo.flags = flags;
913         serviceInfo.packageNames = packageNames;
914         serviceInfo.notificationTimeout = notificationTimeout;
915     }
916 
addA11yWindowInfo(List<AccessibilityWindowInfo> infos, int windowId, boolean isPip, int displayId)917     private AccessibilityWindowInfo addA11yWindowInfo(List<AccessibilityWindowInfo> infos,
918             int windowId, boolean isPip, int displayId) {
919         final AccessibilityWindowInfo info = AccessibilityWindowInfo.obtain();
920         info.setId(windowId);
921         info.setDisplayId(displayId);
922         info.setPictureInPicture(isPip);
923         infos.add(info);
924         return info;
925     }
926 
getRemoteA11yConnection(int windowId, IAccessibilityInteractionConnection connection, String packageName)927     private RemoteAccessibilityConnection getRemoteA11yConnection(int windowId,
928             IAccessibilityInteractionConnection connection,
929             String packageName) {
930         return mMockA11yWindowManager.new RemoteAccessibilityConnection(
931                 windowId, connection, packageName, UID, USER_ID);
932     }
933 
initTestFunctions()934     private void initTestFunctions() {
935         // Init functions for accessibility nodes finding and searching by different filter rules.
936         // We group them together for the tests because they have similar implementation.
937         mFindA11yNodesFunctions = new Callable[] {
938                 // findAccessibilityNodeInfosByViewId
939                 () -> mServiceConnection.findAccessibilityNodeInfosByViewId(WINDOWID,
940                         ROOT_NODE_ID, VIEWID_RESOURCE_NAME, INTERACTION_ID,
941                         mMockCallback, TID),
942                 // findAccessibilityNodeInfosByText
943                 () -> mServiceConnection.findAccessibilityNodeInfosByText(WINDOWID,
944                         ROOT_NODE_ID, VIEW_TEXT, INTERACTION_ID, mMockCallback, TID),
945                 // findAccessibilityNodeInfoByAccessibilityId
946                 () -> mServiceConnection.findAccessibilityNodeInfoByAccessibilityId(WINDOWID,
947                         ROOT_NODE_ID, INTERACTION_ID, mMockCallback, 0, TID, null),
948                 // findFocus
949                 () -> mServiceConnection.findFocus(WINDOWID, ROOT_NODE_ID, FOCUS_INPUT,
950                         INTERACTION_ID, mMockCallback, TID),
951                 // focusSearch
952                 () -> mServiceConnection.focusSearch(WINDOWID, ROOT_NODE_ID, FOCUS_DOWN,
953                         INTERACTION_ID, mMockCallback, TID)
954         };
955         // performAccessibilityAction
956         mPerformA11yAction = () ->  mServiceConnection.performAccessibilityAction(WINDOWID,
957                 ROOT_NODE_ID, ACTION_ACCESSIBILITY_FOCUS, null, INTERACTION_ID,
958                 mMockCallback, TID);
959     }
960 
verifyReplaceActions(IAccessibilityInteractionConnectionCallback replacedCallback)961     private void verifyReplaceActions(IAccessibilityInteractionConnectionCallback replacedCallback)
962             throws RemoteException {
963         final AccessibilityNodeInfo nodeFromApp = AccessibilityNodeInfo.obtain();
964         nodeFromApp.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID, WINDOWID);
965 
966         final AccessibilityNodeInfo nodeFromReplacer = AccessibilityNodeInfo.obtain();
967         nodeFromReplacer.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID,
968                 AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
969         nodeFromReplacer.addAction(AccessibilityAction.ACTION_CLICK);
970         nodeFromReplacer.addAction(AccessibilityAction.ACTION_EXPAND);
971         final List<AccessibilityNodeInfo> replacerList = Arrays.asList(nodeFromReplacer);
972 
973         replacedCallback.setFindAccessibilityNodeInfoResult(nodeFromApp, INTERACTION_ID);
974         replacedCallback.setFindAccessibilityNodeInfosResult(replacerList, INTERACTION_ID + 1);
975 
976         final ArgumentCaptor<AccessibilityNodeInfo> captor =
977                 ArgumentCaptor.forClass(AccessibilityNodeInfo.class);
978         verify(mMockCallback).setFindAccessibilityNodeInfoResult(captor.capture(),
979                 eq(INTERACTION_ID));
980         assertThat(captor.getValue().getActionList()).containsAtLeast(
981                 AccessibilityAction.ACTION_CLICK, AccessibilityAction.ACTION_EXPAND);
982     }
983 
984     private static class TestAccessibilityServiceConnection
985             extends AbstractAccessibilityServiceConnection {
986         int mResolvedUserId;
987 
TestAccessibilityServiceConnection(Context context, ComponentName componentName, AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler, Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport, AccessibilityTrace trace, WindowManagerInternal windowManagerInternal, SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager a11yWindowManager)988         TestAccessibilityServiceConnection(Context context, ComponentName componentName,
989                 AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
990                 Object lock, AccessibilitySecurityPolicy securityPolicy,
991                 SystemSupport systemSupport, AccessibilityTrace trace,
992                 WindowManagerInternal windowManagerInternal,
993                 SystemActionPerformer systemActionPerfomer,
994                 AccessibilityWindowManager a11yWindowManager) {
995             super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
996                     securityPolicy, systemSupport, trace, windowManagerInternal,
997                     systemActionPerfomer, a11yWindowManager);
998             mResolvedUserId = USER_ID;
999         }
1000 
1001         @Override
hasRightsToCurrentUserLocked()1002         protected boolean hasRightsToCurrentUserLocked() {
1003             return mResolvedUserId == mSystemSupport.getCurrentUserIdLocked();
1004         }
1005 
1006         @Override
disableSelf()1007         public void disableSelf() throws RemoteException {}
1008 
1009         @Override
setSoftKeyboardShowMode(int showMode)1010         public boolean setSoftKeyboardShowMode(int showMode) throws RemoteException {
1011             return false;
1012         }
1013 
1014         @Override
getSoftKeyboardShowMode()1015         public int getSoftKeyboardShowMode() throws RemoteException {
1016             return 0;
1017         }
1018 
1019         @Override
switchToInputMethod(String imeId)1020         public boolean switchToInputMethod(String imeId) {
1021             return false;
1022         }
1023 
1024         @Override
setInputMethodEnabled(String imeId, boolean enabled)1025         public int setInputMethodEnabled(String imeId, boolean enabled) throws RemoteException {
1026             return AccessibilityService.SoftKeyboardController.ENABLE_IME_FAIL_UNKNOWN;
1027         }
1028 
1029         @Override
isAccessibilityButtonAvailable()1030         public boolean isAccessibilityButtonAvailable() throws RemoteException {
1031             return false;
1032         }
1033 
1034         @Override
onServiceConnected(ComponentName name, IBinder service)1035         public void onServiceConnected(ComponentName name, IBinder service) {}
1036 
1037         @Override
onServiceDisconnected(ComponentName name)1038         public void onServiceDisconnected(ComponentName name) {}
1039 
1040         @Override
binderDied()1041         public void binderDied() {}
1042 
1043         @Override
isCapturingFingerprintGestures()1044         public boolean isCapturingFingerprintGestures() {
1045             return mCaptureFingerprintGestures;
1046         }
1047 
1048         @Override
onFingerprintGestureDetectionActiveChanged(boolean active)1049         public void onFingerprintGestureDetectionActiveChanged(boolean active) {}
1050 
1051         @Override
onFingerprintGesture(int gesture)1052         public void onFingerprintGesture(int gesture) {}
1053     }
1054 }
1055