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