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

Commit 72f69c6e authored by Dorin Drimus's avatar Dorin Drimus
Browse files

Allow virtual external cameras on default policy

Virtual devices with a default camera policy can create (only) virtual
external cameras that are associated with the default device context.

Bug: 375609768
Test: atest CtsVirtualDevicesCameraTestCases
Test: atest VirtualCameraControllerTest
Flag: android.companion.virtualdevice.flags.external_virtual_cameras
Change-Id: I7fe67ca0b05c278a4c153d338fc89b5f599acda7
parent d056b24d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1083,6 +1083,7 @@ public final class VirtualDeviceManager {
         * @throws UnsupportedOperationException if virtual camera isn't supported on this device.
         * @see VirtualDeviceParams#POLICY_TYPE_CAMERA
         */
        // TODO: b/406957588 - Update documentation after 25Q2 release
        @NonNull
        public VirtualCamera createVirtualCamera(@NonNull VirtualCameraConfig config) {
            return mVirtualDeviceInternal.createVirtualCamera(Objects.requireNonNull(config));
+4 −5
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import android.companion.virtual.VirtualDevice;
import android.companion.virtualdevice.flags.Flags;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraMetadata;
import android.os.Parcel;
import android.os.Parcelable;
@@ -283,17 +282,17 @@ public final class VirtualCameraConfig implements Parcelable {
        }

        /**
         * Sets the lens facing direction of the virtual camera.
         * Sets the lens facing direction of the virtual camera, can be either
         * {@link CameraMetadata#LENS_FACING_FRONT} or {@link CameraMetadata#LENS_FACING_BACK}.
         *
         * <p>A {@link VirtualDevice} can have at most one {@link VirtualCamera} with
         * {@link CameraMetadata#LENS_FACING_FRONT} and at most one {@link VirtualCamera} with
         * {@link CameraMetadata#LENS_FACING_BACK}, though it can create multiple cameras with
         * {@link CameraMetadata#LENS_FACING_EXTERNAL}.
         * {@link CameraMetadata#LENS_FACING_BACK}.
         *
         * @param lensFacing The direction that the virtual camera faces relative to the device's
         *                   screen.
         * @see CameraCharacteristics#LENS_FACING
         */
        // TODO: b/406957588 - Update documentation after 25Q2 release
        @NonNull
        public Builder setLensFacing(int lensFacing) {
            boolean allowLensFacing = lensFacing == CameraMetadata.LENS_FACING_FRONT
+15 −8
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.companion.virtualcamera.IVirtualCameraService;
import android.companion.virtualcamera.VirtualCameraConfiguration;
import android.companion.virtualdevice.flags.Flags;
import android.content.AttributionSource;
import android.content.Context;
import android.hardware.camera2.CameraMetadata;
import android.os.Binder;
import android.os.IBinder;
@@ -195,21 +196,25 @@ public final class VirtualCameraController implements IBinder.DeathRecipient {
    }

    private void checkConfigByPolicy(VirtualCameraConfig config) {
        // Multiple external cameras are allowed on any policy
        if (Flags.externalVirtualCameras()
                && CameraMetadata.LENS_FACING_EXTERNAL == config.getLensFacing()) {
            return;
        }

        if (mCameraPolicy == DEVICE_POLICY_DEFAULT) {
            throw new IllegalArgumentException(
                    "Cannot create virtual camera with DEVICE_POLICY_DEFAULT for "
                            + "POLICY_TYPE_CAMERA");
        } else if (isNonExternalLensFacingAlreadyPresent(config.getLensFacing())) {
                            + "POLICY_TYPE_CAMERA and lens facing " + config.getLensFacing());
        }

        if (isLensFacingAlreadyPresent(config.getLensFacing())) {
            throw new IllegalArgumentException(
                    "Only a single virtual camera can be created with lens facing "
                            + config.getLensFacing());
        }
    }

    private boolean isNonExternalLensFacingAlreadyPresent(int lensFacing) {
        if (Flags.externalVirtualCameras() && CameraMetadata.LENS_FACING_EXTERNAL == lensFacing) {
            return false;
        }
    private boolean isLensFacingAlreadyPresent(int lensFacing) {
        synchronized (mCameras) {
            for (CameraDescriptor cameraDescriptor : mCameras.values()) {
                if (cameraDescriptor.mConfig.getLensFacing() == lensFacing) {
@@ -255,8 +260,10 @@ public final class VirtualCameraController implements IBinder.DeathRecipient {
    private boolean registerCameraWithService(VirtualCameraConfig config) throws RemoteException {
        VirtualCameraConfiguration serviceConfiguration = getServiceCameraConfiguration(config);
        synchronized (mServiceLock) {
            int ownerDeviceId =
                    mCameraPolicy != DEVICE_POLICY_DEFAULT ? mDeviceId : Context.DEVICE_ID_DEFAULT;
            return mVirtualCameraService.registerCamera(config.getCallback().asBinder(),
                    serviceConfiguration, mDeviceId);
                    serviceConfiguration, ownerDeviceId);
        }
    }

+34 −3
Original line number Diff line number Diff line
@@ -177,7 +177,7 @@ public class VirtualCameraControllerTest {
                CAMERA_LENS_FACING_2);
    }

    @Parameters(method = "getSingleCamerasLensFacingDirections")
    @Parameters(method = "getFixedCamerasLensFacingDirections")
    @Test
    public void registerMultipleSameLensFacingCameras_withCustomCameraPolicy_throwsException(
            int lensFacing) {
@@ -215,7 +215,7 @@ public class VirtualCameraControllerTest {
                        LENS_FACING_EXTERNAL), AttributionSource.myAttributionSource()));
    }

    @Parameters(method = "getAllLensFacingDirections")
    @Parameters(method = "getFixedCamerasLensFacingDirections")
    @Test
    public void registerCamera_withDefaultCameraPolicy_throwsException(int lensFacing) {
        mVirtualCameraController.close();
@@ -229,6 +229,37 @@ public class VirtualCameraControllerTest {
                        AttributionSource.myAttributionSource()));
    }

    @Test
    @EnableFlags(Flags.FLAG_EXTERNAL_VIRTUAL_CAMERAS)
    public void registerCamera_withDefaultCameraPolicy_allowsMultipleExternal() {
        mVirtualCameraController.close();
        mVirtualCameraController = new VirtualCameraController(mVirtualCameraServiceMock,
                DEVICE_POLICY_DEFAULT, DEVICE_ID);

        mVirtualCameraController.registerCamera(
                createVirtualCameraConfig(CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1,
                        CAMERA_MAX_FPS_1, CAMERA_NAME_1, CAMERA_SENSOR_ORIENTATION_1,
                        LENS_FACING_EXTERNAL), AttributionSource.myAttributionSource());

        mVirtualCameraController.registerCamera(
                createVirtualCameraConfig(CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT_2,
                        CAMERA_MAX_FPS_2, CAMERA_NAME_2, CAMERA_SENSOR_ORIENTATION_2,
                        LENS_FACING_EXTERNAL), AttributionSource.myAttributionSource());
    }

    @Test
    @DisableFlags(Flags.FLAG_EXTERNAL_VIRTUAL_CAMERAS)
    public void registerCamera_withDefaultCameraPolicy_throwsException_whenNotSupported() {
        mVirtualCameraController.close();
        mVirtualCameraController = new VirtualCameraController(mVirtualCameraServiceMock,
                DEVICE_POLICY_DEFAULT, DEVICE_ID);

        assertThrows(IllegalArgumentException.class, () -> mVirtualCameraController.registerCamera(
                createVirtualCameraConfig(CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1,
                        CAMERA_MAX_FPS_1, CAMERA_NAME_1, CAMERA_SENSOR_ORIENTATION_1,
                        LENS_FACING_EXTERNAL), AttributionSource.myAttributionSource()));
    }

    private VirtualCameraConfig createVirtualCameraConfig(
            int width, int height, int format, int maximumFramesPerSecond,
            String name, int sensorOrientation, int lensFacing) {
@@ -252,7 +283,7 @@ public class VirtualCameraControllerTest {
    }

    @SuppressWarnings("unused") // Parameter for parametrized tests
    private static Integer[] getSingleCamerasLensFacingDirections() {
    private static Integer[] getFixedCamerasLensFacingDirections() {
        return new Integer[]{
                LENS_FACING_BACK,
                LENS_FACING_FRONT,