• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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