Loading core/java/android/companion/virtual/flags/flags.aconfig +20 −0 Original line number Diff line number Diff line Loading @@ -256,3 +256,23 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "computer_control_activity_policy_strict" namespace: "virtual_devices" description: "Implements a strict Activity policy for ComputerControl VirtualDevices" bug: "437849470" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "computer_control_activity_policy_relaxed" namespace: "virtual_devices" description: "Implements a relaxed Activity policy for ComputerControl VirtualDevices" bug: "437849470" metadata { purpose: PURPOSE_BUGFIX } } services/companion/java/com/android/server/companion/virtual/computercontrol/ComputerControlSessionImpl.java +47 −6 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.companion.virtual.computercontrol; 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_ACTIVITY; import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS; import android.annotation.IntRange; Loading @@ -32,11 +33,13 @@ import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.computercontrol.ComputerControlSessionParams; import android.companion.virtual.computercontrol.IComputerControlSession; import android.companion.virtual.computercontrol.IInteractiveMirrorDisplay; import android.companion.virtualdevice.flags.Flags; import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; Loading @@ -62,6 +65,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.wm.WindowManagerInternal; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; Loading Loading @@ -115,11 +120,6 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub .setName(mParams.getName()) .setDevicePolicy(POLICY_TYPE_RECENTS, DEVICE_POLICY_CUSTOM) .build(); final String permissionControllerPackage = mInjector.getPermissionControllerPackageName(); final ActivityPolicyExemption permissionController = new ActivityPolicyExemption.Builder() .setPackageName(permissionControllerPackage) .build(); int displayFlags = DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED | DisplayManager.VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED; Loading Loading @@ -164,7 +164,8 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub try { mVirtualDevice = virtualDeviceFactory.createVirtualDevice(mAppToken, attributionSource, virtualDeviceParams, new ComputerControlActivityListener()); mVirtualDevice.addActivityPolicyExemption(permissionController); applyActivityPolicy(); // Create the display with a clean identity so it can be trusted. mVirtualDisplayId = Binder.withCleanCallingIdentity(() -> { Loading Loading @@ -210,6 +211,32 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub } } private void applyActivityPolicy() throws RemoteException { String permissionControllerPackage = mInjector.getPermissionControllerPackageName(); List<String> exemptedPackageNames = new ArrayList<>(); if (Flags.computerControlActivityPolicyStrict()) { mVirtualDevice.setDevicePolicy(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM); exemptedPackageNames.addAll(mParams.getTargetPackageNames()); exemptedPackageNames.remove(permissionControllerPackage); } else if (Flags.computerControlActivityPolicyRelaxed()) { mVirtualDevice.setDevicePolicy(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM); exemptedPackageNames.addAll(mParams.getTargetPackageNames()); exemptedPackageNames.addAll(mInjector.getAllApplicationsWithoutLauncherActivity()); exemptedPackageNames.remove(permissionControllerPackage); } else { exemptedPackageNames.add(permissionControllerPackage); } for (String allowedPackageName : exemptedPackageNames) { mVirtualDevice.addActivityPolicyExemption( new ActivityPolicyExemption.Builder() .setPackageName(allowedPackageName) .build()); } } @Override public int getVirtualDisplayId() { return mVirtualDisplayId; Loading Loading @@ -380,6 +407,20 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub return mPackageManager.getPermissionControllerPackageName(); } public List<String> getAllApplicationsWithoutLauncherActivity() { List<String> result = new ArrayList<>(); List<ApplicationInfo> installedApplications = mPackageManager.getInstalledApplications(0); for (int i = 0; i < installedApplications.size(); i++) { ApplicationInfo applicationInfo = installedApplications.get(i); if (mPackageManager.getLaunchIntentForPackage(applicationInfo.packageName) == null) { result.add(applicationInfo.packageName); } } return result; } public void launchApplicationOnDisplayAsUser(String packageName, int displayId, UserHandle user) { Intent intent = mPackageManager.getLaunchIntentForPackage(packageName); Loading services/tests/servicestests/src/com/android/server/companion/virtual/computercontrol/ComputerControlSessionTest.java +152 −17 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.companion.virtual.computercontrol; 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_ACTIVITY; import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS; import static com.google.common.truth.Truth.assertThat; Loading @@ -26,6 +27,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; Loading @@ -35,6 +37,7 @@ import android.companion.virtual.ActivityPolicyExemption; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.computercontrol.ComputerControlSessionParams; import android.companion.virtualdevice.flags.Flags; import android.content.AttributionSource; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplayConfig; Loading @@ -45,7 +48,10 @@ import android.hardware.input.VirtualTouchEvent; import android.hardware.input.VirtualTouchscreenConfig; import android.os.Binder; import android.os.IBinder; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.view.DisplayInfo; import android.view.WindowManager; Loading @@ -53,6 +59,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; 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; Loading @@ -61,11 +68,14 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; @Presubmit @RunWith(AndroidJUnit4.class) public class ComputerControlSessionTest { @Rule public SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final String PERMISSION_CONTROLLER_PACKAGE = "permission.controller.package"; Loading @@ -75,8 +85,11 @@ public class ComputerControlSessionTest { private static final int DISPLAY_DPI = 480; private static final String TARGET_PACKAGE_1 = "com.android.foo"; private static final String TARGET_PACKAGE_2 = "com.android.bar"; private static final String TARGET_PACKAGE_3 = "com.android.foobar"; private static final List<String> TARGET_PACKAGE_NAMES = List.of(TARGET_PACKAGE_1, TARGET_PACKAGE_2); private static final List<String> PACKAGES_WITHOUT_LAUNCHER_ACTIVITY = List.of( TARGET_PACKAGE_3); private static final String UNDECLARED_TARGET_PACKAGE = "com.android.baz"; @Mock Loading Loading @@ -104,7 +117,8 @@ public class ComputerControlSessionTest { private AutoCloseable mMockitoSession; private final IBinder mAppToken = new Binder(); private final ComputerControlSessionParams mParams = new ComputerControlSessionParams.Builder() private final ComputerControlSessionParams mDefaultParams = new ComputerControlSessionParams.Builder() .setName(ComputerControlSessionTest.class.getSimpleName()) .setTargetPackageNames(TARGET_PACKAGE_NAMES) .build(); Loading @@ -122,13 +136,12 @@ public class ComputerControlSessionTest { when(mInjector.getPermissionControllerPackageName()) .thenReturn(PERMISSION_CONTROLLER_PACKAGE); when(mInjector.getAllApplicationsWithoutLauncherActivity()) .thenReturn(PACKAGES_WITHOUT_LAUNCHER_ACTIVITY); when(mVirtualDeviceFactory.createVirtualDevice(any(), any(), any(), any())) .thenReturn(mVirtualDevice); when(mVirtualDevice.createVirtualDisplay(any(), any())).thenReturn(VIRTUAL_DISPLAY_ID); when(mVirtualDevice.createVirtualTouchscreen(any(), any())).thenReturn(mVirtualTouchscreen); mSession = new ComputerControlSessionImpl(mAppToken, mParams, AttributionSource.myAttributionSource(), mVirtualDeviceFactory, mOnClosedListener, mInjector); } @After Loading @@ -138,23 +151,20 @@ public class ComputerControlSessionTest { @Test public void createSessionWithoutDisplaySurface_appliesCorrectParams() throws Exception { createComputerControlSession(mDefaultParams); verify(mVirtualDeviceFactory).createVirtualDevice( eq(mAppToken), any(), mVirtualDeviceParamsArgumentCaptor.capture(), any()); assertThat(mVirtualDeviceParamsArgumentCaptor.getValue().getName()) .isEqualTo(mParams.getName()); .isEqualTo(mDefaultParams.getName()); assertThat(mVirtualDeviceParamsArgumentCaptor.getValue() .getDevicePolicy(POLICY_TYPE_RECENTS)) .isEqualTo(DEVICE_POLICY_CUSTOM); verify(mVirtualDevice).addActivityPolicyExemption( mActivityPolicyExemptionArgumentCaptor.capture()); assertThat(mActivityPolicyExemptionArgumentCaptor.getValue().getPackageName()) .isEqualTo(PERMISSION_CONTROLLER_PACKAGE); verify(mVirtualDevice).createVirtualDisplay( mVirtualDisplayConfigArgumentCaptor.capture(), any()); VirtualDisplayConfig virtualDisplayConfig = mVirtualDisplayConfigArgumentCaptor.getValue(); assertThat(virtualDisplayConfig.getName()).contains(mParams.getName()); assertThat(virtualDisplayConfig.getName()).contains(mDefaultParams.getName()); assertThat(virtualDisplayConfig.getDensityDpi()).isEqualTo(DISPLAY_DPI); assertThat(virtualDisplayConfig.getHeight()).isEqualTo(DISPLAY_HEIGHT); Loading @@ -173,14 +183,14 @@ public class ComputerControlSessionTest { mVirtualDpadConfigArgumentCaptor.capture(), any()); VirtualDpadConfig virtualDpadConfig = mVirtualDpadConfigArgumentCaptor.getValue(); assertThat(virtualDpadConfig.getAssociatedDisplayId()).isEqualTo(VIRTUAL_DISPLAY_ID); assertThat(virtualDpadConfig.getInputDeviceName()).contains(mParams.getName()); assertThat(virtualDpadConfig.getInputDeviceName()).contains(mDefaultParams.getName()); verify(mVirtualDevice).createVirtualKeyboard( mVirtualKeyboardConfigArgumentCaptor.capture(), any()); VirtualKeyboardConfig virtualKeyboardConfig = mVirtualKeyboardConfigArgumentCaptor.getValue(); assertThat(virtualKeyboardConfig.getAssociatedDisplayId()).isEqualTo(VIRTUAL_DISPLAY_ID); assertThat(virtualKeyboardConfig.getInputDeviceName()).contains(mParams.getName()); assertThat(virtualKeyboardConfig.getInputDeviceName()).contains(mDefaultParams.getName()); verify(mVirtualDevice).createVirtualTouchscreen( mVirtualTouchscreenConfigArgumentCaptor.capture(), any()); Loading @@ -189,11 +199,109 @@ public class ComputerControlSessionTest { assertThat(virtualTouchscreenConfig.getAssociatedDisplayId()).isEqualTo(VIRTUAL_DISPLAY_ID); assertThat(virtualTouchscreenConfig.getWidth()).isEqualTo(DISPLAY_WIDTH); assertThat(virtualTouchscreenConfig.getHeight()).isEqualTo(DISPLAY_HEIGHT); assertThat(virtualTouchscreenConfig.getInputDeviceName()).contains(mParams.getName()); assertThat(virtualTouchscreenConfig.getInputDeviceName()).contains( mDefaultParams.getName()); } @Test @DisableFlags(value = {Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_RELAXED, Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_STRICT}) public void createSession_noActivityPolicy() throws Exception { createComputerControlSession(mDefaultParams); verify(mVirtualDevice, never()).setDevicePolicy(eq(POLICY_TYPE_ACTIVITY), anyInt()); verify(mVirtualDevice).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(PERMISSION_CONTROLLER_PACKAGE))); } @Test @EnableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_STRICT) @DisableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_RELAXED) public void createSession_strictActivityPolicy() throws Exception { createComputerControlSession(mDefaultParams); verify(mVirtualDevice).setDevicePolicy(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM); for (String expected : TARGET_PACKAGE_NAMES) { verify(mVirtualDevice).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(expected))); } } @Test @EnableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_STRICT) @DisableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_RELAXED) public void createSession_strictActivityPolicy_removesPermissionController() throws Exception { List<String> targetPackageNames = List.of(TARGET_PACKAGE_1, PERMISSION_CONTROLLER_PACKAGE); createComputerControlSession(new ComputerControlSessionParams.Builder() .setTargetPackageNames(targetPackageNames) .setName(ComputerControlSessionTest.class.getSimpleName()) .build()); verify(mVirtualDevice).setDevicePolicy(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM); verify(mVirtualDevice).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(TARGET_PACKAGE_1))); verify(mVirtualDevice, never()).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(PERMISSION_CONTROLLER_PACKAGE))); } @Test @EnableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_RELAXED) @DisableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_STRICT) public void createSession_relaxedActivityPolicy() throws Exception { createComputerControlSession(mDefaultParams); verify(mVirtualDevice).setDevicePolicy(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM); List<String> targetPackageNames = new ArrayList<>(mDefaultParams.getTargetPackageNames()); targetPackageNames.addAll(PACKAGES_WITHOUT_LAUNCHER_ACTIVITY); for (String expected : targetPackageNames) { verify(mVirtualDevice).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(expected))); } } @Test @EnableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_RELAXED) @DisableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_STRICT) public void createSession_relaxedActivityPolicy_removesPermissionController() throws Exception { createComputerControlSession(new ComputerControlSessionParams.Builder() .setName(ComputerControlSessionTest.class.getSimpleName()) .setTargetPackageNames(List.of(TARGET_PACKAGE_1, PERMISSION_CONTROLLER_PACKAGE)) .build()); verify(mVirtualDevice).setDevicePolicy(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM); List<String> targetPackageNames = new ArrayList<>(); targetPackageNames.add(TARGET_PACKAGE_1); targetPackageNames.addAll(PACKAGES_WITHOUT_LAUNCHER_ACTIVITY); for (String expected : targetPackageNames) { verify(mVirtualDevice).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(expected))); } verify(mVirtualDevice, never()).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(PERMISSION_CONTROLLER_PACKAGE))); } @Test @EnableFlags(value = {Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_RELAXED, Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_STRICT}) public void createSession_bothActivityPolicies() throws Exception { createComputerControlSession(mDefaultParams); verify(mVirtualDevice).setDevicePolicy(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM); for (String expected : TARGET_PACKAGE_NAMES) { verify(mVirtualDevice).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(expected))); } } @Test public void closeSession_closesVirtualDevice() throws Exception { createComputerControlSession(mDefaultParams); mSession.close(); verify(mVirtualDevice).setDevicePolicy(POLICY_TYPE_RECENTS, DEVICE_POLICY_DEFAULT); verify(mVirtualDevice).close(); Loading @@ -202,16 +310,19 @@ public class ComputerControlSessionTest { @Test public void getVirtualDisplayId_returnsCreatedDisplay() { createComputerControlSession(mDefaultParams); assertThat(mSession.getVirtualDisplayId()).isEqualTo(VIRTUAL_DISPLAY_ID); } @Test public void createSession_disablesAnimationsOnDisplay() { createComputerControlSession(mDefaultParams); verify(mInjector).disableAnimationsForDisplay(VIRTUAL_DISPLAY_ID); } @Test public void launchApplication_launchesApplication() { createComputerControlSession(mDefaultParams); mSession.launchApplication(TARGET_PACKAGE_1); verify(mInjector).launchApplicationOnDisplayAsUser( eq(TARGET_PACKAGE_1), eq(VIRTUAL_DISPLAY_ID), any()); Loading @@ -219,12 +330,14 @@ public class ComputerControlSessionTest { @Test public void launchApplication_undeclaredPackage_throws() { createComputerControlSession(mDefaultParams); assertThrows(IllegalArgumentException.class, () -> mSession.launchApplication(UNDECLARED_TARGET_PACKAGE)); } @Test public void tap_sendsTouchscreenEvents() throws Exception { createComputerControlSession(mDefaultParams); mSession.tap(60, 200); verify(mVirtualTouchscreen).sendTouchEvent(argThat( new MatchesTouchEvent(60, 200, VirtualTouchEvent.ACTION_DOWN))); Loading @@ -234,6 +347,7 @@ public class ComputerControlSessionTest { @Test public void swipe_sendsTouchscreenEvents() throws Exception { createComputerControlSession(mDefaultParams); mSession.swipe(60, 200, 180, 400); verify(mVirtualTouchscreen).sendTouchEvent(argThat( new MatchesTouchEvent(60, 200, VirtualTouchEvent.ACTION_DOWN))); Loading @@ -251,6 +365,27 @@ public class ComputerControlSessionTest { new MatchesTouchEvent(180, 400, VirtualTouchEvent.ACTION_UP))); } private void createComputerControlSession(ComputerControlSessionParams params) { mSession = new ComputerControlSessionImpl(mAppToken, params, AttributionSource.myAttributionSource(), mVirtualDeviceFactory, mOnClosedListener, mInjector); } private static class MatchesActivityPolicyExcemption implements ArgumentMatcher<ActivityPolicyExemption> { private final String mPackageName; MatchesActivityPolicyExcemption(String packageName) { mPackageName = packageName; } @Override public boolean matches(ActivityPolicyExemption argument) { return mPackageName.equals(argument.getPackageName()); } } private static class MatchesTouchEvent implements ArgumentMatcher<VirtualTouchEvent> { private final int mX; Loading Loading
core/java/android/companion/virtual/flags/flags.aconfig +20 −0 Original line number Diff line number Diff line Loading @@ -256,3 +256,23 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "computer_control_activity_policy_strict" namespace: "virtual_devices" description: "Implements a strict Activity policy for ComputerControl VirtualDevices" bug: "437849470" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "computer_control_activity_policy_relaxed" namespace: "virtual_devices" description: "Implements a relaxed Activity policy for ComputerControl VirtualDevices" bug: "437849470" metadata { purpose: PURPOSE_BUGFIX } }
services/companion/java/com/android/server/companion/virtual/computercontrol/ComputerControlSessionImpl.java +47 −6 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.companion.virtual.computercontrol; 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_ACTIVITY; import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS; import android.annotation.IntRange; Loading @@ -32,11 +33,13 @@ import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.computercontrol.ComputerControlSessionParams; import android.companion.virtual.computercontrol.IComputerControlSession; import android.companion.virtual.computercontrol.IInteractiveMirrorDisplay; import android.companion.virtualdevice.flags.Flags; import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; Loading @@ -62,6 +65,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.wm.WindowManagerInternal; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; Loading Loading @@ -115,11 +120,6 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub .setName(mParams.getName()) .setDevicePolicy(POLICY_TYPE_RECENTS, DEVICE_POLICY_CUSTOM) .build(); final String permissionControllerPackage = mInjector.getPermissionControllerPackageName(); final ActivityPolicyExemption permissionController = new ActivityPolicyExemption.Builder() .setPackageName(permissionControllerPackage) .build(); int displayFlags = DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED | DisplayManager.VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED; Loading Loading @@ -164,7 +164,8 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub try { mVirtualDevice = virtualDeviceFactory.createVirtualDevice(mAppToken, attributionSource, virtualDeviceParams, new ComputerControlActivityListener()); mVirtualDevice.addActivityPolicyExemption(permissionController); applyActivityPolicy(); // Create the display with a clean identity so it can be trusted. mVirtualDisplayId = Binder.withCleanCallingIdentity(() -> { Loading Loading @@ -210,6 +211,32 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub } } private void applyActivityPolicy() throws RemoteException { String permissionControllerPackage = mInjector.getPermissionControllerPackageName(); List<String> exemptedPackageNames = new ArrayList<>(); if (Flags.computerControlActivityPolicyStrict()) { mVirtualDevice.setDevicePolicy(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM); exemptedPackageNames.addAll(mParams.getTargetPackageNames()); exemptedPackageNames.remove(permissionControllerPackage); } else if (Flags.computerControlActivityPolicyRelaxed()) { mVirtualDevice.setDevicePolicy(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM); exemptedPackageNames.addAll(mParams.getTargetPackageNames()); exemptedPackageNames.addAll(mInjector.getAllApplicationsWithoutLauncherActivity()); exemptedPackageNames.remove(permissionControllerPackage); } else { exemptedPackageNames.add(permissionControllerPackage); } for (String allowedPackageName : exemptedPackageNames) { mVirtualDevice.addActivityPolicyExemption( new ActivityPolicyExemption.Builder() .setPackageName(allowedPackageName) .build()); } } @Override public int getVirtualDisplayId() { return mVirtualDisplayId; Loading Loading @@ -380,6 +407,20 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub return mPackageManager.getPermissionControllerPackageName(); } public List<String> getAllApplicationsWithoutLauncherActivity() { List<String> result = new ArrayList<>(); List<ApplicationInfo> installedApplications = mPackageManager.getInstalledApplications(0); for (int i = 0; i < installedApplications.size(); i++) { ApplicationInfo applicationInfo = installedApplications.get(i); if (mPackageManager.getLaunchIntentForPackage(applicationInfo.packageName) == null) { result.add(applicationInfo.packageName); } } return result; } public void launchApplicationOnDisplayAsUser(String packageName, int displayId, UserHandle user) { Intent intent = mPackageManager.getLaunchIntentForPackage(packageName); Loading
services/tests/servicestests/src/com/android/server/companion/virtual/computercontrol/ComputerControlSessionTest.java +152 −17 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.companion.virtual.computercontrol; 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_ACTIVITY; import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS; import static com.google.common.truth.Truth.assertThat; Loading @@ -26,6 +27,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; Loading @@ -35,6 +37,7 @@ import android.companion.virtual.ActivityPolicyExemption; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.computercontrol.ComputerControlSessionParams; import android.companion.virtualdevice.flags.Flags; import android.content.AttributionSource; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplayConfig; Loading @@ -45,7 +48,10 @@ import android.hardware.input.VirtualTouchEvent; import android.hardware.input.VirtualTouchscreenConfig; import android.os.Binder; import android.os.IBinder; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.view.DisplayInfo; import android.view.WindowManager; Loading @@ -53,6 +59,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; 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; Loading @@ -61,11 +68,14 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; @Presubmit @RunWith(AndroidJUnit4.class) public class ComputerControlSessionTest { @Rule public SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final String PERMISSION_CONTROLLER_PACKAGE = "permission.controller.package"; Loading @@ -75,8 +85,11 @@ public class ComputerControlSessionTest { private static final int DISPLAY_DPI = 480; private static final String TARGET_PACKAGE_1 = "com.android.foo"; private static final String TARGET_PACKAGE_2 = "com.android.bar"; private static final String TARGET_PACKAGE_3 = "com.android.foobar"; private static final List<String> TARGET_PACKAGE_NAMES = List.of(TARGET_PACKAGE_1, TARGET_PACKAGE_2); private static final List<String> PACKAGES_WITHOUT_LAUNCHER_ACTIVITY = List.of( TARGET_PACKAGE_3); private static final String UNDECLARED_TARGET_PACKAGE = "com.android.baz"; @Mock Loading Loading @@ -104,7 +117,8 @@ public class ComputerControlSessionTest { private AutoCloseable mMockitoSession; private final IBinder mAppToken = new Binder(); private final ComputerControlSessionParams mParams = new ComputerControlSessionParams.Builder() private final ComputerControlSessionParams mDefaultParams = new ComputerControlSessionParams.Builder() .setName(ComputerControlSessionTest.class.getSimpleName()) .setTargetPackageNames(TARGET_PACKAGE_NAMES) .build(); Loading @@ -122,13 +136,12 @@ public class ComputerControlSessionTest { when(mInjector.getPermissionControllerPackageName()) .thenReturn(PERMISSION_CONTROLLER_PACKAGE); when(mInjector.getAllApplicationsWithoutLauncherActivity()) .thenReturn(PACKAGES_WITHOUT_LAUNCHER_ACTIVITY); when(mVirtualDeviceFactory.createVirtualDevice(any(), any(), any(), any())) .thenReturn(mVirtualDevice); when(mVirtualDevice.createVirtualDisplay(any(), any())).thenReturn(VIRTUAL_DISPLAY_ID); when(mVirtualDevice.createVirtualTouchscreen(any(), any())).thenReturn(mVirtualTouchscreen); mSession = new ComputerControlSessionImpl(mAppToken, mParams, AttributionSource.myAttributionSource(), mVirtualDeviceFactory, mOnClosedListener, mInjector); } @After Loading @@ -138,23 +151,20 @@ public class ComputerControlSessionTest { @Test public void createSessionWithoutDisplaySurface_appliesCorrectParams() throws Exception { createComputerControlSession(mDefaultParams); verify(mVirtualDeviceFactory).createVirtualDevice( eq(mAppToken), any(), mVirtualDeviceParamsArgumentCaptor.capture(), any()); assertThat(mVirtualDeviceParamsArgumentCaptor.getValue().getName()) .isEqualTo(mParams.getName()); .isEqualTo(mDefaultParams.getName()); assertThat(mVirtualDeviceParamsArgumentCaptor.getValue() .getDevicePolicy(POLICY_TYPE_RECENTS)) .isEqualTo(DEVICE_POLICY_CUSTOM); verify(mVirtualDevice).addActivityPolicyExemption( mActivityPolicyExemptionArgumentCaptor.capture()); assertThat(mActivityPolicyExemptionArgumentCaptor.getValue().getPackageName()) .isEqualTo(PERMISSION_CONTROLLER_PACKAGE); verify(mVirtualDevice).createVirtualDisplay( mVirtualDisplayConfigArgumentCaptor.capture(), any()); VirtualDisplayConfig virtualDisplayConfig = mVirtualDisplayConfigArgumentCaptor.getValue(); assertThat(virtualDisplayConfig.getName()).contains(mParams.getName()); assertThat(virtualDisplayConfig.getName()).contains(mDefaultParams.getName()); assertThat(virtualDisplayConfig.getDensityDpi()).isEqualTo(DISPLAY_DPI); assertThat(virtualDisplayConfig.getHeight()).isEqualTo(DISPLAY_HEIGHT); Loading @@ -173,14 +183,14 @@ public class ComputerControlSessionTest { mVirtualDpadConfigArgumentCaptor.capture(), any()); VirtualDpadConfig virtualDpadConfig = mVirtualDpadConfigArgumentCaptor.getValue(); assertThat(virtualDpadConfig.getAssociatedDisplayId()).isEqualTo(VIRTUAL_DISPLAY_ID); assertThat(virtualDpadConfig.getInputDeviceName()).contains(mParams.getName()); assertThat(virtualDpadConfig.getInputDeviceName()).contains(mDefaultParams.getName()); verify(mVirtualDevice).createVirtualKeyboard( mVirtualKeyboardConfigArgumentCaptor.capture(), any()); VirtualKeyboardConfig virtualKeyboardConfig = mVirtualKeyboardConfigArgumentCaptor.getValue(); assertThat(virtualKeyboardConfig.getAssociatedDisplayId()).isEqualTo(VIRTUAL_DISPLAY_ID); assertThat(virtualKeyboardConfig.getInputDeviceName()).contains(mParams.getName()); assertThat(virtualKeyboardConfig.getInputDeviceName()).contains(mDefaultParams.getName()); verify(mVirtualDevice).createVirtualTouchscreen( mVirtualTouchscreenConfigArgumentCaptor.capture(), any()); Loading @@ -189,11 +199,109 @@ public class ComputerControlSessionTest { assertThat(virtualTouchscreenConfig.getAssociatedDisplayId()).isEqualTo(VIRTUAL_DISPLAY_ID); assertThat(virtualTouchscreenConfig.getWidth()).isEqualTo(DISPLAY_WIDTH); assertThat(virtualTouchscreenConfig.getHeight()).isEqualTo(DISPLAY_HEIGHT); assertThat(virtualTouchscreenConfig.getInputDeviceName()).contains(mParams.getName()); assertThat(virtualTouchscreenConfig.getInputDeviceName()).contains( mDefaultParams.getName()); } @Test @DisableFlags(value = {Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_RELAXED, Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_STRICT}) public void createSession_noActivityPolicy() throws Exception { createComputerControlSession(mDefaultParams); verify(mVirtualDevice, never()).setDevicePolicy(eq(POLICY_TYPE_ACTIVITY), anyInt()); verify(mVirtualDevice).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(PERMISSION_CONTROLLER_PACKAGE))); } @Test @EnableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_STRICT) @DisableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_RELAXED) public void createSession_strictActivityPolicy() throws Exception { createComputerControlSession(mDefaultParams); verify(mVirtualDevice).setDevicePolicy(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM); for (String expected : TARGET_PACKAGE_NAMES) { verify(mVirtualDevice).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(expected))); } } @Test @EnableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_STRICT) @DisableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_RELAXED) public void createSession_strictActivityPolicy_removesPermissionController() throws Exception { List<String> targetPackageNames = List.of(TARGET_PACKAGE_1, PERMISSION_CONTROLLER_PACKAGE); createComputerControlSession(new ComputerControlSessionParams.Builder() .setTargetPackageNames(targetPackageNames) .setName(ComputerControlSessionTest.class.getSimpleName()) .build()); verify(mVirtualDevice).setDevicePolicy(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM); verify(mVirtualDevice).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(TARGET_PACKAGE_1))); verify(mVirtualDevice, never()).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(PERMISSION_CONTROLLER_PACKAGE))); } @Test @EnableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_RELAXED) @DisableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_STRICT) public void createSession_relaxedActivityPolicy() throws Exception { createComputerControlSession(mDefaultParams); verify(mVirtualDevice).setDevicePolicy(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM); List<String> targetPackageNames = new ArrayList<>(mDefaultParams.getTargetPackageNames()); targetPackageNames.addAll(PACKAGES_WITHOUT_LAUNCHER_ACTIVITY); for (String expected : targetPackageNames) { verify(mVirtualDevice).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(expected))); } } @Test @EnableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_RELAXED) @DisableFlags(Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_STRICT) public void createSession_relaxedActivityPolicy_removesPermissionController() throws Exception { createComputerControlSession(new ComputerControlSessionParams.Builder() .setName(ComputerControlSessionTest.class.getSimpleName()) .setTargetPackageNames(List.of(TARGET_PACKAGE_1, PERMISSION_CONTROLLER_PACKAGE)) .build()); verify(mVirtualDevice).setDevicePolicy(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM); List<String> targetPackageNames = new ArrayList<>(); targetPackageNames.add(TARGET_PACKAGE_1); targetPackageNames.addAll(PACKAGES_WITHOUT_LAUNCHER_ACTIVITY); for (String expected : targetPackageNames) { verify(mVirtualDevice).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(expected))); } verify(mVirtualDevice, never()).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(PERMISSION_CONTROLLER_PACKAGE))); } @Test @EnableFlags(value = {Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_RELAXED, Flags.FLAG_COMPUTER_CONTROL_ACTIVITY_POLICY_STRICT}) public void createSession_bothActivityPolicies() throws Exception { createComputerControlSession(mDefaultParams); verify(mVirtualDevice).setDevicePolicy(POLICY_TYPE_ACTIVITY, DEVICE_POLICY_CUSTOM); for (String expected : TARGET_PACKAGE_NAMES) { verify(mVirtualDevice).addActivityPolicyExemption( argThat(new MatchesActivityPolicyExcemption(expected))); } } @Test public void closeSession_closesVirtualDevice() throws Exception { createComputerControlSession(mDefaultParams); mSession.close(); verify(mVirtualDevice).setDevicePolicy(POLICY_TYPE_RECENTS, DEVICE_POLICY_DEFAULT); verify(mVirtualDevice).close(); Loading @@ -202,16 +310,19 @@ public class ComputerControlSessionTest { @Test public void getVirtualDisplayId_returnsCreatedDisplay() { createComputerControlSession(mDefaultParams); assertThat(mSession.getVirtualDisplayId()).isEqualTo(VIRTUAL_DISPLAY_ID); } @Test public void createSession_disablesAnimationsOnDisplay() { createComputerControlSession(mDefaultParams); verify(mInjector).disableAnimationsForDisplay(VIRTUAL_DISPLAY_ID); } @Test public void launchApplication_launchesApplication() { createComputerControlSession(mDefaultParams); mSession.launchApplication(TARGET_PACKAGE_1); verify(mInjector).launchApplicationOnDisplayAsUser( eq(TARGET_PACKAGE_1), eq(VIRTUAL_DISPLAY_ID), any()); Loading @@ -219,12 +330,14 @@ public class ComputerControlSessionTest { @Test public void launchApplication_undeclaredPackage_throws() { createComputerControlSession(mDefaultParams); assertThrows(IllegalArgumentException.class, () -> mSession.launchApplication(UNDECLARED_TARGET_PACKAGE)); } @Test public void tap_sendsTouchscreenEvents() throws Exception { createComputerControlSession(mDefaultParams); mSession.tap(60, 200); verify(mVirtualTouchscreen).sendTouchEvent(argThat( new MatchesTouchEvent(60, 200, VirtualTouchEvent.ACTION_DOWN))); Loading @@ -234,6 +347,7 @@ public class ComputerControlSessionTest { @Test public void swipe_sendsTouchscreenEvents() throws Exception { createComputerControlSession(mDefaultParams); mSession.swipe(60, 200, 180, 400); verify(mVirtualTouchscreen).sendTouchEvent(argThat( new MatchesTouchEvent(60, 200, VirtualTouchEvent.ACTION_DOWN))); Loading @@ -251,6 +365,27 @@ public class ComputerControlSessionTest { new MatchesTouchEvent(180, 400, VirtualTouchEvent.ACTION_UP))); } private void createComputerControlSession(ComputerControlSessionParams params) { mSession = new ComputerControlSessionImpl(mAppToken, params, AttributionSource.myAttributionSource(), mVirtualDeviceFactory, mOnClosedListener, mInjector); } private static class MatchesActivityPolicyExcemption implements ArgumentMatcher<ActivityPolicyExemption> { private final String mPackageName; MatchesActivityPolicyExcemption(String packageName) { mPackageName = packageName; } @Override public boolean matches(ActivityPolicyExemption argument) { return mPackageName.equals(argument.getPackageName()); } } private static class MatchesTouchEvent implements ArgumentMatcher<VirtualTouchEvent> { private final int mX; Loading