diff options
5 files changed, 97 insertions, 25 deletions
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl index 8916ce27cf68..6fe0a7342216 100644 --- a/core/java/android/companion/virtual/IVirtualDevice.aidl +++ b/core/java/android/companion/virtual/IVirtualDevice.aidl @@ -84,11 +84,16 @@ interface IVirtualDevice { int getDevicePolicy(int policyType); /** - * Returns whether the device has a valid microphone. - */ + * Returns whether the device has a valid microphone. + */ boolean hasCustomAudioInputSupport(); /** + * Returns whether this device is allowed to create mirror displays. + */ + boolean canCreateMirrorDisplays(); + + /** * Closes the virtual device and frees all associated resources. */ @EnforcePermission("CREATE_VIRTUAL_DEVICE") diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml index a57d6eb11927..eb9e7372ee78 100644 --- a/packages/CompanionDeviceManager/res/values/strings.xml +++ b/packages/CompanionDeviceManager/res/values/strings.xml @@ -83,7 +83,7 @@ <string name="title_nearby_device_streaming">Allow <strong><xliff:g id="device_name" example="NearbyStreamer">%1$s</xliff:g></strong> to stream your <xliff:g id="device_type" example="phone">%2$s</xliff:g>\u2019s apps and system features to <strong><xliff:g id="device_name" example="Chromebook">%3$s</xliff:g></strong>?</string> <!-- Summary for associating an application with a companion device of NEARBY_DEVICE_STREAMING profile [CHAR LIMIT=NONE] --> - <string name="summary_nearby_device_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will have access to anything that’s visible or played on your <xliff:g id="device_type" example="phone">%2$s</xliff:g>, including audio, photos, payment info, passwords, and messages.<br/><br/><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will be able to stream apps and system features to <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g> until you remove access to this permission.</string> + <string name="summary_nearby_device_streaming"><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will have access to anything that’s visible or played on <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g>, including audio, photos, payment info, passwords, and messages.<br/><br/><xliff:g id="app_name" example="Exo">%1$s</xliff:g> will be able to stream apps and system features to <xliff:g id="device_name" example="Chromebook">%3$s</xliff:g> until you remove access to this permission.</string> <!-- Description of the helper dialog for NEARBY_DEVICE_STREAMING profile. [CHAR LIMIT=NONE] --> <string name="helper_summary_nearby_device_streaming"><xliff:g id="app_name" example="NearbyStreamerApp">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_name" example="NearbyDevice">%2$s</xliff:g> to stream apps and other system features between your devices</string> diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index cd2dd3a27c9a..81ae717ca959 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -42,6 +42,7 @@ import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; import android.app.compat.CompatChanges; import android.companion.AssociationInfo; +import android.companion.AssociationRequest; import android.companion.virtual.ActivityPolicyExemption; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.IVirtualDeviceActivityListener; @@ -153,6 +154,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private static final String PERSISTENT_ID_PREFIX_CDM_ASSOCIATION = "companion:"; + private static final List<String> DEVICE_PROFILES_ALLOWING_MIRROR_DISPLAYS = List.of( + AssociationRequest.DEVICE_PROFILE_APP_STREAMING); + /** * Timeout until {@link #launchPendingIntent} stops waiting for an activity to be launched. */ @@ -498,6 +502,10 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub return mAssociationInfo == null ? mParams.getName() : mAssociationInfo.getDisplayName(); } + String getDeviceProfile() { + return mAssociationInfo == null ? null : mAssociationInfo.getDeviceProfile(); + } + /** Returns the public representation of the device. */ VirtualDevice getPublicVirtualDeviceObject() { return mPublicVirtualDeviceObject; @@ -1294,6 +1302,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub return hasCustomAudioInputSupportInternal(); } + @Override + public boolean canCreateMirrorDisplays() { + return DEVICE_PROFILES_ALLOWING_MIRROR_DISPLAYS.contains(getDeviceProfile()); + } + private boolean hasCustomAudioInputSupportInternal() { if (!Flags.vdmPublicApis()) { return false; diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index ae33b83b49dc..3ea2dcaa86fd 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1549,33 +1549,49 @@ public final class DisplayManagerService extends SystemService { return false; } + private boolean hasVideoOutputPermission(String func) { + return checkCallingPermission(CAPTURE_VIDEO_OUTPUT, func) + || hasSecureVideoOutputPermission(func); + } + + private boolean hasSecureVideoOutputPermission(String func) { + return checkCallingPermission(CAPTURE_SECURE_VIDEO_OUTPUT, func); + } + + private boolean canCreateMirrorDisplays(IVirtualDevice virtualDevice) { + if (virtualDevice == null) { + return false; + } + try { + return virtualDevice.canCreateMirrorDisplays(); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to query virtual device for permissions", e); + return false; + } + } + private boolean canProjectVideo(IMediaProjection projection) { - if (projection != null) { - try { - if (projection.canProjectVideo()) { - return true; - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to query projection service for permissions", e); - } + if (projection == null) { + return false; } - if (checkCallingPermission(CAPTURE_VIDEO_OUTPUT, "canProjectVideo()")) { - return true; + try { + return projection.canProjectVideo(); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to query projection service for permissions", e); + return false; } - return canProjectSecureVideo(projection); } private boolean canProjectSecureVideo(IMediaProjection projection) { - if (projection != null) { - try { - if (projection.canProjectSecureVideo()) { - return true; - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to query projection service for permissions", e); - } + if (projection == null) { + return false; + } + try { + return projection.canProjectSecureVideo(); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to query projection service for permissions", e); + return false; } - return checkCallingPermission(CAPTURE_SECURE_VIDEO_OUTPUT, "canProjectSecureVideo()"); } private boolean checkCallingPermission(String permission, String func) { @@ -1681,7 +1697,8 @@ public final class DisplayManagerService extends SystemService { && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) { // Only a valid media projection or a virtual device can create a mirror virtual // display. - if (!canProjectVideo(projection) && virtualDevice == null) { + if (!canProjectVideo(projection) && !canCreateMirrorDisplays(virtualDevice) + && !hasVideoOutputPermission("createVirtualDisplayInternal")) { throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or " + "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate " + "MediaProjection token in order to create a screen sharing virtual " @@ -1691,7 +1708,8 @@ public final class DisplayManagerService extends SystemService { } } if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) { - if (!canProjectSecureVideo(projection)) { + if (!canProjectSecureVideo(projection) + && !hasSecureVideoOutputPermission("createVirtualDisplayInternal")) { throw new SecurityException("Requires CAPTURE_SECURE_VIDEO_OUTPUT " + "or an appropriate MediaProjection token to create a " + "secure virtual display."); diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java index 255dcb083518..db58232794c7 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -1308,6 +1308,38 @@ public class DisplayManagerServiceTest { } /** + * Tests that it's not allowed to create an auto-mirror virtual display without + * CAPTURE_VIDEO_OUTPUT permission or a virtual device that can mirror displays + */ + @Test + public void createAutoMirrorDisplay_withoutPermissionOrAllowedVirtualDevice_throwsException() + throws Exception { + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerInternal localService = displayManager.new LocalService(); + registerDefaultDisplays(displayManager); + when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); + IVirtualDevice virtualDevice = mock(IVirtualDevice.class); + when(virtualDevice.getDeviceId()).thenReturn(1); + when(virtualDevice.canCreateMirrorDisplays()).thenReturn(false); + when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true); + when(mContext.checkCallingPermission(CAPTURE_VIDEO_OUTPUT)).thenReturn( + PackageManager.PERMISSION_DENIED); + + final VirtualDisplayConfig.Builder builder = + new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320) + .setFlags(VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) + .setUniqueId("uniqueId --- mirror display"); + assertThrows(SecurityException.class, () -> { + localService.createVirtualDisplay( + builder.build(), + mMockAppToken /* callback */, + virtualDevice /* virtualDeviceToken */, + mock(DisplayWindowPolicyController.class), + PACKAGE_NAME); + }); + } + + /** * Tests that the virtual display is added to the default display group when created with * VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR using a virtual device. */ @@ -1319,6 +1351,7 @@ public class DisplayManagerServiceTest { when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); IVirtualDevice virtualDevice = mock(IVirtualDevice.class); when(virtualDevice.getDeviceId()).thenReturn(1); + when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true); when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true); // Create an auto-mirror virtual display using a virtual device. @@ -1351,6 +1384,7 @@ public class DisplayManagerServiceTest { when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); IVirtualDevice virtualDevice = mock(IVirtualDevice.class); when(virtualDevice.getDeviceId()).thenReturn(1); + when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true); when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true); // Create an auto-mirror virtual display using a virtual device. @@ -1417,6 +1451,7 @@ public class DisplayManagerServiceTest { when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); IVirtualDevice virtualDevice = mock(IVirtualDevice.class); when(virtualDevice.getDeviceId()).thenReturn(1); + when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true); when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true); when(mContext.checkCallingPermission(ADD_ALWAYS_UNLOCKED_DISPLAY)) .thenReturn(PackageManager.PERMISSION_GRANTED); @@ -1452,6 +1487,7 @@ public class DisplayManagerServiceTest { when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); IVirtualDevice virtualDevice = mock(IVirtualDevice.class); when(virtualDevice.getDeviceId()).thenReturn(1); + when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true); when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true); // Create an auto-mirror virtual display using a virtual device. |