Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e9dff546 authored by Vladimir Komsiyski's avatar Vladimir Komsiyski Committed by Android (Google) Code Review
Browse files

Merge "Only support mirror display creation for device streaming" into main

parents e30de592 8fae556d
Loading
Loading
Loading
Loading
+7 −2
Original line number Original line Diff line number Diff line
@@ -88,6 +88,11 @@ interface IVirtualDevice {
     */
     */
    boolean hasCustomAudioInputSupport();
    boolean hasCustomAudioInputSupport();


    /**
     * Returns whether this device is allowed to create mirror displays.
     */
    boolean canCreateMirrorDisplays();

    /**
    /**
     * Closes the virtual device and frees all associated resources.
     * Closes the virtual device and frees all associated resources.
     */
     */
+1 −1
Original line number Original line Diff line number Diff line
@@ -83,7 +83,7 @@
    <string name="title_nearby_device_streaming">Allow &lt;strong&gt;<xliff:g id="device_name" example="NearbyStreamer">%1$s</xliff:g>&lt;/strong&gt; to stream your <xliff:g id="device_type" example="phone">%2$s</xliff:g>\u2019s apps and system features to &lt;strong&gt;<xliff:g id="device_name" example="Chromebook">%3$s</xliff:g>&lt;/strong&gt;?</string>
    <string name="title_nearby_device_streaming">Allow &lt;strong&gt;<xliff:g id="device_name" example="NearbyStreamer">%1$s</xliff:g>&lt;/strong&gt; to stream your <xliff:g id="device_type" example="phone">%2$s</xliff:g>\u2019s apps and system features to &lt;strong&gt;<xliff:g id="device_name" example="Chromebook">%3$s</xliff:g>&lt;/strong&gt;?</string>


    <!-- Summary for associating an application with a companion device of NEARBY_DEVICE_STREAMING profile [CHAR LIMIT=NONE] -->
    <!-- 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.&lt;br/>&lt;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.&lt;br/>&lt;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] -->
    <!-- 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>
    <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>
