Loading core/java/android/companion/virtual/VirtualDeviceManager.java +1 −0 Original line number Diff line number Diff line Loading @@ -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)); Loading core/java/android/companion/virtual/camera/VirtualCameraConfig.java +4 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java +15 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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); } } Loading services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java +34 −3 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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(); Loading @@ -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) { Loading @@ -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, Loading Loading
core/java/android/companion/virtual/VirtualDeviceManager.java +1 −0 Original line number Diff line number Diff line Loading @@ -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)); Loading
core/java/android/companion/virtual/camera/VirtualCameraConfig.java +4 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading
services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java +15 −8 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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); } } Loading
services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java +34 −3 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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(); Loading @@ -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) { Loading @@ -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, Loading