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