1 /* 2 * Copyright (C) 2021 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.companion.virtual; 18 19 import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES; 20 21 import static com.google.common.truth.Truth.assertThat; 22 import static com.google.common.truth.Truth.assertWithMessage; 23 24 import static org.mockito.ArgumentMatchers.any; 25 import static org.mockito.ArgumentMatchers.anyBoolean; 26 import static org.mockito.ArgumentMatchers.anyFloat; 27 import static org.mockito.ArgumentMatchers.anyInt; 28 import static org.mockito.ArgumentMatchers.anyString; 29 import static org.mockito.ArgumentMatchers.eq; 30 import static org.mockito.ArgumentMatchers.nullable; 31 import static org.mockito.Mockito.argThat; 32 import static org.mockito.Mockito.doCallRealMethod; 33 import static org.mockito.Mockito.doNothing; 34 import static org.mockito.Mockito.doReturn; 35 import static org.mockito.Mockito.never; 36 import static org.mockito.Mockito.times; 37 import static org.mockito.Mockito.verify; 38 import static org.mockito.Mockito.verifyNoMoreInteractions; 39 import static org.mockito.Mockito.when; 40 import static org.testng.Assert.assertThrows; 41 42 import android.Manifest; 43 import android.app.WindowConfiguration; 44 import android.app.admin.DevicePolicyManager; 45 import android.companion.AssociationInfo; 46 import android.companion.virtual.IVirtualDeviceActivityListener; 47 import android.companion.virtual.VirtualDeviceParams; 48 import android.companion.virtual.audio.IAudioConfigChangedCallback; 49 import android.companion.virtual.audio.IAudioRoutingCallback; 50 import android.content.ComponentName; 51 import android.content.Context; 52 import android.content.ContextWrapper; 53 import android.content.Intent; 54 import android.content.pm.ActivityInfo; 55 import android.content.pm.ApplicationInfo; 56 import android.graphics.Point; 57 import android.hardware.display.DisplayManagerInternal; 58 import android.hardware.input.IInputManager; 59 import android.hardware.input.InputManagerInternal; 60 import android.hardware.input.VirtualKeyEvent; 61 import android.hardware.input.VirtualMouseButtonEvent; 62 import android.hardware.input.VirtualMouseRelativeEvent; 63 import android.hardware.input.VirtualMouseScrollEvent; 64 import android.hardware.input.VirtualTouchEvent; 65 import android.net.MacAddress; 66 import android.os.Binder; 67 import android.os.Handler; 68 import android.os.IBinder; 69 import android.os.IPowerManager; 70 import android.os.IThermalService; 71 import android.os.PowerManager; 72 import android.os.Process; 73 import android.os.RemoteException; 74 import android.os.WorkSource; 75 import android.platform.test.annotations.Presubmit; 76 import android.testing.AndroidTestingRunner; 77 import android.testing.TestableLooper; 78 import android.util.ArraySet; 79 import android.view.DisplayInfo; 80 import android.view.KeyEvent; 81 import android.view.WindowManager; 82 83 import androidx.test.InstrumentationRegistry; 84 85 import com.android.internal.app.BlockedAppStreamingActivity; 86 import com.android.server.LocalServices; 87 88 import org.junit.Before; 89 import org.junit.Test; 90 import org.junit.runner.RunWith; 91 import org.mockito.ArgumentCaptor; 92 import org.mockito.Mock; 93 import org.mockito.Mockito; 94 import org.mockito.MockitoAnnotations; 95 96 import java.util.ArrayList; 97 import java.util.Arrays; 98 import java.util.function.Consumer; 99 100 @Presubmit 101 @RunWith(AndroidTestingRunner.class) 102 @TestableLooper.RunWithLooper(setAsMainLooper = true) 103 public class VirtualDeviceManagerServiceTest { 104 105 private static final String NONBLOCKED_APP_PACKAGE_NAME = "com.someapp"; 106 private static final String PERMISSION_CONTROLLER_PACKAGE_NAME = 107 "com.android.permissioncontroller"; 108 private static final String SETTINGS_PACKAGE_NAME = "com.android.settings"; 109 private static final String VENDING_PACKAGE_NAME = "com.android.vending"; 110 private static final String GOOGLE_DIALER_PACKAGE_NAME = "com.google.android.dialer"; 111 private static final String GOOGLE_MAPS_PACKAGE_NAME = "com.google.android.apps.maps"; 112 private static final String DEVICE_NAME = "device name"; 113 private static final int DISPLAY_ID = 2; 114 private static final int UID_1 = 0; 115 private static final int UID_2 = 10; 116 private static final int UID_3 = 10000; 117 private static final int UID_4 = 10001; 118 private static final int ASSOCIATION_ID_1 = 1; 119 private static final int ASSOCIATION_ID_2 = 2; 120 private static final int PRODUCT_ID = 10; 121 private static final int VENDOR_ID = 5; 122 private static final String UNIQUE_ID = "uniqueid"; 123 private static final String PHYS = "phys"; 124 private static final int HEIGHT = 1800; 125 private static final int WIDTH = 900; 126 private static final Binder BINDER = new Binder("binder"); 127 private static final int FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES = 0x00000; 128 129 private Context mContext; 130 private InputManagerMockHelper mInputManagerMockHelper; 131 private VirtualDeviceImpl mDeviceImpl; 132 private InputController mInputController; 133 private AssociationInfo mAssociationInfo; 134 private VirtualDeviceManagerService mVdms; 135 private VirtualDeviceManagerInternal mLocalService; 136 @Mock 137 private InputController.NativeWrapper mNativeWrapperMock; 138 @Mock 139 private DisplayManagerInternal mDisplayManagerInternalMock; 140 @Mock 141 private VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback; 142 @Mock 143 private DevicePolicyManager mDevicePolicyManagerMock; 144 @Mock 145 private InputManagerInternal mInputManagerInternalMock; 146 @Mock 147 private IVirtualDeviceActivityListener mActivityListener; 148 @Mock 149 private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback; 150 @Mock 151 private VirtualDeviceManagerInternal.VirtualDisplayListener mDisplayListener; 152 @Mock 153 private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener mAppsOnVirtualDeviceListener; 154 @Mock 155 IPowerManager mIPowerManagerMock; 156 @Mock 157 IThermalService mIThermalServiceMock; 158 private PowerManager mPowerManager; 159 @Mock 160 private IAudioRoutingCallback mRoutingCallback; 161 @Mock 162 private IAudioConfigChangedCallback mConfigChangedCallback; 163 @Mock 164 private ApplicationInfo mApplicationInfoMock; 165 @Mock 166 IInputManager mIInputManagerMock; 167 getBlockedActivities()168 private ArraySet<ComponentName> getBlockedActivities() { 169 ArraySet<ComponentName> blockedActivities = new ArraySet<>(); 170 blockedActivities.add(new ComponentName(SETTINGS_PACKAGE_NAME, SETTINGS_PACKAGE_NAME)); 171 blockedActivities.add(new ComponentName(VENDING_PACKAGE_NAME, VENDING_PACKAGE_NAME)); 172 blockedActivities.add( 173 new ComponentName(GOOGLE_DIALER_PACKAGE_NAME, GOOGLE_DIALER_PACKAGE_NAME)); 174 blockedActivities.add( 175 new ComponentName(GOOGLE_MAPS_PACKAGE_NAME, GOOGLE_MAPS_PACKAGE_NAME)); 176 return blockedActivities; 177 } 178 getActivityInfoList( String packageName, String name, boolean displayOnRemoveDevices)179 private ArrayList<ActivityInfo> getActivityInfoList( 180 String packageName, String name, boolean displayOnRemoveDevices) { 181 ActivityInfo activityInfo = new ActivityInfo(); 182 activityInfo.packageName = packageName; 183 activityInfo.name = name; 184 activityInfo.flags = displayOnRemoveDevices 185 ? FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES : FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES; 186 activityInfo.applicationInfo = mApplicationInfoMock; 187 return new ArrayList<>(Arrays.asList(activityInfo)); 188 } 189 190 @Before setUp()191 public void setUp() throws Exception { 192 MockitoAnnotations.initMocks(this); 193 194 LocalServices.removeServiceForTest(DisplayManagerInternal.class); 195 LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock); 196 197 doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt()); 198 doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt()); 199 doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt()); 200 LocalServices.removeServiceForTest(InputManagerInternal.class); 201 LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock); 202 203 final DisplayInfo displayInfo = new DisplayInfo(); 204 displayInfo.uniqueId = UNIQUE_ID; 205 doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt()); 206 LocalServices.removeServiceForTest(DisplayManagerInternal.class); 207 LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock); 208 209 mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext())); 210 doReturn(mContext).when(mContext).createContextAsUser(eq(Process.myUserHandle()), anyInt()); 211 doNothing().when(mContext).enforceCallingOrSelfPermission( 212 eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString()); 213 when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn( 214 mDevicePolicyManagerMock); 215 216 mPowerManager = new PowerManager(mContext, mIPowerManagerMock, mIThermalServiceMock, 217 new Handler(TestableLooper.get(this).getLooper())); 218 when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(mPowerManager); 219 220 mInputManagerMockHelper = new InputManagerMockHelper( 221 TestableLooper.get(this), mNativeWrapperMock, mIInputManagerMock); 222 // Allow virtual devices to be created on the looper thread for testing. 223 final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true; 224 mInputController = new InputController(new Object(), mNativeWrapperMock, 225 new Handler(TestableLooper.get(this).getLooper()), 226 mContext.getSystemService(WindowManager.class), threadVerifier); 227 228 mAssociationInfo = new AssociationInfo(1, 0, null, 229 MacAddress.BROADCAST_ADDRESS, "", null, true, false, false, 0, 0); 230 231 mVdms = new VirtualDeviceManagerService(mContext); 232 mLocalService = mVdms.getLocalServiceInstance(); 233 234 VirtualDeviceParams params = new VirtualDeviceParams 235 .Builder() 236 .setBlockedActivities(getBlockedActivities()) 237 .build(); 238 mDeviceImpl = new VirtualDeviceImpl(mContext, 239 mAssociationInfo, new Binder(), /* uid */ 0, mInputController, 240 (int associationId) -> { 241 }, mPendingTrampolineCallback, mActivityListener, mRunningAppsChangedCallback, 242 params); 243 } 244 245 @Test onVirtualDisplayRemovedLocked_doesNotThrowException()246 public void onVirtualDisplayRemovedLocked_doesNotThrowException() { 247 mDeviceImpl.onVirtualDisplayCreatedLocked( 248 mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); 249 // This call should not throw any exceptions. 250 mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID); 251 } 252 253 @Test onVirtualDisplayCreatedLocked_listenersNotified()254 public void onVirtualDisplayCreatedLocked_listenersNotified() { 255 mLocalService.registerVirtualDisplayListener(mDisplayListener); 256 257 mLocalService.onVirtualDisplayCreated(DISPLAY_ID); 258 TestableLooper.get(this).processAllMessages(); 259 260 verify(mDisplayListener).onVirtualDisplayCreated(DISPLAY_ID); 261 } 262 263 @Test onVirtualDisplayRemovedLocked_listenersNotified()264 public void onVirtualDisplayRemovedLocked_listenersNotified() { 265 mLocalService.registerVirtualDisplayListener(mDisplayListener); 266 mDeviceImpl.onVirtualDisplayCreatedLocked( 267 mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); 268 269 mLocalService.onVirtualDisplayRemoved(mDeviceImpl, DISPLAY_ID); 270 TestableLooper.get(this).processAllMessages(); 271 272 verify(mDisplayListener).onVirtualDisplayRemoved(DISPLAY_ID); 273 } 274 275 @Test onAppsOnVirtualDeviceChanged_singleVirtualDevice_listenersNotified()276 public void onAppsOnVirtualDeviceChanged_singleVirtualDevice_listenersNotified() { 277 ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2)); 278 mLocalService.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener); 279 280 mVdms.notifyRunningAppsChanged(ASSOCIATION_ID_1, uids); 281 TestableLooper.get(this).processAllMessages(); 282 283 verify(mAppsOnVirtualDeviceListener).onAppsOnAnyVirtualDeviceChanged(uids); 284 } 285 286 @Test onAppsOnVirtualDeviceChanged_multipleVirtualDevices_listenersNotified()287 public void onAppsOnVirtualDeviceChanged_multipleVirtualDevices_listenersNotified() { 288 ArraySet<Integer> uidsOnDevice1 = new ArraySet<>(Arrays.asList(UID_1, UID_2)); 289 ArraySet<Integer> uidsOnDevice2 = new ArraySet<>(Arrays.asList(UID_3, UID_4)); 290 mLocalService.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener); 291 292 // Notifies that the running apps on the first virtual device has changed. 293 mVdms.notifyRunningAppsChanged(ASSOCIATION_ID_1, uidsOnDevice1); 294 TestableLooper.get(this).processAllMessages(); 295 verify(mAppsOnVirtualDeviceListener).onAppsOnAnyVirtualDeviceChanged( 296 new ArraySet<>(Arrays.asList(UID_1, UID_2))); 297 298 // Notifies that the running apps on the second virtual device has changed. 299 mVdms.notifyRunningAppsChanged(ASSOCIATION_ID_2, uidsOnDevice2); 300 TestableLooper.get(this).processAllMessages(); 301 // The union of the apps running on both virtual devices are sent to the listeners. 302 verify(mAppsOnVirtualDeviceListener).onAppsOnAnyVirtualDeviceChanged( 303 new ArraySet<>(Arrays.asList(UID_1, UID_2, UID_3, UID_4))); 304 305 // Notifies that the running apps on the first virtual device has changed again. 306 uidsOnDevice1.remove(UID_2); 307 mVdms.notifyRunningAppsChanged(ASSOCIATION_ID_1, uidsOnDevice1); 308 mLocalService.onAppsOnVirtualDeviceChanged(); 309 TestableLooper.get(this).processAllMessages(); 310 // The union of the apps running on both virtual devices are sent to the listeners. 311 verify(mAppsOnVirtualDeviceListener).onAppsOnAnyVirtualDeviceChanged( 312 new ArraySet<>(Arrays.asList(UID_1, UID_3, UID_4))); 313 314 // Notifies that the running apps on the first virtual device has changed but with the same 315 // set of UIDs. 316 mVdms.notifyRunningAppsChanged(ASSOCIATION_ID_1, uidsOnDevice1); 317 mLocalService.onAppsOnVirtualDeviceChanged(); 318 TestableLooper.get(this).processAllMessages(); 319 // Listeners should not be notified. 320 verifyNoMoreInteractions(mAppsOnVirtualDeviceListener); 321 } 322 323 @Test onVirtualDisplayCreatedLocked_wakeLockIsAcquired()324 public void onVirtualDisplayCreatedLocked_wakeLockIsAcquired() throws RemoteException { 325 verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(), 326 nullable(String.class), nullable(String.class), nullable(WorkSource.class), 327 nullable(String.class), anyInt(), eq(null)); 328 mDeviceImpl.onVirtualDisplayCreatedLocked( 329 mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); 330 verify(mIPowerManagerMock).acquireWakeLock(any(Binder.class), anyInt(), 331 nullable(String.class), nullable(String.class), nullable(WorkSource.class), 332 nullable(String.class), eq(DISPLAY_ID), eq(null)); 333 } 334 335 @Test onVirtualDisplayCreatedLocked_duplicateCalls_onlyOneWakeLockIsAcquired()336 public void onVirtualDisplayCreatedLocked_duplicateCalls_onlyOneWakeLockIsAcquired() 337 throws RemoteException { 338 GenericWindowPolicyController gwpc = mDeviceImpl.createWindowPolicyController(); 339 mDeviceImpl.onVirtualDisplayCreatedLocked( 340 mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); 341 assertThrows(IllegalStateException.class, 342 () -> mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID)); 343 TestableLooper.get(this).processAllMessages(); 344 verify(mIPowerManagerMock).acquireWakeLock(any(Binder.class), anyInt(), 345 nullable(String.class), nullable(String.class), nullable(WorkSource.class), 346 nullable(String.class), eq(DISPLAY_ID), eq(null)); 347 } 348 349 @Test onVirtualDisplayRemovedLocked_unknownDisplayId_throwsException()350 public void onVirtualDisplayRemovedLocked_unknownDisplayId_throwsException() { 351 final int unknownDisplayId = 999; 352 assertThrows(IllegalStateException.class, 353 () -> mDeviceImpl.onVirtualDisplayRemovedLocked(unknownDisplayId)); 354 } 355 356 @Test onVirtualDisplayRemovedLocked_wakeLockIsReleased()357 public void onVirtualDisplayRemovedLocked_wakeLockIsReleased() throws RemoteException { 358 mDeviceImpl.onVirtualDisplayCreatedLocked( 359 mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); 360 ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class); 361 TestableLooper.get(this).processAllMessages(); 362 verify(mIPowerManagerMock).acquireWakeLock(wakeLockCaptor.capture(), 363 anyInt(), 364 nullable(String.class), nullable(String.class), nullable(WorkSource.class), 365 nullable(String.class), eq(DISPLAY_ID), eq(null)); 366 367 IBinder wakeLock = wakeLockCaptor.getValue(); 368 mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID); 369 verify(mIPowerManagerMock).releaseWakeLock(eq(wakeLock), anyInt()); 370 } 371 372 @Test addVirtualDisplay_displayNotReleased_wakeLockIsReleased()373 public void addVirtualDisplay_displayNotReleased_wakeLockIsReleased() throws RemoteException { 374 mDeviceImpl.onVirtualDisplayCreatedLocked( 375 mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); 376 ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class); 377 TestableLooper.get(this).processAllMessages(); 378 verify(mIPowerManagerMock).acquireWakeLock(wakeLockCaptor.capture(), 379 anyInt(), 380 nullable(String.class), nullable(String.class), nullable(WorkSource.class), 381 nullable(String.class), eq(DISPLAY_ID), eq(null)); 382 IBinder wakeLock = wakeLockCaptor.getValue(); 383 384 // Close the VirtualDevice without first notifying it of the VirtualDisplay removal. 385 mDeviceImpl.close(); 386 verify(mIPowerManagerMock).releaseWakeLock(eq(wakeLock), anyInt()); 387 } 388 389 @Test createVirtualKeyboard_noDisplay_failsSecurityException()390 public void createVirtualKeyboard_noDisplay_failsSecurityException() { 391 assertThrows( 392 SecurityException.class, 393 () -> mDeviceImpl.createVirtualKeyboard(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, 394 PRODUCT_ID, BINDER)); 395 } 396 397 @Test createVirtualMouse_noDisplay_failsSecurityException()398 public void createVirtualMouse_noDisplay_failsSecurityException() { 399 assertThrows( 400 SecurityException.class, 401 () -> mDeviceImpl.createVirtualMouse(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, 402 PRODUCT_ID, BINDER)); 403 } 404 405 @Test createVirtualTouchscreen_noDisplay_failsSecurityException()406 public void createVirtualTouchscreen_noDisplay_failsSecurityException() { 407 assertThrows( 408 SecurityException.class, 409 () -> mDeviceImpl.createVirtualTouchscreen(DISPLAY_ID, DEVICE_NAME, 410 VENDOR_ID, PRODUCT_ID, BINDER, new Point(WIDTH, HEIGHT))); 411 } 412 413 @Test onAudioSessionStarting_noDisplay_failsSecurityException()414 public void onAudioSessionStarting_noDisplay_failsSecurityException() { 415 assertThrows(SecurityException.class, 416 () -> mDeviceImpl.onAudioSessionStarting( 417 DISPLAY_ID, mRoutingCallback, mConfigChangedCallback)); 418 } 419 420 @Test createVirtualKeyboard_noPermission_failsSecurityException()421 public void createVirtualKeyboard_noPermission_failsSecurityException() { 422 mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID); 423 doCallRealMethod().when(mContext).enforceCallingOrSelfPermission( 424 eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString()); 425 assertThrows( 426 SecurityException.class, 427 () -> mDeviceImpl.createVirtualKeyboard(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, 428 PRODUCT_ID, BINDER)); 429 } 430 431 @Test createVirtualMouse_noPermission_failsSecurityException()432 public void createVirtualMouse_noPermission_failsSecurityException() { 433 mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID); 434 doCallRealMethod().when(mContext).enforceCallingOrSelfPermission( 435 eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString()); 436 assertThrows( 437 SecurityException.class, 438 () -> mDeviceImpl.createVirtualMouse(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, 439 PRODUCT_ID, BINDER)); 440 } 441 442 @Test createVirtualTouchscreen_noPermission_failsSecurityException()443 public void createVirtualTouchscreen_noPermission_failsSecurityException() { 444 mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID); 445 doCallRealMethod().when(mContext).enforceCallingOrSelfPermission( 446 eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString()); 447 assertThrows( 448 SecurityException.class, 449 () -> mDeviceImpl.createVirtualTouchscreen(DISPLAY_ID, DEVICE_NAME, 450 VENDOR_ID, PRODUCT_ID, BINDER, new Point(WIDTH, HEIGHT))); 451 } 452 453 @Test onAudioSessionStarting_noPermission_failsSecurityException()454 public void onAudioSessionStarting_noPermission_failsSecurityException() { 455 mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID); 456 doCallRealMethod().when(mContext).enforceCallingOrSelfPermission( 457 eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString()); 458 assertThrows(SecurityException.class, 459 () -> mDeviceImpl.onAudioSessionStarting( 460 DISPLAY_ID, mRoutingCallback, mConfigChangedCallback)); 461 } 462 463 @Test onAudioSessionEnded_noPermission_failsSecurityException()464 public void onAudioSessionEnded_noPermission_failsSecurityException() { 465 doCallRealMethod().when(mContext).enforceCallingOrSelfPermission( 466 eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString()); 467 assertThrows(SecurityException.class, () -> mDeviceImpl.onAudioSessionEnded()); 468 } 469 470 @Test createVirtualKeyboard_hasDisplay_obtainFileDescriptor()471 public void createVirtualKeyboard_hasDisplay_obtainFileDescriptor() { 472 mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID); 473 mDeviceImpl.createVirtualKeyboard(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, 474 BINDER); 475 assertWithMessage("Virtual keyboard should register fd when the display matches") 476 .that(mInputController.mInputDeviceDescriptors).isNotEmpty(); 477 verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME), eq(VENDOR_ID), 478 eq(PRODUCT_ID), anyString()); 479 } 480 481 @Test createVirtualMouse_hasDisplay_obtainFileDescriptor()482 public void createVirtualMouse_hasDisplay_obtainFileDescriptor() { 483 mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID); 484 mDeviceImpl.createVirtualMouse(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, 485 BINDER); 486 assertWithMessage("Virtual keyboard should register fd when the display matches") 487 .that(mInputController.mInputDeviceDescriptors).isNotEmpty(); 488 verify(mNativeWrapperMock).openUinputMouse(eq(DEVICE_NAME), eq(VENDOR_ID), eq(PRODUCT_ID), 489 anyString()); 490 } 491 492 @Test createVirtualTouchscreen_hasDisplay_obtainFileDescriptor()493 public void createVirtualTouchscreen_hasDisplay_obtainFileDescriptor() { 494 mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID); 495 mDeviceImpl.createVirtualTouchscreen(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, 496 BINDER, new Point(WIDTH, HEIGHT)); 497 assertWithMessage("Virtual keyboard should register fd when the display matches") 498 .that(mInputController.mInputDeviceDescriptors).isNotEmpty(); 499 verify(mNativeWrapperMock).openUinputTouchscreen(eq(DEVICE_NAME), eq(VENDOR_ID), 500 eq(PRODUCT_ID), anyString(), eq(HEIGHT), eq(WIDTH)); 501 } 502 503 @Test onAudioSessionStarting_hasVirtualAudioController()504 public void onAudioSessionStarting_hasVirtualAudioController() { 505 mDeviceImpl.onVirtualDisplayCreatedLocked( 506 mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); 507 508 mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mRoutingCallback, mConfigChangedCallback); 509 510 assertThat(mDeviceImpl.getVirtualAudioControllerForTesting()).isNotNull(); 511 } 512 513 @Test onAudioSessionEnded_noVirtualAudioController()514 public void onAudioSessionEnded_noVirtualAudioController() { 515 mDeviceImpl.onVirtualDisplayCreatedLocked( 516 mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); 517 mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mRoutingCallback, mConfigChangedCallback); 518 519 mDeviceImpl.onAudioSessionEnded(); 520 521 assertThat(mDeviceImpl.getVirtualAudioControllerForTesting()).isNull(); 522 } 523 524 @Test close_cleanVirtualAudioController()525 public void close_cleanVirtualAudioController() { 526 mDeviceImpl.onVirtualDisplayCreatedLocked( 527 mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); 528 mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mRoutingCallback, mConfigChangedCallback); 529 530 mDeviceImpl.close(); 531 532 assertThat(mDeviceImpl.getVirtualAudioControllerForTesting()).isNull(); 533 } 534 535 @Test sendKeyEvent_noFd()536 public void sendKeyEvent_noFd() { 537 assertThrows( 538 IllegalArgumentException.class, 539 () -> 540 mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder() 541 .setKeyCode(KeyEvent.KEYCODE_A) 542 .setAction(VirtualKeyEvent.ACTION_DOWN).build())); 543 } 544 545 @Test sendKeyEvent_hasFd_writesEvent()546 public void sendKeyEvent_hasFd_writesEvent() { 547 final int fd = 1; 548 final int keyCode = KeyEvent.KEYCODE_A; 549 final int action = VirtualKeyEvent.ACTION_UP; 550 mInputController.mInputDeviceDescriptors.put(BINDER, 551 new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 1, 552 /* displayId= */ 1, PHYS)); 553 mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder().setKeyCode(keyCode) 554 .setAction(action).build()); 555 verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action); 556 } 557 558 @Test sendButtonEvent_noFd()559 public void sendButtonEvent_noFd() { 560 assertThrows( 561 IllegalArgumentException.class, 562 () -> 563 mDeviceImpl.sendButtonEvent(BINDER, 564 new VirtualMouseButtonEvent.Builder() 565 .setButtonCode(VirtualMouseButtonEvent.BUTTON_BACK) 566 .setAction(VirtualMouseButtonEvent.ACTION_BUTTON_PRESS) 567 .build())); 568 } 569 570 @Test sendButtonEvent_hasFd_writesEvent()571 public void sendButtonEvent_hasFd_writesEvent() { 572 final int fd = 1; 573 final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK; 574 final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS; 575 mInputController.mInputDeviceDescriptors.put(BINDER, 576 new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, 577 /* displayId= */ 1, PHYS)); 578 doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); 579 mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder() 580 .setButtonCode(buttonCode) 581 .setAction(action).build()); 582 verify(mNativeWrapperMock).writeButtonEvent(fd, buttonCode, action); 583 } 584 585 @Test sendButtonEvent_hasFd_wrongDisplay_throwsIllegalStateException()586 public void sendButtonEvent_hasFd_wrongDisplay_throwsIllegalStateException() { 587 final int fd = 1; 588 final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK; 589 final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS; 590 mInputController.mInputDeviceDescriptors.put(BINDER, 591 new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, 592 /* displayId= */ 1, PHYS)); 593 assertThrows( 594 IllegalStateException.class, 595 () -> 596 mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder() 597 .setButtonCode(buttonCode) 598 .setAction(action).build())); 599 } 600 601 @Test sendRelativeEvent_noFd()602 public void sendRelativeEvent_noFd() { 603 assertThrows( 604 IllegalArgumentException.class, 605 () -> 606 mDeviceImpl.sendRelativeEvent(BINDER, 607 new VirtualMouseRelativeEvent.Builder().setRelativeX( 608 0.0f).setRelativeY(0.0f).build())); 609 } 610 611 @Test sendRelativeEvent_hasFd_writesEvent()612 public void sendRelativeEvent_hasFd_writesEvent() { 613 final int fd = 1; 614 final float x = -0.2f; 615 final float y = 0.7f; 616 mInputController.mInputDeviceDescriptors.put(BINDER, 617 new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, 618 /* displayId= */ 1, PHYS)); 619 doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); 620 mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder() 621 .setRelativeX(x).setRelativeY(y).build()); 622 verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y); 623 } 624 625 @Test sendRelativeEvent_hasFd_wrongDisplay_throwsIllegalStateException()626 public void sendRelativeEvent_hasFd_wrongDisplay_throwsIllegalStateException() { 627 final int fd = 1; 628 final float x = -0.2f; 629 final float y = 0.7f; 630 mInputController.mInputDeviceDescriptors.put(BINDER, 631 new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, 632 /* displayId= */ 1, PHYS)); 633 assertThrows( 634 IllegalStateException.class, 635 () -> 636 mDeviceImpl.sendRelativeEvent(BINDER, 637 new VirtualMouseRelativeEvent.Builder() 638 .setRelativeX(x).setRelativeY(y).build())); 639 } 640 641 @Test sendScrollEvent_noFd()642 public void sendScrollEvent_noFd() { 643 assertThrows( 644 IllegalArgumentException.class, 645 () -> 646 mDeviceImpl.sendScrollEvent(BINDER, 647 new VirtualMouseScrollEvent.Builder() 648 .setXAxisMovement(-1f) 649 .setYAxisMovement(1f).build())); 650 } 651 652 @Test sendScrollEvent_hasFd_writesEvent()653 public void sendScrollEvent_hasFd_writesEvent() { 654 final int fd = 1; 655 final float x = 0.5f; 656 final float y = 1f; 657 mInputController.mInputDeviceDescriptors.put(BINDER, 658 new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, 659 /* displayId= */ 1, PHYS)); 660 doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); 661 mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder() 662 .setXAxisMovement(x) 663 .setYAxisMovement(y).build()); 664 verify(mNativeWrapperMock).writeScrollEvent(fd, x, y); 665 } 666 667 @Test sendScrollEvent_hasFd_wrongDisplay_throwsIllegalStateException()668 public void sendScrollEvent_hasFd_wrongDisplay_throwsIllegalStateException() { 669 final int fd = 1; 670 final float x = 0.5f; 671 final float y = 1f; 672 mInputController.mInputDeviceDescriptors.put(BINDER, 673 new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, 674 /* displayId= */ 1, PHYS)); 675 assertThrows( 676 IllegalStateException.class, 677 () -> 678 mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder() 679 .setXAxisMovement(x) 680 .setYAxisMovement(y).build())); 681 } 682 683 @Test sendTouchEvent_noFd()684 public void sendTouchEvent_noFd() { 685 assertThrows( 686 IllegalArgumentException.class, 687 () -> 688 mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder() 689 .setX(0.0f) 690 .setY(0.0f) 691 .setAction(VirtualTouchEvent.ACTION_UP) 692 .setPointerId(1) 693 .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER) 694 .build())); 695 } 696 697 @Test sendTouchEvent_hasFd_writesEvent_withoutPressureOrMajorAxisSize()698 public void sendTouchEvent_hasFd_writesEvent_withoutPressureOrMajorAxisSize() { 699 final int fd = 1; 700 final int pointerId = 5; 701 final int toolType = VirtualTouchEvent.TOOL_TYPE_FINGER; 702 final float x = 100.5f; 703 final float y = 200.5f; 704 final int action = VirtualTouchEvent.ACTION_UP; 705 mInputController.mInputDeviceDescriptors.put(BINDER, 706 new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3, 707 /* displayId= */ 1, PHYS)); 708 mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x) 709 .setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType).build()); 710 verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN, 711 Float.NaN); 712 } 713 714 @Test sendTouchEvent_hasFd_writesEvent()715 public void sendTouchEvent_hasFd_writesEvent() { 716 final int fd = 1; 717 final int pointerId = 5; 718 final int toolType = VirtualTouchEvent.TOOL_TYPE_FINGER; 719 final float x = 100.5f; 720 final float y = 200.5f; 721 final int action = VirtualTouchEvent.ACTION_UP; 722 final float pressure = 1.0f; 723 final float majorAxisSize = 10.0f; 724 mInputController.mInputDeviceDescriptors.put(BINDER, 725 new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3, 726 /* displayId= */ 1, PHYS)); 727 mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x) 728 .setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType) 729 .setPressure(pressure).setMajorAxisSize(majorAxisSize).build()); 730 verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, pressure, 731 majorAxisSize); 732 } 733 734 @Test setShowPointerIcon_setsValueForAllDisplays()735 public void setShowPointerIcon_setsValueForAllDisplays() { 736 mDeviceImpl.mVirtualDisplayIds.add(1); 737 mDeviceImpl.mVirtualDisplayIds.add(2); 738 mDeviceImpl.mVirtualDisplayIds.add(3); 739 mDeviceImpl.createVirtualMouse(1, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, BINDER); 740 mDeviceImpl.createVirtualMouse(2, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, BINDER); 741 mDeviceImpl.createVirtualMouse(3, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, BINDER); 742 mDeviceImpl.setShowPointerIcon(false); 743 verify(mInputManagerInternalMock, times(3)).setPointerIconVisible(eq(false), anyInt()); 744 verify(mInputManagerInternalMock, never()).setPointerIconVisible(eq(true), anyInt()); 745 mDeviceImpl.setShowPointerIcon(true); 746 verify(mInputManagerInternalMock, times(3)).setPointerIconVisible(eq(true), anyInt()); 747 } 748 749 @Test openNonBlockedAppOnVirtualDisplay_doesNotStartBlockedAlertActivity()750 public void openNonBlockedAppOnVirtualDisplay_doesNotStartBlockedAlertActivity() { 751 mDeviceImpl.onVirtualDisplayCreatedLocked( 752 mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); 753 GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get( 754 DISPLAY_ID); 755 doNothing().when(mContext).startActivityAsUser(any(), any(), any()); 756 757 ArrayList<ActivityInfo> activityInfos = getActivityInfoList( 758 NONBLOCKED_APP_PACKAGE_NAME, 759 NONBLOCKED_APP_PACKAGE_NAME, /* displayOnRemoveDevices */ true); 760 Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( 761 activityInfos.get(0), mAssociationInfo.getDisplayName()); 762 gwpc.canContainActivities(activityInfos, WindowConfiguration.WINDOWING_MODE_FULLSCREEN); 763 764 verify(mContext, never()).startActivityAsUser(argThat(intent -> 765 intent.filterEquals(blockedAppIntent)), any(), any()); 766 } 767 768 @Test openPermissionControllerOnVirtualDisplay_startBlockedAlertActivity()769 public void openPermissionControllerOnVirtualDisplay_startBlockedAlertActivity() { 770 mDeviceImpl.onVirtualDisplayCreatedLocked( 771 mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); 772 GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get( 773 DISPLAY_ID); 774 doNothing().when(mContext).startActivityAsUser(any(), any(), any()); 775 776 ArrayList<ActivityInfo> activityInfos = getActivityInfoList( 777 PERMISSION_CONTROLLER_PACKAGE_NAME, 778 PERMISSION_CONTROLLER_PACKAGE_NAME, /* displayOnRemoveDevices */ false); 779 Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( 780 activityInfos.get(0), mAssociationInfo.getDisplayName()); 781 gwpc.canContainActivities(activityInfos, WindowConfiguration.WINDOWING_MODE_FULLSCREEN); 782 783 verify(mContext).startActivityAsUser(argThat(intent -> 784 intent.filterEquals(blockedAppIntent)), any(), any()); 785 } 786 787 @Test openSettingsOnVirtualDisplay_startBlockedAlertActivity()788 public void openSettingsOnVirtualDisplay_startBlockedAlertActivity() { 789 mDeviceImpl.onVirtualDisplayCreatedLocked( 790 mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); 791 GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get( 792 DISPLAY_ID); 793 doNothing().when(mContext).startActivityAsUser(any(), any(), any()); 794 795 ArrayList<ActivityInfo> activityInfos = getActivityInfoList( 796 SETTINGS_PACKAGE_NAME, 797 SETTINGS_PACKAGE_NAME, /* displayOnRemoveDevices */ true); 798 Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( 799 activityInfos.get(0), mAssociationInfo.getDisplayName()); 800 gwpc.canContainActivities(activityInfos, WindowConfiguration.WINDOWING_MODE_FULLSCREEN); 801 802 verify(mContext).startActivityAsUser(argThat(intent -> 803 intent.filterEquals(blockedAppIntent)), any(), any()); 804 } 805 806 @Test openVendingOnVirtualDisplay_startBlockedAlertActivity()807 public void openVendingOnVirtualDisplay_startBlockedAlertActivity() { 808 mDeviceImpl.onVirtualDisplayCreatedLocked( 809 mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); 810 GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get( 811 DISPLAY_ID); 812 doNothing().when(mContext).startActivityAsUser(any(), any(), any()); 813 814 ArrayList<ActivityInfo> activityInfos = getActivityInfoList( 815 VENDING_PACKAGE_NAME, 816 VENDING_PACKAGE_NAME, /* displayOnRemoveDevices */ true); 817 Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( 818 activityInfos.get(0), mAssociationInfo.getDisplayName()); 819 gwpc.canContainActivities(activityInfos, WindowConfiguration.WINDOWING_MODE_FULLSCREEN); 820 821 verify(mContext).startActivityAsUser(argThat(intent -> 822 intent.filterEquals(blockedAppIntent)), any(), any()); 823 } 824 825 @Test openGoogleDialerOnVirtualDisplay_startBlockedAlertActivity()826 public void openGoogleDialerOnVirtualDisplay_startBlockedAlertActivity() { 827 mDeviceImpl.onVirtualDisplayCreatedLocked( 828 mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); 829 GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get( 830 DISPLAY_ID); 831 doNothing().when(mContext).startActivityAsUser(any(), any(), any()); 832 833 ArrayList<ActivityInfo> activityInfos = getActivityInfoList( 834 GOOGLE_DIALER_PACKAGE_NAME, 835 GOOGLE_DIALER_PACKAGE_NAME, /* displayOnRemoveDevices */ true); 836 Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( 837 activityInfos.get(0), mAssociationInfo.getDisplayName()); 838 gwpc.canContainActivities(activityInfos, WindowConfiguration.WINDOWING_MODE_FULLSCREEN); 839 840 verify(mContext).startActivityAsUser(argThat(intent -> 841 intent.filterEquals(blockedAppIntent)), any(), any()); 842 } 843 844 @Test openGoogleMapsOnVirtualDisplay_startBlockedAlertActivity()845 public void openGoogleMapsOnVirtualDisplay_startBlockedAlertActivity() { 846 mDeviceImpl.onVirtualDisplayCreatedLocked( 847 mDeviceImpl.createWindowPolicyController(), DISPLAY_ID); 848 GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get( 849 DISPLAY_ID); 850 doNothing().when(mContext).startActivityAsUser(any(), any(), any()); 851 852 ArrayList<ActivityInfo> activityInfos = getActivityInfoList( 853 GOOGLE_MAPS_PACKAGE_NAME, 854 GOOGLE_MAPS_PACKAGE_NAME, /* displayOnRemoveDevices */ true); 855 Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( 856 activityInfos.get(0), mAssociationInfo.getDisplayName()); 857 gwpc.canContainActivities(activityInfos, WindowConfiguration.WINDOWING_MODE_FULLSCREEN); 858 859 verify(mContext).startActivityAsUser(argThat(intent -> 860 intent.filterEquals(blockedAppIntent)), any(), any()); 861 } 862 } 863