+13 −0
Original line number Original line Diff line number Diff line
@@ -42,6 +42,7 @@ import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager;
import android.app.compat.CompatChanges;
import android.app.compat.CompatChanges;
import android.companion.AssociationInfo;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.virtual.ActivityPolicyExemption;
import android.companion.virtual.ActivityPolicyExemption;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDeviceActivityListener;
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 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.
     * 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();
        return mAssociationInfo == null ? mParams.getName() : mAssociationInfo.getDisplayName();
    }
    }


    String getDeviceProfile() {
        return mAssociationInfo == null ? null : mAssociationInfo.getDeviceProfile();
    }

    /** Returns the public representation of the device. */
    /** Returns the public representation of the device. */
    VirtualDevice getPublicVirtualDeviceObject() {
    VirtualDevice getPublicVirtualDeviceObject() {
        return mPublicVirtualDeviceObject;
        return mPublicVirtualDeviceObject;
@@ -1294,6 +1302,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        return hasCustomAudioInputSupportInternal();
        return hasCustomAudioInputSupportInternal();
    }
    }


    @Override
    public boolean canCreateMirrorDisplays() {
        return DEVICE_PROFILES_ALLOWING_MIRROR_DISPLAYS.contains(getDeviceProfile());
    }

    private boolean hasCustomAudioInputSupportInternal() {
    private boolean hasCustomAudioInputSupportInternal() {
        if (!Flags.vdmPublicApis()) {
        if (!Flags.vdmPublicApis()) {
            return false;
            return false;
+40 −22
Original line number Original line Diff line number Diff line
@@ -1661,34 +1661,50 @@ public final class DisplayManagerService extends SystemService {
        return false;
        return false;
    }
    }


    private boolean canProjectVideo(IMediaProjection projection) {
    private boolean hasVideoOutputPermission(String func) {
        if (projection != null) {
        return checkCallingPermission(CAPTURE_VIDEO_OUTPUT, func)
            try {
                || hasSecureVideoOutputPermission(func);
                if (projection.canProjectVideo()) {
    }
                    return true;

    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) {
        } catch (RemoteException e) {
                Slog.e(TAG, "Unable to query projection service for permissions", e);
            Slog.e(TAG, "Unable to query virtual device for permissions", e);
            return false;
        }
        }
    }
    }
        if (checkCallingPermission(CAPTURE_VIDEO_OUTPUT, "canProjectVideo()")) {

            return true;
    private boolean canProjectVideo(IMediaProjection projection) {
        if (projection == null) {
            return false;
        }
        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) {
    private boolean canProjectSecureVideo(IMediaProjection projection) {
        if (projection != null) {
        if (projection == null) {
            try {
            return false;
                if (projection.canProjectSecureVideo()) {
                    return true;
        }
        }
        try {
            return projection.canProjectSecureVideo();
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            Slog.e(TAG, "Unable to query projection service for permissions", 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) {
    private boolean checkCallingPermission(String permission, String func) {
        if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
        if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
@@ -1793,7 +1809,8 @@ public final class DisplayManagerService extends SystemService {
                && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
                && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
            // Only a valid media projection or a virtual device can create a mirror virtual
            // Only a valid media projection or a virtual device can create a mirror virtual
            // display.
            // display.
            if (!canProjectVideo(projection) && virtualDevice == null) {
            if (!canProjectVideo(projection) && !canCreateMirrorDisplays(virtualDevice)
                    && !hasVideoOutputPermission("createVirtualDisplayInternal")) {
                throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
                throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
                        + "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
                        + "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
                        + "MediaProjection token in order to create a screen sharing virtual "
                        + "MediaProjection token in order to create a screen sharing virtual "
@@ -1803,7 +1820,8 @@ public final class DisplayManagerService extends SystemService {
            }
            }
        }
        }
        if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
        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 "
                throw new SecurityException("Requires CAPTURE_SECURE_VIDEO_OUTPUT "
                        + "or an appropriate MediaProjection token to create a "
                        + "or an appropriate MediaProjection token to create a "
                        + "secure virtual display.");
                        + "secure virtual display.");
+36 −0
Original line number Original line Diff line number Diff line
@@ -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
     * Tests that the virtual display is added to the default display group when created with
     * VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR using a virtual device.
     * VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR using a virtual device.
@@ -1320,6 +1352,7 @@ public class DisplayManagerServiceTest {
        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
        when(virtualDevice.getDeviceId()).thenReturn(1);
        when(virtualDevice.getDeviceId()).thenReturn(1);
        when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true);
        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);


        // Create an auto-mirror virtual display using a virtual device.
        // Create an auto-mirror virtual display using a virtual device.
@@ -1352,6 +1385,7 @@ public class DisplayManagerServiceTest {
        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
        when(virtualDevice.getDeviceId()).thenReturn(1);
        when(virtualDevice.getDeviceId()).thenReturn(1);
        when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true);
        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);


        // Create an auto-mirror virtual display using a virtual device.
        // Create an auto-mirror virtual display using a virtual device.
@@ -1418,6 +1452,7 @@ public class DisplayManagerServiceTest {
        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
        when(virtualDevice.getDeviceId()).thenReturn(1);
        when(virtualDevice.getDeviceId()).thenReturn(1);
        when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true);
        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
        when(mContext.checkCallingPermission(ADD_ALWAYS_UNLOCKED_DISPLAY))
        when(mContext.checkCallingPermission(ADD_ALWAYS_UNLOCKED_DISPLAY))
                .thenReturn(PackageManager.PERMISSION_GRANTED);
                .thenReturn(PackageManager.PERMISSION_GRANTED);
@@ -1453,6 +1488,7 @@ public class DisplayManagerServiceTest {
        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
        when(virtualDevice.getDeviceId()).thenReturn(1);
        when(virtualDevice.getDeviceId()).thenReturn(1);
        when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true);
        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);


        // Create an auto-mirror virtual display using a virtual device.
        // Create an auto-mirror virtual display using a virtual device.