| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.companion.virtual; |
| |
| import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM; |
| import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT; |
| import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS; |
| import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_SENSORS; |
| import static android.content.Context.DEVICE_ID_DEFAULT; |
| import static android.content.Context.DEVICE_ID_INVALID; |
| import static android.content.Intent.ACTION_VIEW; |
| import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES; |
| import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.common.truth.Truth.assertWithMessage; |
| |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.anyBoolean; |
| import static org.mockito.ArgumentMatchers.anyFloat; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.ArgumentMatchers.anyString; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.ArgumentMatchers.nullable; |
| import static org.mockito.Mockito.argThat; |
| import static org.mockito.Mockito.clearInvocations; |
| import static org.mockito.Mockito.doNothing; |
| import static org.mockito.Mockito.doReturn; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.verifyNoMoreInteractions; |
| import static org.mockito.Mockito.when; |
| import static org.testng.Assert.assertThrows; |
| |
| import android.Manifest; |
| import android.app.WindowConfiguration; |
| import android.app.admin.DevicePolicyManager; |
| import android.companion.AssociationInfo; |
| import android.companion.virtual.IVirtualDeviceActivityListener; |
| import android.companion.virtual.IVirtualDeviceIntentInterceptor; |
| import android.companion.virtual.IVirtualDeviceSoundEffectListener; |
| import android.companion.virtual.VirtualDeviceParams; |
| import android.companion.virtual.audio.IAudioConfigChangedCallback; |
| import android.companion.virtual.audio.IAudioRoutingCallback; |
| import android.companion.virtual.flags.Flags; |
| import android.companion.virtual.sensor.VirtualSensor; |
| import android.companion.virtual.sensor.VirtualSensorCallback; |
| import android.companion.virtual.sensor.VirtualSensorConfig; |
| import android.content.AttributionSource; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.ContextWrapper; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.PackageManager; |
| import android.hardware.Sensor; |
| import android.hardware.display.DisplayManagerGlobal; |
| import android.hardware.display.DisplayManagerInternal; |
| import android.hardware.display.IDisplayManager; |
| import android.hardware.display.IVirtualDisplayCallback; |
| import android.hardware.display.VirtualDisplayConfig; |
| import android.hardware.input.IInputManager; |
| import android.hardware.input.VirtualDpadConfig; |
| import android.hardware.input.VirtualKeyEvent; |
| import android.hardware.input.VirtualKeyboardConfig; |
| import android.hardware.input.VirtualMouseButtonEvent; |
| import android.hardware.input.VirtualMouseConfig; |
| import android.hardware.input.VirtualMouseRelativeEvent; |
| import android.hardware.input.VirtualMouseScrollEvent; |
| import android.hardware.input.VirtualNavigationTouchpadConfig; |
| import android.hardware.input.VirtualTouchEvent; |
| import android.hardware.input.VirtualTouchscreenConfig; |
| import android.media.AudioManager; |
| import android.net.MacAddress; |
| import android.net.Uri; |
| import android.os.Binder; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.IPowerManager; |
| import android.os.IThermalService; |
| import android.os.LocaleList; |
| import android.os.PowerManager; |
| import android.os.Process; |
| import android.os.RemoteException; |
| import android.os.WorkSource; |
| import android.platform.test.annotations.Presubmit; |
| import android.platform.test.flag.junit.SetFlagsRule; |
| import android.testing.AndroidTestingRunner; |
| import android.testing.TestableLooper; |
| import android.util.ArraySet; |
| import android.view.Display; |
| import android.view.DisplayInfo; |
| import android.view.KeyEvent; |
| import android.view.WindowManager; |
| |
| import androidx.test.platform.app.InstrumentationRegistry; |
| |
| import com.android.compatibility.common.util.AdoptShellPermissionsRule; |
| import com.android.internal.app.BlockedAppStreamingActivity; |
| import com.android.internal.os.BackgroundThread; |
| import com.android.server.LocalServices; |
| import com.android.server.companion.virtual.camera.VirtualCameraController; |
| import com.android.server.input.InputManagerInternal; |
| import com.android.server.sensors.SensorManagerInternal; |
| |
| import com.google.android.collect.Sets; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.Mock; |
| import org.mockito.Mockito; |
| import org.mockito.MockitoAnnotations; |
| |
| import java.util.Arrays; |
| import java.util.Set; |
| import java.util.function.Consumer; |
| |
| @Presubmit |
| @RunWith(AndroidTestingRunner.class) |
| @TestableLooper.RunWithLooper(setAsMainLooper = true) |
| public class VirtualDeviceManagerServiceTest { |
| |
| private static final String NONBLOCKED_APP_PACKAGE_NAME = "com.someapp"; |
| private static final String PERMISSION_CONTROLLER_PACKAGE_NAME = |
| "com.android.permissioncontroller"; |
| private static final String SETTINGS_PACKAGE_NAME = "com.android.settings"; |
| private static final String VENDING_PACKAGE_NAME = "com.android.vending"; |
| private static final String GOOGLE_DIALER_PACKAGE_NAME = "com.google.android.dialer"; |
| private static final String GOOGLE_MAPS_PACKAGE_NAME = "com.google.android.apps.maps"; |
| private static final String DEVICE_NAME_1 = "device name 1"; |
| private static final String DEVICE_NAME_2 = "device name 2"; |
| private static final String DEVICE_NAME_3 = "device name 3"; |
| private static final int DISPLAY_ID_1 = 2; |
| private static final int DISPLAY_ID_2 = 3; |
| private static final int NON_EXISTENT_DISPLAY_ID = 42; |
| private static final int DEVICE_OWNER_UID_1 = 50; |
| private static final int DEVICE_OWNER_UID_2 = 51; |
| private static final int UID_1 = 0; |
| private static final int UID_2 = 10; |
| private static final int UID_3 = 10000; |
| private static final int UID_4 = 10001; |
| private static final int PRODUCT_ID = 10; |
| private static final int VENDOR_ID = 5; |
| private static final String UNIQUE_ID = "uniqueid"; |
| private static final String PHYS = "phys"; |
| private static final int INPUT_DEVICE_ID = 53; |
| private static final int HEIGHT = 1800; |
| private static final int WIDTH = 900; |
| private static final int SENSOR_HANDLE = 64; |
| private static final Binder BINDER = new Binder("binder"); |
| private static final int FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES = 0x00000; |
| private static final int VIRTUAL_DEVICE_ID_1 = 42; |
| private static final int VIRTUAL_DEVICE_ID_2 = 43; |
| private static final VirtualDisplayConfig VIRTUAL_DISPLAY_CONFIG = |
| new VirtualDisplayConfig.Builder("virtual_display", 640, 480, 400).build(); |
| private static final VirtualDpadConfig DPAD_CONFIG = |
| new VirtualDpadConfig.Builder() |
| .setVendorId(VENDOR_ID) |
| .setProductId(PRODUCT_ID) |
| .setInputDeviceName(DEVICE_NAME_1) |
| .setAssociatedDisplayId(DISPLAY_ID_1) |
| .build(); |
| private static final VirtualKeyboardConfig KEYBOARD_CONFIG = |
| new VirtualKeyboardConfig.Builder() |
| .setVendorId(VENDOR_ID) |
| .setProductId(PRODUCT_ID) |
| .setInputDeviceName(DEVICE_NAME_1) |
| .setAssociatedDisplayId(DISPLAY_ID_1) |
| .setLanguageTag(VirtualKeyboardConfig.DEFAULT_LANGUAGE_TAG) |
| .setLayoutType(VirtualKeyboardConfig.DEFAULT_LAYOUT_TYPE) |
| .build(); |
| private static final VirtualMouseConfig MOUSE_CONFIG = |
| new VirtualMouseConfig.Builder() |
| .setVendorId(VENDOR_ID) |
| .setProductId(PRODUCT_ID) |
| .setInputDeviceName(DEVICE_NAME_1) |
| .setAssociatedDisplayId(DISPLAY_ID_1) |
| .build(); |
| private static final VirtualTouchscreenConfig TOUCHSCREEN_CONFIG = |
| new VirtualTouchscreenConfig.Builder(WIDTH, HEIGHT) |
| .setVendorId(VENDOR_ID) |
| .setProductId(PRODUCT_ID) |
| .setInputDeviceName(DEVICE_NAME_1) |
| .setAssociatedDisplayId(DISPLAY_ID_1) |
| .build(); |
| private static final VirtualNavigationTouchpadConfig NAVIGATION_TOUCHPAD_CONFIG = |
| new VirtualNavigationTouchpadConfig.Builder(WIDTH, HEIGHT) |
| .setVendorId(VENDOR_ID) |
| .setProductId(PRODUCT_ID) |
| .setInputDeviceName(DEVICE_NAME_1) |
| .setAssociatedDisplayId(DISPLAY_ID_1) |
| .build(); |
| private static final String TEST_SITE = "http://test"; |
| |
| @Rule |
| public SetFlagsRule mSetFlagsRule = new SetFlagsRule(); |
| |
| @Rule |
| public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule( |
| InstrumentationRegistry.getInstrumentation().getUiAutomation(), |
| Manifest.permission.CREATE_VIRTUAL_DEVICE); |
| |
| private Context mContext; |
| private InputManagerMockHelper mInputManagerMockHelper; |
| private VirtualDeviceImpl mDeviceImpl; |
| private InputController mInputController; |
| private SensorController mSensorController; |
| private CameraAccessController mCameraAccessController; |
| private AssociationInfo mAssociationInfo; |
| private VirtualDeviceManagerService mVdms; |
| private VirtualDeviceManagerInternal mLocalService; |
| private VirtualDeviceManagerService.VirtualDeviceManagerImpl mVdm; |
| private VirtualDeviceManagerService.VirtualDeviceManagerNativeImpl mVdmNative; |
| private VirtualDeviceLog mVirtualDeviceLog; |
| @Mock |
| private InputController.NativeWrapper mNativeWrapperMock; |
| @Mock |
| private DisplayManagerInternal mDisplayManagerInternalMock; |
| @Mock |
| private IDisplayManager mIDisplayManager; |
| @Mock |
| private VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback; |
| @Mock |
| private DevicePolicyManager mDevicePolicyManagerMock; |
| @Mock |
| private InputManagerInternal mInputManagerInternalMock; |
| @Mock |
| private SensorManagerInternal mSensorManagerInternalMock; |
| @Mock |
| private VirtualSensorCallback mSensorCallback; |
| @Mock |
| private IVirtualDeviceActivityListener mActivityListener; |
| @Mock |
| private IVirtualDeviceSoundEffectListener mSoundEffectListener; |
| @Mock |
| private IVirtualDisplayCallback mVirtualDisplayCallback; |
| @Mock |
| private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback; |
| @Mock |
| private VirtualDeviceManagerInternal.VirtualDisplayListener mDisplayListener; |
| @Mock |
| private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener mAppsOnVirtualDeviceListener; |
| @Mock |
| IPowerManager mIPowerManagerMock; |
| @Mock |
| IThermalService mIThermalServiceMock; |
| @Mock |
| private IAudioRoutingCallback mRoutingCallback; |
| @Mock |
| private IAudioConfigChangedCallback mConfigChangedCallback; |
| @Mock |
| private CameraAccessController.CameraAccessBlockedCallback mCameraAccessBlockedCallback; |
| @Mock |
| private ApplicationInfo mApplicationInfoMock; |
| @Mock |
| IInputManager mIInputManagerMock; |
| |
| private ArraySet<ComponentName> getBlockedActivities() { |
| ArraySet<ComponentName> blockedActivities = new ArraySet<>(); |
| blockedActivities.add(new ComponentName(SETTINGS_PACKAGE_NAME, SETTINGS_PACKAGE_NAME)); |
| blockedActivities.add(new ComponentName(VENDING_PACKAGE_NAME, VENDING_PACKAGE_NAME)); |
| blockedActivities.add( |
| new ComponentName(GOOGLE_DIALER_PACKAGE_NAME, GOOGLE_DIALER_PACKAGE_NAME)); |
| blockedActivities.add( |
| new ComponentName(GOOGLE_MAPS_PACKAGE_NAME, GOOGLE_MAPS_PACKAGE_NAME)); |
| return blockedActivities; |
| } |
| |
| private Intent createRestrictedActivityBlockedIntent(Set<String> displayCategories, |
| String targetDisplayCategory) { |
| when(mDisplayManagerInternalMock.createVirtualDisplay(any(), any(), any(), any(), |
| eq(NONBLOCKED_APP_PACKAGE_NAME))).thenReturn(DISPLAY_ID_1); |
| VirtualDisplayConfig config = new VirtualDisplayConfig.Builder("display", 640, 480, |
| 420).setDisplayCategories(displayCategories).build(); |
| mDeviceImpl.createVirtualDisplay(config, mVirtualDisplayCallback, |
| NONBLOCKED_APP_PACKAGE_NAME); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| doNothing().when(mContext).startActivityAsUser(any(), any(), any()); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices= */ true, |
| targetDisplayCategory); |
| Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( |
| activityInfo, mAssociationInfo.getDisplayName()); |
| gwpc.canActivityBeLaunched(activityInfo, blockedAppIntent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false); |
| return blockedAppIntent; |
| } |
| |
| |
| private ActivityInfo getActivityInfo( |
| String packageName, String name, boolean displayOnRemoteDevices, |
| String requiredDisplayCategory) { |
| ActivityInfo activityInfo = new ActivityInfo(); |
| activityInfo.packageName = packageName; |
| activityInfo.name = name; |
| activityInfo.flags = displayOnRemoteDevices |
| ? FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES : FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES; |
| activityInfo.applicationInfo = mApplicationInfoMock; |
| activityInfo.requiredDisplayCategory = requiredDisplayCategory; |
| return activityInfo; |
| } |
| |
| @Before |
| public void setUp() throws Exception { |
| MockitoAnnotations.initMocks(this); |
| |
| LocalServices.removeServiceForTest(DisplayManagerInternal.class); |
| LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock); |
| |
| mSetFlagsRule.initAllFlagsToReleaseConfigDefault(); |
| |
| doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt()); |
| doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt()); |
| doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt()); |
| LocalServices.removeServiceForTest(InputManagerInternal.class); |
| LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock); |
| |
| LocalServices.removeServiceForTest(SensorManagerInternal.class); |
| LocalServices.addService(SensorManagerInternal.class, mSensorManagerInternalMock); |
| |
| final DisplayInfo displayInfo = new DisplayInfo(); |
| displayInfo.uniqueId = UNIQUE_ID; |
| doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt()); |
| LocalServices.removeServiceForTest(DisplayManagerInternal.class); |
| LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock); |
| |
| mContext = Mockito.spy(new ContextWrapper( |
| InstrumentationRegistry.getInstrumentation().getTargetContext())); |
| doReturn(mContext).when(mContext).createContextAsUser(eq(Process.myUserHandle()), anyInt()); |
| doNothing().when(mContext).sendBroadcastAsUser(any(), any()); |
| when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn( |
| mDevicePolicyManagerMock); |
| |
| PowerManager powerManager = new PowerManager(mContext, mIPowerManagerMock, |
| mIThermalServiceMock, |
| new Handler(TestableLooper.get(this).getLooper())); |
| when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager); |
| |
| mInputManagerMockHelper = new InputManagerMockHelper( |
| TestableLooper.get(this), mNativeWrapperMock, mIInputManagerMock); |
| // Allow virtual devices to be created on the looper thread for testing. |
| final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true; |
| mInputController = new InputController(mNativeWrapperMock, |
| new Handler(TestableLooper.get(this).getLooper()), |
| mContext.getSystemService(WindowManager.class), threadVerifier); |
| mCameraAccessController = |
| new CameraAccessController(mContext, mLocalService, mCameraAccessBlockedCallback); |
| |
| mAssociationInfo = new AssociationInfo(/* associationId= */ 1, 0, null, |
| null, MacAddress.BROADCAST_ADDRESS, "", null, null, true, false, false, |
| 0, 0, -1); |
| |
| mVdms = new VirtualDeviceManagerService(mContext); |
| mLocalService = mVdms.getLocalServiceInstance(); |
| mVdm = mVdms.new VirtualDeviceManagerImpl(); |
| mVdmNative = mVdms.new VirtualDeviceManagerNativeImpl(); |
| mVirtualDeviceLog = new VirtualDeviceLog(mContext); |
| mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1); |
| mSensorController = mDeviceImpl.getSensorControllerForTest(); |
| } |
| |
| @After |
| public void tearDown() { |
| mDeviceImpl.close(); |
| mInputManagerMockHelper.tearDown(); |
| } |
| |
| @Test |
| public void getDeviceIdForDisplayId_invalidDisplayId_returnsDefault() { |
| assertThat(mVdm.getDeviceIdForDisplayId(Display.INVALID_DISPLAY)) |
| .isEqualTo(DEVICE_ID_DEFAULT); |
| } |
| |
| @Test |
| public void getDeviceIdForDisplayId_defaultDisplayId_returnsDefault() { |
| assertThat(mVdm.getDeviceIdForDisplayId(Display.DEFAULT_DISPLAY)) |
| .isEqualTo(DEVICE_ID_DEFAULT); |
| } |
| |
| @Test |
| public void getDeviceIdForDisplayId_nonExistentDisplayId_returnsDefault() { |
| assertThat(mVdm.getDeviceIdForDisplayId(NON_EXISTENT_DISPLAY_ID)) |
| .isEqualTo(DEVICE_ID_DEFAULT); |
| } |
| |
| @Test |
| public void getDeviceIdForDisplayId_withValidVirtualDisplayId_returnsDeviceId() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| |
| assertThat(mVdm.getDeviceIdForDisplayId(DISPLAY_ID_1)) |
| .isEqualTo(mDeviceImpl.getDeviceId()); |
| } |
| |
| @Test |
| public void isDeviceIdValid_defaultDeviceId_returnsFalse() { |
| assertThat(mVdm.isValidVirtualDeviceId(DEVICE_ID_DEFAULT)).isFalse(); |
| } |
| |
| @Test |
| public void isDeviceIdValid_validVirtualDeviceId_returnsTrue() { |
| assertThat(mVdm.isValidVirtualDeviceId(mDeviceImpl.getDeviceId())).isTrue(); |
| } |
| |
| @Test |
| public void isDeviceIdValid_nonExistentDeviceId_returnsFalse() { |
| assertThat(mVdm.isValidVirtualDeviceId(mDeviceImpl.getDeviceId() + 1)).isFalse(); |
| } |
| |
| @Test |
| public void getDevicePolicy_invalidDeviceId_returnsDefault() { |
| assertThat(mVdm.getDevicePolicy(DEVICE_ID_INVALID, POLICY_TYPE_SENSORS)) |
| .isEqualTo(DEVICE_POLICY_DEFAULT); |
| assertThat(mVdmNative.getDevicePolicy(DEVICE_ID_INVALID, POLICY_TYPE_SENSORS)) |
| .isEqualTo(DEVICE_POLICY_DEFAULT); |
| } |
| |
| @Test |
| public void getDevicePolicy_defaultDeviceId_returnsDefault() { |
| assertThat(mVdm.getDevicePolicy(DEVICE_ID_DEFAULT, POLICY_TYPE_SENSORS)) |
| .isEqualTo(DEVICE_POLICY_DEFAULT); |
| assertThat(mVdmNative.getDevicePolicy(DEVICE_ID_DEFAULT, POLICY_TYPE_SENSORS)) |
| .isEqualTo(DEVICE_POLICY_DEFAULT); |
| } |
| |
| @Test |
| public void getDevicePolicy_nonExistentDeviceId_returnsDefault() { |
| assertThat(mVdm.getDevicePolicy(mDeviceImpl.getDeviceId() + 1, POLICY_TYPE_SENSORS)) |
| .isEqualTo(DEVICE_POLICY_DEFAULT); |
| assertThat(mVdmNative.getDevicePolicy(mDeviceImpl.getDeviceId() + 1, POLICY_TYPE_SENSORS)) |
| .isEqualTo(DEVICE_POLICY_DEFAULT); |
| } |
| |
| @Test |
| public void getDevicePolicy_unspecifiedPolicy_returnsDefault() { |
| assertThat(mVdm.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS)) |
| .isEqualTo(DEVICE_POLICY_DEFAULT); |
| assertThat(mVdmNative.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS)) |
| .isEqualTo(DEVICE_POLICY_DEFAULT); |
| } |
| |
| @Test |
| public void getDevicePolicy_returnsCustom() { |
| VirtualDeviceParams params = new VirtualDeviceParams |
| .Builder() |
| .setBlockedActivities(getBlockedActivities()) |
| .setDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM) |
| .build(); |
| mDeviceImpl.close(); |
| mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params); |
| |
| assertThat(mVdm.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS)) |
| .isEqualTo(DEVICE_POLICY_CUSTOM); |
| assertThat(mVdmNative.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS)) |
| .isEqualTo(DEVICE_POLICY_CUSTOM); |
| } |
| |
| @Test |
| public void getDevicePolicy_defaultRecentsPolicy_gwpcCanShowRecentsOnHostDevice() { |
| VirtualDeviceParams params = new VirtualDeviceParams |
| .Builder() |
| .build(); |
| mDeviceImpl.close(); |
| mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params); |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| |
| GenericWindowPolicyController gwpc = |
| mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1); |
| assertThat(gwpc.canShowTasksInHostDeviceRecents()).isTrue(); |
| } |
| |
| @Test |
| public void getDevicePolicy_customRecentsPolicy_gwpcCannotShowRecentsOnHostDevice() { |
| VirtualDeviceParams params = new VirtualDeviceParams |
| .Builder() |
| .setDevicePolicy(POLICY_TYPE_RECENTS, DEVICE_POLICY_CUSTOM) |
| .build(); |
| mDeviceImpl.close(); |
| mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params); |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| |
| GenericWindowPolicyController gwpc = |
| mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1); |
| assertThat(gwpc.canShowTasksInHostDeviceRecents()).isFalse(); |
| } |
| |
| @Test |
| public void getDeviceOwnerUid_oneDevice_returnsCorrectId() { |
| int ownerUid = mLocalService.getDeviceOwnerUid(mDeviceImpl.getDeviceId()); |
| assertThat(ownerUid).isEqualTo(mDeviceImpl.getOwnerUid()); |
| } |
| |
| @Test |
| public void getDeviceOwnerUid_twoDevices_returnsCorrectId() { |
| createVirtualDevice(VIRTUAL_DEVICE_ID_2, DEVICE_OWNER_UID_2); |
| |
| int secondDeviceOwner = mLocalService.getDeviceOwnerUid(VIRTUAL_DEVICE_ID_2); |
| assertThat(secondDeviceOwner).isEqualTo(DEVICE_OWNER_UID_2); |
| |
| int firstDeviceOwner = mLocalService.getDeviceOwnerUid(VIRTUAL_DEVICE_ID_1); |
| assertThat(firstDeviceOwner).isEqualTo(DEVICE_OWNER_UID_1); |
| } |
| |
| @Test |
| public void getDeviceOwnerUid_nonExistentDevice_returnsInvalidUid() { |
| int nonExistentDeviceId = DEVICE_ID_DEFAULT; |
| int ownerUid = mLocalService.getDeviceOwnerUid(nonExistentDeviceId); |
| assertThat(ownerUid).isEqualTo(Process.INVALID_UID); |
| } |
| |
| @Test |
| public void getVirtualSensor_defaultDeviceId_returnsNull() { |
| assertThat(mLocalService.getVirtualSensor(DEVICE_ID_DEFAULT, SENSOR_HANDLE)).isNull(); |
| } |
| |
| @Test |
| public void getVirtualSensor_invalidDeviceId_returnsNull() { |
| assertThat(mLocalService.getVirtualSensor(DEVICE_ID_INVALID, SENSOR_HANDLE)).isNull(); |
| } |
| |
| @Test |
| public void getVirtualSensor_noSensors_returnsNull() { |
| assertThat(mLocalService.getVirtualSensor(VIRTUAL_DEVICE_ID_1, SENSOR_HANDLE)).isNull(); |
| } |
| |
| @Test |
| public void getVirtualSensor_returnsCorrectSensor() { |
| VirtualDeviceParams params = new VirtualDeviceParams.Builder() |
| .setDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM) |
| .addVirtualSensorConfig( |
| new VirtualSensorConfig.Builder(Sensor.TYPE_ACCELEROMETER, DEVICE_NAME_1) |
| .build()) |
| .setVirtualSensorCallback(BackgroundThread.getExecutor(), mSensorCallback) |
| .build(); |
| |
| doReturn(SENSOR_HANDLE).when(mSensorManagerInternalMock).createRuntimeSensor( |
| anyInt(), anyInt(), anyString(), anyString(), anyFloat(), anyFloat(), anyFloat(), |
| anyInt(), anyInt(), anyInt(), any()); |
| mDeviceImpl.close(); |
| mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params); |
| |
| VirtualSensor sensor = mLocalService.getVirtualSensor(VIRTUAL_DEVICE_ID_1, SENSOR_HANDLE); |
| assertThat(sensor).isNotNull(); |
| assertThat(sensor.getDeviceId()).isEqualTo(VIRTUAL_DEVICE_ID_1); |
| assertThat(sensor.getHandle()).isEqualTo(SENSOR_HANDLE); |
| assertThat(sensor.getType()).isEqualTo(Sensor.TYPE_ACCELEROMETER); |
| } |
| |
| @Test |
| public void getDeviceIdsForUid_noRunningApps_returnsNull() { |
| assertThat(mLocalService.getDeviceIdsForUid(UID_1)).isEmpty(); |
| assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).isEmpty(); |
| } |
| |
| @Test |
| public void getDeviceIdsForUid_differentUidOnDevice_returnsNull() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged( |
| Sets.newArraySet(UID_2)); |
| |
| assertThat(mLocalService.getDeviceIdsForUid(UID_1)).isEmpty(); |
| assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).isEmpty(); |
| } |
| |
| @Test |
| public void getDeviceIdsForUid_oneUidOnDevice_returnsCorrectId() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged( |
| Sets.newArraySet(UID_1)); |
| |
| int deviceId = mDeviceImpl.getDeviceId(); |
| assertThat(mLocalService.getDeviceIdsForUid(UID_1)).containsExactly(deviceId); |
| assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).asList().containsExactly(deviceId); |
| } |
| |
| @Test |
| public void getDeviceIdsForUid_twoUidsOnDevice_returnsCorrectId() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| |
| mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged( |
| Sets.newArraySet(UID_1, UID_2)); |
| |
| int deviceId = mDeviceImpl.getDeviceId(); |
| assertThat(mLocalService.getDeviceIdsForUid(UID_1)).containsExactly(deviceId); |
| assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).asList().containsExactly(deviceId); |
| } |
| |
| @Test |
| public void getDeviceIdsForUid_twoDevicesUidOnOne_returnsCorrectId() { |
| VirtualDeviceImpl secondDevice = createVirtualDevice(VIRTUAL_DEVICE_ID_2, |
| DEVICE_OWNER_UID_2); |
| addVirtualDisplay(secondDevice, DISPLAY_ID_2); |
| |
| secondDevice.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_2).onRunningAppsChanged( |
| Sets.newArraySet(UID_1)); |
| |
| int deviceId = secondDevice.getDeviceId(); |
| assertThat(mLocalService.getDeviceIdsForUid(UID_1)).containsExactly(deviceId); |
| assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).asList().containsExactly(deviceId); |
| } |
| |
| @Test |
| public void getDeviceIdsForUid_twoDevicesUidOnBoth_returnsCorrectId() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| VirtualDeviceImpl secondDevice = createVirtualDevice(VIRTUAL_DEVICE_ID_2, |
| DEVICE_OWNER_UID_2); |
| addVirtualDisplay(secondDevice, DISPLAY_ID_2); |
| |
| |
| mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged( |
| Sets.newArraySet(UID_1)); |
| secondDevice.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_2).onRunningAppsChanged( |
| Sets.newArraySet(UID_1, UID_2)); |
| |
| assertThat(mLocalService.getDeviceIdsForUid(UID_1)).containsExactly( |
| mDeviceImpl.getDeviceId(), secondDevice.getDeviceId()); |
| assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).asList().containsExactly( |
| mDeviceImpl.getDeviceId(), secondDevice.getDeviceId()); |
| } |
| |
| @Test |
| public void getPreferredLocaleListForApp_keyboardAttached_returnLocaleHints() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER); |
| |
| mVdms.notifyRunningAppsChanged(mDeviceImpl.getDeviceId(), Sets.newArraySet(UID_1)); |
| |
| LocaleList localeList = mLocalService.getPreferredLocaleListForUid(UID_1); |
| assertThat(localeList).isEqualTo( |
| LocaleList.forLanguageTags(KEYBOARD_CONFIG.getLanguageTag())); |
| } |
| |
| @Test |
| public void getPreferredLocaleListForApp_noKeyboardAttached_nullLocaleHints() { |
| mVdms.notifyRunningAppsChanged(mDeviceImpl.getDeviceId(), Sets.newArraySet(UID_1)); |
| |
| // no preceding call to createVirtualKeyboard() |
| assertThat(mLocalService.getPreferredLocaleListForUid(UID_1)).isNull(); |
| } |
| |
| @Test |
| public void getPreferredLocaleListForApp_appOnMultipleVD_localeOnFirstVDReturned() { |
| VirtualDeviceImpl secondDevice = createVirtualDevice(VIRTUAL_DEVICE_ID_2, |
| DEVICE_OWNER_UID_2); |
| Binder secondBinder = new Binder("secondBinder"); |
| VirtualKeyboardConfig firstKeyboardConfig = |
| new VirtualKeyboardConfig.Builder() |
| .setVendorId(VENDOR_ID) |
| .setProductId(PRODUCT_ID) |
| .setInputDeviceName(DEVICE_NAME_1) |
| .setAssociatedDisplayId(DISPLAY_ID_1) |
| .setLanguageTag("zh-CN") |
| .build(); |
| VirtualKeyboardConfig secondKeyboardConfig = |
| new VirtualKeyboardConfig.Builder() |
| .setVendorId(VENDOR_ID) |
| .setProductId(PRODUCT_ID) |
| .setInputDeviceName(DEVICE_NAME_2) |
| .setAssociatedDisplayId(DISPLAY_ID_2) |
| .setLanguageTag("fr-FR") |
| .build(); |
| |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| addVirtualDisplay(secondDevice, DISPLAY_ID_2); |
| |
| mDeviceImpl.createVirtualKeyboard(firstKeyboardConfig, BINDER); |
| secondDevice.createVirtualKeyboard(secondKeyboardConfig, secondBinder); |
| |
| mVdms.notifyRunningAppsChanged(mDeviceImpl.getDeviceId(), Sets.newArraySet(UID_1)); |
| mVdms.notifyRunningAppsChanged(secondDevice.getDeviceId(), Sets.newArraySet(UID_1)); |
| |
| LocaleList localeList = mLocalService.getPreferredLocaleListForUid(UID_1); |
| assertThat(localeList).isEqualTo( |
| LocaleList.forLanguageTags(firstKeyboardConfig.getLanguageTag())); |
| } |
| |
| @Test |
| public void cameraAccessController_observerCountUpdated() { |
| assertThat(mCameraAccessController.getObserverCount()).isEqualTo(1); |
| |
| VirtualDeviceImpl secondDevice = |
| createVirtualDevice(VIRTUAL_DEVICE_ID_2, DEVICE_OWNER_UID_2); |
| assertThat(mCameraAccessController.getObserverCount()).isEqualTo(2); |
| |
| mDeviceImpl.close(); |
| assertThat(mCameraAccessController.getObserverCount()).isEqualTo(1); |
| |
| secondDevice.close(); |
| assertThat(mCameraAccessController.getObserverCount()).isEqualTo(0); |
| } |
| |
| @Test |
| public void onVirtualDisplayRemovedLocked_doesNotThrowException() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| // This call should not throw any exceptions. |
| mDeviceImpl.onVirtualDisplayRemoved(DISPLAY_ID_1); |
| } |
| |
| @Test |
| public void onVirtualDisplayCreatedLocked_listenersNotified() { |
| mLocalService.registerVirtualDisplayListener(mDisplayListener); |
| |
| mLocalService.onVirtualDisplayCreated(DISPLAY_ID_1); |
| TestableLooper.get(this).processAllMessages(); |
| |
| verify(mDisplayListener).onVirtualDisplayCreated(DISPLAY_ID_1); |
| } |
| |
| @Test |
| public void onVirtualDisplayRemovedLocked_listenersNotified() { |
| mLocalService.registerVirtualDisplayListener(mDisplayListener); |
| |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| |
| mLocalService.onVirtualDisplayRemoved(mDeviceImpl, DISPLAY_ID_1); |
| TestableLooper.get(this).processAllMessages(); |
| |
| verify(mDisplayListener).onVirtualDisplayRemoved(DISPLAY_ID_1); |
| } |
| |
| @Test |
| public void onAppsOnVirtualDeviceChanged_singleVirtualDevice_listenersNotified() { |
| ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2)); |
| mLocalService.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener); |
| |
| mVdms.notifyRunningAppsChanged(mDeviceImpl.getDeviceId(), uids); |
| TestableLooper.get(this).processAllMessages(); |
| |
| verify(mAppsOnVirtualDeviceListener).onAppsOnAnyVirtualDeviceChanged(uids); |
| } |
| |
| @Test |
| public void onAppsOnVirtualDeviceChanged_multipleVirtualDevices_listenersNotified() { |
| createVirtualDevice(VIRTUAL_DEVICE_ID_2, DEVICE_OWNER_UID_2); |
| |
| ArraySet<Integer> uidsOnDevice1 = new ArraySet<>(Arrays.asList(UID_1, UID_2)); |
| ArraySet<Integer> uidsOnDevice2 = new ArraySet<>(Arrays.asList(UID_3, UID_4)); |
| mLocalService.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener); |
| |
| // Notifies that the running apps on the first virtual device has changed. |
| mVdms.notifyRunningAppsChanged(mDeviceImpl.getDeviceId(), uidsOnDevice1); |
| TestableLooper.get(this).processAllMessages(); |
| verify(mAppsOnVirtualDeviceListener).onAppsOnAnyVirtualDeviceChanged( |
| new ArraySet<>(Arrays.asList(UID_1, UID_2))); |
| |
| // Notifies that the running apps on the second virtual device has changed. |
| mVdms.notifyRunningAppsChanged(VIRTUAL_DEVICE_ID_2, uidsOnDevice2); |
| TestableLooper.get(this).processAllMessages(); |
| // The union of the apps running on both virtual devices are sent to the listeners. |
| verify(mAppsOnVirtualDeviceListener).onAppsOnAnyVirtualDeviceChanged( |
| new ArraySet<>(Arrays.asList(UID_1, UID_2, UID_3, UID_4))); |
| |
| // Notifies that the running apps on the first virtual device has changed again. |
| uidsOnDevice1.remove(UID_2); |
| mVdms.notifyRunningAppsChanged(mDeviceImpl.getDeviceId(), uidsOnDevice1); |
| mLocalService.onAppsOnVirtualDeviceChanged(); |
| TestableLooper.get(this).processAllMessages(); |
| // The union of the apps running on both virtual devices are sent to the listeners. |
| verify(mAppsOnVirtualDeviceListener).onAppsOnAnyVirtualDeviceChanged( |
| new ArraySet<>(Arrays.asList(UID_1, UID_3, UID_4))); |
| |
| // Notifies that the running apps on the first virtual device has changed but with the same |
| // set of UIDs. |
| mVdms.notifyRunningAppsChanged(mDeviceImpl.getDeviceId(), uidsOnDevice1); |
| mLocalService.onAppsOnVirtualDeviceChanged(); |
| TestableLooper.get(this).processAllMessages(); |
| // Listeners should not be notified. |
| verifyNoMoreInteractions(mAppsOnVirtualDeviceListener); |
| } |
| |
| @Test |
| public void onVirtualDisplayCreatedLocked_wakeLockIsAcquired() throws RemoteException { |
| verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(), |
| nullable(String.class), nullable(String.class), nullable(WorkSource.class), |
| nullable(String.class), anyInt(), eq(null)); |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| verify(mIPowerManagerMock).acquireWakeLock(any(Binder.class), anyInt(), |
| nullable(String.class), nullable(String.class), nullable(WorkSource.class), |
| nullable(String.class), eq(DISPLAY_ID_1), eq(null)); |
| } |
| |
| @Test |
| public void onVirtualDisplayCreatedLocked_duplicateCalls_onlyOneWakeLockIsAcquired() |
| throws RemoteException { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| assertThrows(IllegalStateException.class, |
| () -> addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1)); |
| TestableLooper.get(this).processAllMessages(); |
| verify(mIPowerManagerMock).acquireWakeLock(any(Binder.class), anyInt(), |
| nullable(String.class), nullable(String.class), nullable(WorkSource.class), |
| nullable(String.class), eq(DISPLAY_ID_1), eq(null)); |
| } |
| |
| @Test |
| public void onVirtualDisplayRemovedLocked_wakeLockIsReleased() throws RemoteException { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class); |
| TestableLooper.get(this).processAllMessages(); |
| verify(mIPowerManagerMock).acquireWakeLock(wakeLockCaptor.capture(), |
| anyInt(), |
| nullable(String.class), nullable(String.class), nullable(WorkSource.class), |
| nullable(String.class), eq(DISPLAY_ID_1), eq(null)); |
| |
| IBinder wakeLock = wakeLockCaptor.getValue(); |
| mDeviceImpl.onVirtualDisplayRemoved(DISPLAY_ID_1); |
| verify(mIPowerManagerMock).releaseWakeLock(eq(wakeLock), anyInt()); |
| } |
| |
| @Test |
| public void addVirtualDisplay_displayNotReleased_wakeLockIsReleased() throws RemoteException { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class); |
| TestableLooper.get(this).processAllMessages(); |
| verify(mIPowerManagerMock).acquireWakeLock(wakeLockCaptor.capture(), |
| anyInt(), |
| nullable(String.class), nullable(String.class), nullable(WorkSource.class), |
| nullable(String.class), eq(DISPLAY_ID_1), eq(null)); |
| IBinder wakeLock = wakeLockCaptor.getValue(); |
| |
| // Close the VirtualDevice without first notifying it of the VirtualDisplay removal. |
| mDeviceImpl.close(); |
| verify(mIPowerManagerMock).releaseWakeLock(eq(wakeLock), anyInt()); |
| } |
| |
| @Test |
| public void createVirtualDpad_noDisplay_failsSecurityException() { |
| assertThrows(SecurityException.class, |
| () -> mDeviceImpl.createVirtualDpad(DPAD_CONFIG, BINDER)); |
| } |
| |
| @Test |
| public void createVirtualKeyboard_noDisplay_failsSecurityException() { |
| assertThrows(SecurityException.class, |
| () -> mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER)); |
| } |
| |
| @Test |
| public void createVirtualMouse_noDisplay_failsSecurityException() { |
| assertThrows(SecurityException.class, |
| () -> mDeviceImpl.createVirtualMouse(MOUSE_CONFIG, BINDER)); |
| } |
| |
| @Test |
| public void createVirtualTouchscreen_noDisplay_failsSecurityException() { |
| assertThrows(SecurityException.class, |
| () -> mDeviceImpl.createVirtualTouchscreen(TOUCHSCREEN_CONFIG, BINDER)); |
| } |
| |
| @Test |
| public void createVirtualTouchscreen_zeroDisplayDimension_failsIllegalArgumentException() { |
| assertThrows(IllegalArgumentException.class, |
| () -> new VirtualTouchscreenConfig.Builder( |
| /* touchscrenWidth= */ 0, /* touchscreenHeight= */ 0)); |
| } |
| |
| @Test |
| public void createVirtualTouchscreen_negativeDisplayDimension_failsIllegalArgumentException() { |
| assertThrows(IllegalArgumentException.class, |
| () -> new VirtualTouchscreenConfig.Builder( |
| /* touchscrenWidth= */ -100, /* touchscreenHeight= */ -100)); |
| } |
| |
| @Test |
| public void createVirtualTouchscreen_positiveDisplayDimension_successful() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| VirtualTouchscreenConfig positiveConfig = |
| new VirtualTouchscreenConfig.Builder( |
| /* touchscrenWidth= */ 600, /* touchscreenHeight= */ 800) |
| .setVendorId(VENDOR_ID) |
| .setProductId(PRODUCT_ID) |
| .setInputDeviceName(DEVICE_NAME_1) |
| .setAssociatedDisplayId(DISPLAY_ID_1) |
| .build(); |
| mDeviceImpl.createVirtualTouchscreen(positiveConfig, BINDER); |
| assertWithMessage( |
| "Virtual touchscreen should create input device descriptor on successful creation" |
| + ".").that(mInputController.getInputDeviceDescriptors()).isNotEmpty(); |
| } |
| |
| @Test |
| public void createVirtualNavigationTouchpad_noDisplay_failsSecurityException() { |
| assertThrows(SecurityException.class, |
| () -> mDeviceImpl.createVirtualNavigationTouchpad(NAVIGATION_TOUCHPAD_CONFIG, |
| BINDER)); |
| } |
| |
| @Test |
| public void createVirtualNavigationTouchpad_zeroDisplayDimension_failsWithException() { |
| assertThrows(IllegalArgumentException.class, |
| () -> new VirtualNavigationTouchpadConfig.Builder( |
| /* touchpadHeight= */ 0, /* touchpadWidth= */ 0)); |
| } |
| |
| @Test |
| public void createVirtualNavigationTouchpad_negativeDisplayDimension_failsWithException() { |
| assertThrows(IllegalArgumentException.class, |
| () -> new VirtualNavigationTouchpadConfig.Builder( |
| /* touchpadHeight= */ -50, /* touchpadWidth= */ 50)); |
| } |
| |
| @Test |
| public void createVirtualNavigationTouchpad_positiveDisplayDimension_successful() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| VirtualNavigationTouchpadConfig positiveConfig = |
| new VirtualNavigationTouchpadConfig.Builder( |
| /* touchpadHeight= */ 50, /* touchpadWidth= */ 50) |
| .setVendorId(VENDOR_ID) |
| .setProductId(PRODUCT_ID) |
| .setInputDeviceName(DEVICE_NAME_1) |
| .setAssociatedDisplayId(DISPLAY_ID_1) |
| .build(); |
| mDeviceImpl.createVirtualNavigationTouchpad(positiveConfig, BINDER); |
| assertWithMessage( |
| "Virtual navigation touchpad should create input device descriptor on successful " |
| + "creation" |
| + ".").that(mInputController.getInputDeviceDescriptors()).isNotEmpty(); |
| } |
| |
| @Test |
| public void onAudioSessionStarting_noDisplay_failsSecurityException() { |
| assertThrows(SecurityException.class, |
| () -> mDeviceImpl.onAudioSessionStarting( |
| DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback)); |
| } |
| |
| @Test |
| public void createVirtualDpad_noPermission_failsSecurityException() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) { |
| assertThrows(SecurityException.class, |
| () -> mDeviceImpl.createVirtualDpad(DPAD_CONFIG, BINDER)); |
| } |
| } |
| |
| @Test |
| public void createVirtualKeyboard_noPermission_failsSecurityException() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) { |
| assertThrows(SecurityException.class, |
| () -> mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER)); |
| } |
| } |
| |
| @Test |
| public void createVirtualMouse_noPermission_failsSecurityException() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) { |
| assertThrows(SecurityException.class, |
| () -> mDeviceImpl.createVirtualMouse(MOUSE_CONFIG, BINDER)); |
| } |
| } |
| |
| @Test |
| public void createVirtualTouchscreen_noPermission_failsSecurityException() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) { |
| assertThrows(SecurityException.class, |
| () -> mDeviceImpl.createVirtualTouchscreen(TOUCHSCREEN_CONFIG, BINDER)); |
| } |
| } |
| |
| @Test |
| public void createVirtualNavigationTouchpad_noPermission_failsSecurityException() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) { |
| assertThrows(SecurityException.class, |
| () -> mDeviceImpl.createVirtualNavigationTouchpad(NAVIGATION_TOUCHPAD_CONFIG, |
| BINDER)); |
| } |
| } |
| |
| @Test |
| public void onAudioSessionStarting_noPermission_failsSecurityException() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) { |
| assertThrows(SecurityException.class, |
| () -> mDeviceImpl.onAudioSessionStarting( |
| DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback)); |
| } |
| } |
| |
| @Test |
| public void onAudioSessionEnded_noPermission_failsSecurityException() { |
| try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) { |
| assertThrows(SecurityException.class, () -> mDeviceImpl.onAudioSessionEnded()); |
| } |
| } |
| |
| @Test |
| public void createVirtualDpad_hasDisplay_obtainFileDescriptor() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| mDeviceImpl.createVirtualDpad(DPAD_CONFIG, BINDER); |
| assertWithMessage("Virtual dpad should register fd when the display matches").that( |
| mInputController.getInputDeviceDescriptors()).isNotEmpty(); |
| verify(mNativeWrapperMock).openUinputDpad(eq(DEVICE_NAME_1), eq(VENDOR_ID), eq(PRODUCT_ID), |
| anyString()); |
| } |
| |
| @Test |
| public void createVirtualKeyboard_hasDisplay_obtainFileDescriptor() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER); |
| assertWithMessage("Virtual keyboard should register fd when the display matches").that( |
| mInputController.getInputDeviceDescriptors()).isNotEmpty(); |
| verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME_1), eq(VENDOR_ID), |
| eq(PRODUCT_ID), anyString()); |
| } |
| |
| @Test |
| public void createVirtualKeyboard_keyboardCreated_localeUpdated() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER); |
| assertWithMessage("Virtual keyboard should register fd when the display matches") |
| .that(mInputController.getInputDeviceDescriptors()) |
| .isNotEmpty(); |
| verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME_1), eq(VENDOR_ID), |
| eq(PRODUCT_ID), anyString()); |
| assertThat(mDeviceImpl.getDeviceLocaleList()).isEqualTo( |
| LocaleList.forLanguageTags(KEYBOARD_CONFIG.getLanguageTag())); |
| } |
| |
| @Test |
| public void createVirtualKeyboard_keyboardWithoutExplicitLayoutInfo_localeUpdatedWithDefault() { |
| VirtualKeyboardConfig configWithoutExplicitLayoutInfo = |
| new VirtualKeyboardConfig.Builder() |
| .setVendorId(VENDOR_ID) |
| .setProductId(PRODUCT_ID) |
| .setInputDeviceName(DEVICE_NAME_1) |
| .setAssociatedDisplayId(DISPLAY_ID_1) |
| .build(); |
| |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| mDeviceImpl.createVirtualKeyboard(configWithoutExplicitLayoutInfo, BINDER); |
| assertWithMessage("Virtual keyboard should register fd when the display matches") |
| .that(mInputController.getInputDeviceDescriptors()) |
| .isNotEmpty(); |
| verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME_1), eq(VENDOR_ID), |
| eq(PRODUCT_ID), anyString()); |
| assertThat(mDeviceImpl.getDeviceLocaleList()).isEqualTo( |
| LocaleList.forLanguageTags(VirtualKeyboardConfig.DEFAULT_LANGUAGE_TAG)); |
| } |
| |
| @Test |
| public void virtualDeviceWithoutKeyboard_noLocaleUpdate() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| |
| // no preceding call to createVirtualKeyboard() |
| assertThat(mDeviceImpl.getDeviceLocaleList()).isNull(); |
| } |
| |
| @Test |
| public void createVirtualMouse_hasDisplay_obtainFileDescriptor() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| mDeviceImpl.createVirtualMouse(MOUSE_CONFIG, BINDER); |
| assertWithMessage("Virtual mouse should register fd when the display matches").that( |
| mInputController.getInputDeviceDescriptors()).isNotEmpty(); |
| verify(mNativeWrapperMock).openUinputMouse(eq(DEVICE_NAME_1), eq(VENDOR_ID), eq(PRODUCT_ID), |
| anyString()); |
| } |
| |
| @Test |
| public void createVirtualTouchscreen_hasDisplay_obtainFileDescriptor() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| mDeviceImpl.createVirtualTouchscreen(TOUCHSCREEN_CONFIG, BINDER); |
| assertWithMessage("Virtual touchscreen should register fd when the display matches").that( |
| mInputController.getInputDeviceDescriptors()).isNotEmpty(); |
| verify(mNativeWrapperMock).openUinputTouchscreen(eq(DEVICE_NAME_1), eq(VENDOR_ID), |
| eq(PRODUCT_ID), anyString(), eq(HEIGHT), eq(WIDTH)); |
| } |
| |
| @Test |
| public void createVirtualNavigationTouchpad_hasDisplay_obtainFileDescriptor() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| mDeviceImpl.createVirtualNavigationTouchpad(NAVIGATION_TOUCHPAD_CONFIG, BINDER); |
| assertWithMessage("Virtual navigation touchpad should register fd when the display matches") |
| .that( |
| mInputController.getInputDeviceDescriptors()).isNotEmpty(); |
| verify(mNativeWrapperMock).openUinputTouchscreen(eq(DEVICE_NAME_1), eq(VENDOR_ID), |
| eq(PRODUCT_ID), anyString(), eq(HEIGHT), eq(WIDTH)); |
| } |
| |
| @Test |
| public void createVirtualKeyboard_inputDeviceId_obtainFromInputController() { |
| final int fd = 1; |
| mInputController.addDeviceForTesting(BINDER, fd, |
| InputController.InputDeviceDescriptor.TYPE_KEYBOARD, DISPLAY_ID_1, PHYS, |
| DEVICE_NAME_1, INPUT_DEVICE_ID); |
| assertWithMessage( |
| "InputController should return device id from InputDeviceDescriptor").that( |
| mInputController.getInputDeviceId(BINDER)).isEqualTo(INPUT_DEVICE_ID); |
| } |
| |
| @Test |
| public void onAudioSessionStarting_hasVirtualAudioController() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| |
| mDeviceImpl.onAudioSessionStarting(DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback); |
| |
| assertThat(mDeviceImpl.getVirtualAudioControllerForTesting()).isNotNull(); |
| } |
| |
| @Test |
| public void onAudioSessionEnded_noVirtualAudioController() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| mDeviceImpl.onAudioSessionStarting(DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback); |
| |
| mDeviceImpl.onAudioSessionEnded(); |
| |
| assertThat(mDeviceImpl.getVirtualAudioControllerForTesting()).isNull(); |
| } |
| |
| @Test |
| public void close_cleanVirtualAudioController() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| mDeviceImpl.onAudioSessionStarting(DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback); |
| |
| mDeviceImpl.close(); |
| |
| assertThat(mDeviceImpl.getVirtualAudioControllerForTesting()).isNull(); |
| } |
| |
| @Test |
| public void close_cleanSensorController() { |
| mSensorController.addSensorForTesting( |
| BINDER, SENSOR_HANDLE, Sensor.TYPE_ACCELEROMETER, DEVICE_NAME_1); |
| |
| mDeviceImpl.close(); |
| |
| assertThat(mSensorController.getSensorDescriptors()).isEmpty(); |
| verify(mSensorManagerInternalMock).removeRuntimeSensor(SENSOR_HANDLE); |
| } |
| |
| @Test |
| public void closedDevice_lateCallToRunningAppsChanged_isIgnored() { |
| mLocalService.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener); |
| int deviceId = mDeviceImpl.getDeviceId(); |
| mDeviceImpl.close(); |
| mVdms.notifyRunningAppsChanged(deviceId, Sets.newArraySet(UID_1)); |
| TestableLooper.get(this).processAllMessages(); |
| verify(mAppsOnVirtualDeviceListener, never()).onAppsOnAnyVirtualDeviceChanged(any()); |
| } |
| |
| @Test |
| public void sendKeyEvent_noFd() { |
| assertThrows( |
| IllegalArgumentException.class, |
| () -> |
| mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder() |
| .setKeyCode(KeyEvent.KEYCODE_A) |
| .setAction(VirtualKeyEvent.ACTION_DOWN).build())); |
| } |
| |
| @Test |
| public void sendKeyEvent_hasFd_writesEvent() { |
| final int fd = 1; |
| final int keyCode = KeyEvent.KEYCODE_A; |
| final int action = VirtualKeyEvent.ACTION_UP; |
| final long eventTimeNanos = 5000L; |
| mInputController.addDeviceForTesting(BINDER, fd, |
| InputController.InputDeviceDescriptor.TYPE_KEYBOARD, DISPLAY_ID_1, PHYS, |
| DEVICE_NAME_1, INPUT_DEVICE_ID); |
| |
| mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder() |
| .setKeyCode(keyCode) |
| .setAction(action) |
| .setEventTimeNanos(eventTimeNanos) |
| .build()); |
| verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action, eventTimeNanos); |
| } |
| |
| @Test |
| public void sendButtonEvent_noFd() { |
| assertThrows( |
| IllegalArgumentException.class, |
| () -> |
| mDeviceImpl.sendButtonEvent(BINDER, |
| new VirtualMouseButtonEvent.Builder() |
| .setButtonCode(VirtualMouseButtonEvent.BUTTON_BACK) |
| .setAction(VirtualMouseButtonEvent.ACTION_BUTTON_PRESS) |
| .build())); |
| } |
| |
| @Test |
| public void sendButtonEvent_hasFd_writesEvent() { |
| final int fd = 1; |
| final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK; |
| final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS; |
| final long eventTimeNanos = 5000L; |
| mInputController.addDeviceForTesting(BINDER, fd, |
| InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, |
| DEVICE_NAME_1, INPUT_DEVICE_ID); |
| doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); |
| mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder() |
| .setButtonCode(buttonCode) |
| .setAction(action) |
| .setEventTimeNanos(eventTimeNanos) |
| .build()); |
| verify(mNativeWrapperMock).writeButtonEvent(fd, buttonCode, action, eventTimeNanos); |
| } |
| |
| @Test |
| public void sendButtonEvent_hasFd_wrongDisplay_throwsIllegalStateException() { |
| final int fd = 1; |
| final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK; |
| final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS; |
| mInputController.addDeviceForTesting(BINDER, fd, |
| InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1, |
| INPUT_DEVICE_ID); |
| assertThrows( |
| IllegalStateException.class, |
| () -> |
| mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder() |
| .setButtonCode(buttonCode) |
| .setAction(action).build())); |
| } |
| |
| @Test |
| public void sendRelativeEvent_noFd() { |
| assertThrows( |
| IllegalArgumentException.class, |
| () -> |
| mDeviceImpl.sendRelativeEvent(BINDER, |
| new VirtualMouseRelativeEvent.Builder().setRelativeX( |
| 0.0f).setRelativeY(0.0f).build())); |
| } |
| |
| @Test |
| public void sendRelativeEvent_hasFd_writesEvent() { |
| final int fd = 1; |
| final float x = -0.2f; |
| final float y = 0.7f; |
| final long eventTimeNanos = 5000L; |
| mInputController.addDeviceForTesting(BINDER, fd, |
| InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1, |
| INPUT_DEVICE_ID); |
| doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); |
| mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder() |
| .setRelativeX(x) |
| .setRelativeY(y) |
| .setEventTimeNanos(eventTimeNanos) |
| .build()); |
| verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y, eventTimeNanos); |
| } |
| |
| @Test |
| public void sendRelativeEvent_hasFd_wrongDisplay_throwsIllegalStateException() { |
| final int fd = 1; |
| final float x = -0.2f; |
| final float y = 0.7f; |
| mInputController.addDeviceForTesting(BINDER, fd, |
| InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1, |
| INPUT_DEVICE_ID); |
| assertThrows( |
| IllegalStateException.class, |
| () -> |
| mDeviceImpl.sendRelativeEvent(BINDER, |
| new VirtualMouseRelativeEvent.Builder() |
| .setRelativeX(x).setRelativeY(y).build())); |
| } |
| |
| @Test |
| public void sendScrollEvent_noFd() { |
| assertThrows( |
| IllegalArgumentException.class, |
| () -> |
| mDeviceImpl.sendScrollEvent(BINDER, |
| new VirtualMouseScrollEvent.Builder() |
| .setXAxisMovement(-1f) |
| .setYAxisMovement(1f).build())); |
| } |
| |
| @Test |
| public void sendScrollEvent_hasFd_writesEvent() { |
| final int fd = 1; |
| final float x = 0.5f; |
| final float y = 1f; |
| final long eventTimeNanos = 5000L; |
| mInputController.addDeviceForTesting(BINDER, fd, |
| InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1, |
| INPUT_DEVICE_ID); |
| doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); |
| mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder() |
| .setXAxisMovement(x) |
| .setYAxisMovement(y) |
| .setEventTimeNanos(eventTimeNanos) |
| .build()); |
| verify(mNativeWrapperMock).writeScrollEvent(fd, x, y, eventTimeNanos); |
| } |
| |
| @Test |
| public void sendScrollEvent_hasFd_wrongDisplay_throwsIllegalStateException() { |
| final int fd = 1; |
| final float x = 0.5f; |
| final float y = 1f; |
| mInputController.addDeviceForTesting(BINDER, fd, |
| InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1, |
| INPUT_DEVICE_ID); |
| assertThrows( |
| IllegalStateException.class, |
| () -> |
| mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder() |
| .setXAxisMovement(x) |
| .setYAxisMovement(y).build())); |
| } |
| |
| @Test |
| public void sendTouchEvent_noFd() { |
| assertThrows( |
| IllegalArgumentException.class, |
| () -> |
| mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder() |
| .setX(0.0f) |
| .setY(0.0f) |
| .setAction(VirtualTouchEvent.ACTION_UP) |
| .setPointerId(1) |
| .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER) |
| .build())); |
| } |
| |
| @Test |
| public void sendTouchEvent_hasFd_writesEvent_withoutPressureOrMajorAxisSize() { |
| final int fd = 1; |
| final int pointerId = 5; |
| final int toolType = VirtualTouchEvent.TOOL_TYPE_FINGER; |
| final float x = 100.5f; |
| final float y = 200.5f; |
| final int action = VirtualTouchEvent.ACTION_UP; |
| final long eventTimeNanos = 5000L; |
| mInputController.addDeviceForTesting(BINDER, fd, |
| InputController.InputDeviceDescriptor.TYPE_TOUCHSCREEN, DISPLAY_ID_1, PHYS, |
| DEVICE_NAME_1, INPUT_DEVICE_ID); |
| mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder() |
| .setX(x) |
| .setY(y) |
| .setAction(action) |
| .setPointerId(pointerId) |
| .setToolType(toolType) |
| .setEventTimeNanos(eventTimeNanos) |
| .build()); |
| verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN, |
| Float.NaN, eventTimeNanos); |
| } |
| |
| @Test |
| public void sendTouchEvent_hasFd_writesEvent() { |
| final int fd = 1; |
| final int pointerId = 5; |
| final int toolType = VirtualTouchEvent.TOOL_TYPE_FINGER; |
| final float x = 100.5f; |
| final float y = 200.5f; |
| final int action = VirtualTouchEvent.ACTION_UP; |
| final float pressure = 1.0f; |
| final float majorAxisSize = 10.0f; |
| final long eventTimeNanos = 5000L; |
| mInputController.addDeviceForTesting(BINDER, fd, |
| InputController.InputDeviceDescriptor.TYPE_TOUCHSCREEN, DISPLAY_ID_1, PHYS, |
| DEVICE_NAME_1, INPUT_DEVICE_ID); |
| mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder() |
| .setX(x) |
| .setY(y) |
| .setAction(action) |
| .setPointerId(pointerId) |
| .setToolType(toolType) |
| .setPressure(pressure) |
| .setMajorAxisSize(majorAxisSize) |
| .setEventTimeNanos(eventTimeNanos) |
| .build()); |
| verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, pressure, |
| majorAxisSize, eventTimeNanos); |
| } |
| |
| @Test |
| public void setShowPointerIcon_setsValueForAllDisplays() { |
| addVirtualDisplay(mDeviceImpl, 1); |
| addVirtualDisplay(mDeviceImpl, 2); |
| addVirtualDisplay(mDeviceImpl, 3); |
| VirtualMouseConfig config1 = new VirtualMouseConfig.Builder() |
| .setAssociatedDisplayId(1) |
| .setInputDeviceName(DEVICE_NAME_1) |
| .setVendorId(VENDOR_ID) |
| .setProductId(PRODUCT_ID) |
| .build(); |
| VirtualMouseConfig config2 = new VirtualMouseConfig.Builder() |
| .setAssociatedDisplayId(2) |
| .setInputDeviceName(DEVICE_NAME_2) |
| .setVendorId(VENDOR_ID) |
| .setProductId(PRODUCT_ID) |
| .build(); |
| VirtualMouseConfig config3 = new VirtualMouseConfig.Builder() |
| .setAssociatedDisplayId(3) |
| .setInputDeviceName(DEVICE_NAME_3) |
| .setVendorId(VENDOR_ID) |
| .setProductId(PRODUCT_ID) |
| .build(); |
| |
| mDeviceImpl.createVirtualMouse(config1, BINDER); |
| mDeviceImpl.createVirtualMouse(config2, BINDER); |
| mDeviceImpl.createVirtualMouse(config3, BINDER); |
| clearInvocations(mInputManagerInternalMock); |
| mDeviceImpl.setShowPointerIcon(false); |
| |
| verify(mInputManagerInternalMock, times(3)).setPointerIconVisible(eq(false), anyInt()); |
| verify(mInputManagerInternalMock, never()).setPointerIconVisible(eq(true), anyInt()); |
| mDeviceImpl.setShowPointerIcon(true); |
| verify(mInputManagerInternalMock, times(3)).setPointerIconVisible(eq(true), anyInt()); |
| } |
| |
| @Test |
| public void openNonBlockedAppOnVirtualDisplay_doesNotStartBlockedAlertActivity() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| doNothing().when(mContext).startActivityAsUser(any(), any(), any()); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( |
| activityInfo, mAssociationInfo.getDisplayName()); |
| gwpc.canActivityBeLaunched(activityInfo, blockedAppIntent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false); |
| |
| verify(mContext, never()).startActivityAsUser(argThat(intent -> |
| intent.filterEquals(blockedAppIntent)), any(), any()); |
| } |
| |
| @Test |
| public void openPermissionControllerOnVirtualDisplay_startBlockedAlertActivity() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| doNothing().when(mContext).startActivityAsUser(any(), any(), any()); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| PERMISSION_CONTROLLER_PACKAGE_NAME, |
| PERMISSION_CONTROLLER_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ false, |
| /* targetDisplayCategory */ null); |
| Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( |
| activityInfo, mAssociationInfo.getDisplayName()); |
| gwpc.canActivityBeLaunched(activityInfo, blockedAppIntent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false); |
| |
| verify(mContext).startActivityAsUser(argThat(intent -> |
| intent.filterEquals(blockedAppIntent)), any(), any()); |
| } |
| |
| @Test |
| public void openPermissionControllerOnVirtualDisplay_displayOnRemoteDevices_startsWhenFlagIsEnabled() { |
| mSetFlagsRule.enableFlags(Flags.FLAG_STREAM_PERMISSIONS); |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| doNothing().when(mContext).startActivityAsUser(any(), any(), any()); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| PERMISSION_CONTROLLER_PACKAGE_NAME, |
| PERMISSION_CONTROLLER_PACKAGE_NAME, |
| /* displayOnRemoveDevices */ true, |
| /* targetDisplayCategory */ null); |
| Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( |
| activityInfo, mAssociationInfo.getDisplayName()); |
| gwpc.canActivityBeLaunched(activityInfo, blockedAppIntent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false); |
| |
| verify(mContext, never()).startActivityAsUser(argThat(intent -> |
| intent.filterEquals(blockedAppIntent)), any(), any()); |
| } |
| |
| @Test |
| public void openPermissionControllerOnVirtualDisplay_dontDisplayOnRemoteDevices_startsWhenFlagIsEnabled() { |
| mSetFlagsRule.enableFlags(Flags.FLAG_STREAM_PERMISSIONS); |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| doNothing().when(mContext).startActivityAsUser(any(), any(), any()); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| PERMISSION_CONTROLLER_PACKAGE_NAME, |
| PERMISSION_CONTROLLER_PACKAGE_NAME, |
| /* displayOnRemoveDevices */ false, |
| /* targetDisplayCategory */ null); |
| Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( |
| activityInfo, mAssociationInfo.getDisplayName()); |
| gwpc.canActivityBeLaunched(activityInfo, blockedAppIntent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false); |
| |
| verify(mContext).startActivityAsUser(argThat(intent -> |
| intent.filterEquals(blockedAppIntent)), any(), any()); |
| } |
| |
| @Test |
| public void openSettingsOnVirtualDisplay_startBlockedAlertActivity() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| doNothing().when(mContext).startActivityAsUser(any(), any(), any()); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| SETTINGS_PACKAGE_NAME, |
| SETTINGS_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( |
| activityInfo, mAssociationInfo.getDisplayName()); |
| gwpc.canActivityBeLaunched(activityInfo, blockedAppIntent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false); |
| |
| verify(mContext).startActivityAsUser(argThat(intent -> |
| intent.filterEquals(blockedAppIntent)), any(), any()); |
| } |
| |
| @Test |
| public void openVendingOnVirtualDisplay_startBlockedAlertActivity() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| doNothing().when(mContext).startActivityAsUser(any(), any(), any()); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| VENDING_PACKAGE_NAME, |
| VENDING_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( |
| activityInfo, mAssociationInfo.getDisplayName()); |
| gwpc.canActivityBeLaunched(activityInfo, blockedAppIntent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false); |
| |
| verify(mContext).startActivityAsUser(argThat(intent -> |
| intent.filterEquals(blockedAppIntent)), any(), any()); |
| } |
| |
| @Test |
| public void openGoogleDialerOnVirtualDisplay_startBlockedAlertActivity() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| doNothing().when(mContext).startActivityAsUser(any(), any(), any()); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| GOOGLE_DIALER_PACKAGE_NAME, |
| GOOGLE_DIALER_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( |
| activityInfo, mAssociationInfo.getDisplayName()); |
| gwpc.canActivityBeLaunched(activityInfo, blockedAppIntent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false); |
| |
| verify(mContext).startActivityAsUser(argThat(intent -> |
| intent.filterEquals(blockedAppIntent)), any(), any()); |
| } |
| |
| @Test |
| public void openGoogleMapsOnVirtualDisplay_startBlockedAlertActivity() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| doNothing().when(mContext).startActivityAsUser(any(), any(), any()); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| GOOGLE_MAPS_PACKAGE_NAME, |
| GOOGLE_MAPS_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( |
| activityInfo, mAssociationInfo.getDisplayName()); |
| gwpc.canActivityBeLaunched(activityInfo, blockedAppIntent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false); |
| |
| verify(mContext).startActivityAsUser(argThat(intent -> |
| intent.filterEquals(blockedAppIntent)), any(), any()); |
| } |
| |
| @Test |
| public void openNonBlockedAppOnMirrorDisplay_flagEnabled_cannotBeLaunched() { |
| mSetFlagsRule.enableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR); |
| when(mDisplayManagerInternalMock.getDisplayIdToMirror(anyInt())) |
| .thenReturn(Display.DEFAULT_DISPLAY); |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| doNothing().when(mContext).startActivityAsUser(any(), any(), any()); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertThat(gwpc.canActivityBeLaunched(activityInfo, null, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/ false)) |
| .isFalse(); |
| // Verify that BlockedAppStreamingActivity also doesn't launch for mirror displays. |
| Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( |
| activityInfo, mAssociationInfo.getDisplayName()); |
| verify(mContext, never()).startActivityAsUser(argThat(intent -> |
| intent.filterEquals(blockedAppIntent)), any(), any()); |
| } |
| |
| @Test |
| public void openNonBlockedAppOnMirrorDisplay_flagDisabled_launchesActivity() { |
| when(mDisplayManagerInternalMock.getDisplayIdToMirror(anyInt())) |
| .thenReturn(Display.DEFAULT_DISPLAY); |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| doNothing().when(mContext).startActivityAsUser(any(), any(), any()); |
| |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertThat(gwpc.canActivityBeLaunched(activityInfo, null, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/ false)) |
| .isTrue(); |
| Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( |
| activityInfo, mAssociationInfo.getDisplayName()); |
| verify(mContext, never()).startActivityAsUser(argThat(intent -> |
| intent.filterEquals(blockedAppIntent)), any(), any()); |
| } |
| |
| @Test |
| public void registerRunningAppsChangedListener_onRunningAppsChanged_listenersNotified() { |
| ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2)); |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| |
| gwpc.onRunningAppsChanged(uids); |
| mDeviceImpl.onRunningAppsChanged(uids); |
| |
| assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(1); |
| verify(mRunningAppsChangedCallback).accept(new ArraySet<>(Arrays.asList(UID_1, UID_2))); |
| } |
| |
| @Test |
| public void noRunningAppsChangedListener_onRunningAppsChanged_doesNotThrowException() { |
| ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2)); |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| gwpc.unregisterRunningAppsChangedListener(mDeviceImpl); |
| |
| // This call should not throw any exceptions. |
| gwpc.onRunningAppsChanged(uids); |
| |
| assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_permissionDialog_flagDisabled_isBlocked() { |
| mSetFlagsRule.disableFlags(Flags.FLAG_STREAM_PERMISSIONS); |
| VirtualDeviceParams params = new VirtualDeviceParams.Builder().build(); |
| mDeviceImpl.close(); |
| mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params); |
| doNothing().when(mContext).startActivityAsUser(any(), any(), any()); |
| |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| ComponentName permissionComponent = getPermissionDialogComponent(); |
| ActivityInfo activityInfo = getActivityInfo( |
| permissionComponent.getPackageName(), |
| permissionComponent.getClassName(), |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertThat(gwpc.canActivityBeLaunched(activityInfo, null, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false)) |
| .isFalse(); |
| |
| Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent( |
| activityInfo, mAssociationInfo.getDisplayName()); |
| verify(mContext).startActivityAsUser(argThat(intent -> |
| intent.filterEquals(blockedAppIntent)), any(), any()); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_permissionDialog_flagEnabled_isStreamed() { |
| mSetFlagsRule.enableFlags(Flags.FLAG_STREAM_PERMISSIONS); |
| VirtualDeviceParams params = new VirtualDeviceParams.Builder().build(); |
| mDeviceImpl.close(); |
| mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params); |
| |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| ComponentName permissionComponent = getPermissionDialogComponent(); |
| ActivityInfo activityInfo = getActivityInfo( |
| permissionComponent.getPackageName(), |
| permissionComponent.getClassName(), |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertThat(gwpc.canActivityBeLaunched(activityInfo, null, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false)) |
| .isTrue(); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_activityCanLaunch() { |
| Intent intent = new Intent(ACTION_VIEW, Uri.parse(TEST_SITE)); |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| assertThat(gwpc.canActivityBeLaunched(activityInfo, intent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false)) |
| .isTrue(); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_intentInterceptedWhenRegistered_activityNoLaunch() |
| throws RemoteException { |
| Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(TEST_SITE)); |
| |
| IVirtualDeviceIntentInterceptor.Stub interceptor = |
| mock(IVirtualDeviceIntentInterceptor.Stub.class); |
| doNothing().when(interceptor).onIntentIntercepted(any()); |
| doReturn(interceptor).when(interceptor).asBinder(); |
| doReturn(interceptor).when(interceptor).queryLocalInterface(anyString()); |
| |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| |
| IntentFilter intentFilter = new IntentFilter(Intent.ACTION_VIEW); |
| intentFilter.addDataScheme(IntentFilter.SCHEME_HTTP); |
| intentFilter.addDataScheme(IntentFilter.SCHEME_HTTPS); |
| |
| // register interceptor and intercept intent |
| mDeviceImpl.registerIntentInterceptor(interceptor, intentFilter); |
| assertThat(gwpc.canActivityBeLaunched(activityInfo, intent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false)) |
| .isFalse(); |
| ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); |
| verify(interceptor).onIntentIntercepted(intentCaptor.capture()); |
| Intent cIntent = intentCaptor.getValue(); |
| assertThat(cIntent).isNotNull(); |
| assertThat(cIntent.getAction()).isEqualTo(Intent.ACTION_VIEW); |
| assertThat(cIntent.getData().toString()).isEqualTo(TEST_SITE); |
| |
| // unregister interceptor and launch activity |
| mDeviceImpl.unregisterIntentInterceptor(interceptor); |
| assertThat(gwpc.canActivityBeLaunched(activityInfo, intent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false)) |
| .isTrue(); |
| } |
| |
| @Test |
| public void canActivityBeLaunched_noMatchIntentFilter_activityLaunches() |
| throws RemoteException { |
| Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("testing")); |
| |
| IVirtualDeviceIntentInterceptor.Stub interceptor = |
| mock(IVirtualDeviceIntentInterceptor.Stub.class); |
| doNothing().when(interceptor).onIntentIntercepted(any()); |
| doReturn(interceptor).when(interceptor).asBinder(); |
| doReturn(interceptor).when(interceptor).queryLocalInterface(anyString()); |
| |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest( |
| DISPLAY_ID_1); |
| ActivityInfo activityInfo = getActivityInfo( |
| NONBLOCKED_APP_PACKAGE_NAME, |
| NONBLOCKED_APP_PACKAGE_NAME, |
| /* displayOnRemoteDevices */ true, |
| /* targetDisplayCategory */ null); |
| |
| IntentFilter intentFilter = new IntentFilter(Intent.ACTION_VIEW); |
| intentFilter.addDataScheme("mailto"); |
| |
| // register interceptor with different filter |
| mDeviceImpl.registerIntentInterceptor(interceptor, intentFilter); |
| |
| assertThat(gwpc.canActivityBeLaunched(activityInfo, intent, |
| WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false)) |
| .isTrue(); |
| } |
| |
| @Test |
| public void nonRestrictedActivityOnRestrictedVirtualDisplay_startBlockedAlertActivity() { |
| Intent blockedAppIntent = createRestrictedActivityBlockedIntent(Set.of("abc"), |
| /* targetDisplayCategory= */ null); |
| verify(mContext).startActivityAsUser(argThat(intent -> |
| intent.filterEquals(blockedAppIntent)), any(), any()); |
| } |
| |
| @Test |
| public void restrictedActivityOnRestrictedVirtualDisplay_doesNotStartBlockedAlertActivity() { |
| Intent blockedAppIntent = createRestrictedActivityBlockedIntent(Set.of("abc"), "abc"); |
| verify(mContext, never()).startActivityAsUser(argThat(intent -> |
| intent.filterEquals(blockedAppIntent)), any(), any()); |
| } |
| |
| @Test |
| public void restrictedActivityOnNonRestrictedVirtualDisplay_startBlockedAlertActivity() { |
| Intent blockedAppIntent = createRestrictedActivityBlockedIntent( |
| /* displayCategories= */ Set.of(), "abc"); |
| verify(mContext).startActivityAsUser(argThat(intent -> |
| intent.filterEquals(blockedAppIntent)), any(), any()); |
| } |
| |
| @Test |
| public void restrictedActivityNonMatchingRestrictedVirtualDisplay_startBlockedAlertActivity() { |
| Intent blockedAppIntent = createRestrictedActivityBlockedIntent(Set.of("abc"), "def"); |
| verify(mContext).startActivityAsUser(argThat(intent -> |
| intent.filterEquals(blockedAppIntent)), any(), any()); |
| } |
| |
| @Test |
| public void playSoundEffect_callsSoundEffectListener() throws Exception { |
| mVdm.playSoundEffect(mDeviceImpl.getDeviceId(), AudioManager.FX_KEY_CLICK); |
| |
| verify(mSoundEffectListener).onPlaySoundEffect(AudioManager.FX_KEY_CLICK); |
| } |
| |
| @Test |
| public void getDisplayIdsForDevice_invalidDeviceId_emptyResult() { |
| ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_2); |
| assertThat(displayIds).isEmpty(); |
| } |
| |
| @Test |
| public void getDisplayIdsForDevice_noDisplays_emptyResult() { |
| ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_1); |
| assertThat(displayIds).isEmpty(); |
| } |
| |
| @Test |
| public void getDisplayIdsForDevice_oneDisplay_resultContainsCorrectDisplayId() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_1); |
| assertThat(displayIds).containsExactly(DISPLAY_ID_1); |
| } |
| |
| @Test |
| public void getDisplayIdsForDevice_twoDisplays_resultContainsCorrectDisplayIds() { |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1); |
| addVirtualDisplay(mDeviceImpl, DISPLAY_ID_2); |
| ArraySet<Integer> displayIds = mLocalService.getDisplayIdsForDevice(VIRTUAL_DEVICE_ID_1); |
| assertThat(displayIds).containsExactly(DISPLAY_ID_1, DISPLAY_ID_2); |
| } |
| |
| @Test |
| public void getPersistentIdForDevice_invalidDeviceId_returnsNull() { |
| assertThat(mLocalService.getPersistentIdForDevice(DEVICE_ID_INVALID)).isNull(); |
| assertThat(mLocalService.getPersistentIdForDevice(DEVICE_ID_DEFAULT)).isNull(); |
| assertThat(mLocalService.getPersistentIdForDevice(VIRTUAL_DEVICE_ID_2)).isNull(); |
| } |
| |
| @Test |
| public void getPersistentIdForDevice_returnsCorrectId() { |
| assertThat(mLocalService.getPersistentIdForDevice(VIRTUAL_DEVICE_ID_1)) |
| .isEqualTo(mDeviceImpl.getPersistentDeviceId()); |
| } |
| |
| private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid) { |
| VirtualDeviceParams params = new VirtualDeviceParams.Builder() |
| .setBlockedActivities(getBlockedActivities()) |
| .build(); |
| return createVirtualDevice(virtualDeviceId, ownerUid, params); |
| } |
| |
| private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid, |
| VirtualDeviceParams params) { |
| VirtualDeviceImpl virtualDeviceImpl = |
| new VirtualDeviceImpl( |
| mContext, |
| mAssociationInfo, |
| mVdms, |
| mVirtualDeviceLog, |
| new Binder(), |
| new AttributionSource( |
| ownerUid, "com.android.virtualdevice.test", "virtualdevice"), |
| virtualDeviceId, |
| mInputController, |
| mCameraAccessController, |
| mPendingTrampolineCallback, |
| mActivityListener, |
| mSoundEffectListener, |
| mRunningAppsChangedCallback, |
| params, |
| new DisplayManagerGlobal(mIDisplayManager), |
| new VirtualCameraController(mContext)); |
| mVdms.addVirtualDevice(virtualDeviceImpl); |
| assertThat(virtualDeviceImpl.getAssociationId()).isEqualTo(mAssociationInfo.getId()); |
| assertThat(virtualDeviceImpl.getPersistentDeviceId()) |
| .isEqualTo("companion:" + mAssociationInfo.getId()); |
| return virtualDeviceImpl; |
| } |
| |
| private void addVirtualDisplay(VirtualDeviceImpl virtualDevice, int displayId) { |
| when(mDisplayManagerInternalMock.createVirtualDisplay(any(), eq(mVirtualDisplayCallback), |
| eq(virtualDevice), any(), any())).thenReturn(displayId); |
| virtualDevice.createVirtualDisplay(VIRTUAL_DISPLAY_CONFIG, mVirtualDisplayCallback, |
| NONBLOCKED_APP_PACKAGE_NAME); |
| } |
| |
| private ComponentName getPermissionDialogComponent() { |
| Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS); |
| PackageManager packageManager = mContext.getPackageManager(); |
| intent.setPackage(packageManager.getPermissionControllerPackageName()); |
| return intent.resolveActivity(packageManager); |
| } |
| |
| /** Helper class to drop permissions temporarily and restore them at the end of a test. */ |
| static final class DropShellPermissionsTemporarily implements AutoCloseable { |
| DropShellPermissionsTemporarily() { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .dropShellPermissionIdentity(); |
| } |
| |
| @Override |
| public void close() { |
| InstrumentationRegistry.getInstrumentation().getUiAutomation() |
| .adoptShellPermissionIdentity(); |
| } |
| } |
| } |