diff options
author | 2024-10-28 21:55:35 -0700 | |
---|---|---|
committer | 2024-11-13 15:13:04 -0800 | |
commit | 16adef06d41d0c7475b32166d9902cea23759660 (patch) | |
tree | 41d5e5f5784b0c6b6706d324711e31946b830813 | |
parent | 0df244d34c310967c22caea243354dcb5c5cac7a (diff) |
Add multi-client support in camera2
Add support for multiple clients to access the same camera using
camera2 api. This is initial set of changes and includes all the new
API changes as well as implementation of opening the camera in shared
mode, new open callbacks, newly added characteristics and creating
shared sesion.
After the merge of these initial changes, it will be followed by
another set of changes which will include implementation of
startStreaming and stopStreaming APIs and the capability for multiple
clients to stream the camera images.
Flag: com.android.internal.camera.flags.camera_multi_client
Bug:265196098
API-Coverage-Bug: 377371012
Test: Tested that a java and native client are able to open the camera
at the same time and get the required callbacks and they are able to get
the shared session configuration using newly added characteristics.
Tested the clientaccessprioritieschanged callback occur appropriately
when new client connects/disconnects.
Change-Id: Iee01b79a3a4188fa32ce361f829ac8ea62111076
16 files changed, 1381 insertions, 44 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index fee53d60691b..51ba63e0e2fd 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -5045,15 +5045,27 @@ package android.hardware.biometrics { package android.hardware.camera2 { + public final class CameraCharacteristics extends android.hardware.camera2.CameraMetadata<android.hardware.camera2.CameraCharacteristics.Key<?>> { + field @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.SharedSessionConfiguration> SHARED_SESSION_CONFIGURATION; + } + public abstract class CameraDevice implements java.lang.AutoCloseable { method @Deprecated public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; field public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = 1; // 0x1 field public static final int SESSION_OPERATION_MODE_NORMAL = 0; // 0x0 + field @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public static final int SESSION_OPERATION_MODE_SHARED = 2; // 0x2 field public static final int SESSION_OPERATION_MODE_VENDOR_START = 32768; // 0x8000 } + public abstract static class CameraDevice.StateCallback { + method @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public void onClientSharedAccessPriorityChanged(@NonNull android.hardware.camera2.CameraDevice, boolean); + method @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public void onOpenedInSharedMode(@NonNull android.hardware.camera2.CameraDevice, boolean); + } + public final class CameraManager { + method @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public boolean isCameraDeviceSharingSupported(@NonNull String) throws android.hardware.camera2.CameraAccessException; method @RequiresPermission(allOf={android.Manifest.permission.SYSTEM_CAMERA, android.Manifest.permission.CAMERA}) public void openCamera(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException; + method @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") @RequiresPermission(allOf={android.Manifest.permission.SYSTEM_CAMERA, android.Manifest.permission.CAMERA}) public void openSharedCamera(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException; } public abstract static class CameraManager.AvailabilityCallback { @@ -5061,6 +5073,12 @@ package android.hardware.camera2 { method @RequiresPermission(android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER) public void onCameraOpened(@NonNull String, @NonNull String); } + @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public abstract class CameraSharedCaptureSession extends android.hardware.camera2.CameraCaptureSession { + ctor public CameraSharedCaptureSession(); + method @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public abstract int startStreaming(@NonNull java.util.List<android.view.Surface>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraCaptureSession.CaptureCallback) throws android.hardware.camera2.CameraAccessException; + method @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public abstract void stopStreaming() throws android.hardware.camera2.CameraAccessException; + } + } package android.hardware.camera2.extension { @@ -5169,6 +5187,28 @@ package android.hardware.camera2.params { field public static final int ROTATION_90 = 1; // 0x1 } + public final class SessionConfiguration implements android.os.Parcelable { + field @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public static final int SESSION_SHARED = 2; // 0x2 + } + + @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public final class SharedSessionConfiguration { + method @Nullable public android.graphics.ColorSpace getColorSpace(); + method @NonNull public java.util.List<android.hardware.camera2.params.SharedSessionConfiguration.SharedOutputConfiguration> getOutputStreamsInformation(); + } + + public static final class SharedSessionConfiguration.SharedOutputConfiguration { + method public int getDataspace(); + method public int getFormat(); + method public int getMirrorMode(); + method @Nullable public String getPhysicalCameraId(); + method @NonNull public android.util.Size getSize(); + method public long getStreamUseCase(); + method public int getSurfaceType(); + method public int getTimestampBase(); + method public long getUsage(); + method public boolean isReadoutTimestampEnabled(); + } + } package android.hardware.devicestate { diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 58e524e741b3..5533a640b9d8 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -19,9 +19,9 @@ package android.hardware.camera2; import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.camera2.impl.CameraMetadataNative; -import android.hardware.camera2.impl.ExtensionKey; import android.hardware.camera2.impl.PublicKey; import android.hardware.camera2.impl.SyntheticKey; import android.hardware.camera2.params.DeviceStateSensorOrientationMap; @@ -6172,6 +6172,66 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> JPEGR_AVAILABLE_JPEG_R_STALL_DURATIONS_MAXIMUM_RESOLUTION = new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.jpegr.availableJpegRStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class); + /** + * <p>Color space used for shared session configuration for all the output targets + * when camera is opened in shared mode. This should be one of the values specified in + * availableColorSpaceProfilesMap.</p> + * <p><b>Possible values:</b></p> + * <ul> + * <li>{@link #SHARED_SESSION_COLOR_SPACE_UNSPECIFIED UNSPECIFIED}</li> + * <li>{@link #SHARED_SESSION_COLOR_SPACE_SRGB SRGB}</li> + * <li>{@link #SHARED_SESSION_COLOR_SPACE_DISPLAY_P3 DISPLAY_P3}</li> + * <li>{@link #SHARED_SESSION_COLOR_SPACE_BT2020_HLG BT2020_HLG}</li> + * </ul> + * + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @see #SHARED_SESSION_COLOR_SPACE_UNSPECIFIED + * @see #SHARED_SESSION_COLOR_SPACE_SRGB + * @see #SHARED_SESSION_COLOR_SPACE_DISPLAY_P3 + * @see #SHARED_SESSION_COLOR_SPACE_BT2020_HLG + * @hide + */ + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + public static final Key<Integer> SHARED_SESSION_COLOR_SPACE = + new Key<Integer>("android.sharedSession.colorSpace", int.class); + + /** + * <p>List of shared output configurations that this camera device supports when + * camera is opened in shared mode. Array contains following entries for each supported + * shared configuration: + * 1) surface type + * 2) width + * 3) height + * 4) format + * 5) mirrorMode + * 6) useReadoutTimestamp + * 7) timestampBase + * 8) dataspace + * 9) usage + * 10) streamUsecase + * 11) physical camera id len + * 12) physical camera id as UTF-8 null terminated string.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @hide + */ + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + public static final Key<long[]> SHARED_SESSION_OUTPUT_CONFIGURATIONS = + new Key<long[]>("android.sharedSession.outputConfigurations", long[].class); + + /** + * <p>The available stream configurations that this camera device supports for + * shared capture session when camera is opened in shared mode. Android camera framework + * will generate this tag if the camera device can be opened in shared mode.</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @hide + */ + @SystemApi + @NonNull + @SyntheticKey + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + public static final Key<android.hardware.camera2.params.SharedSessionConfiguration> SHARED_SESSION_CONFIGURATION = + new Key<android.hardware.camera2.params.SharedSessionConfiguration>("android.sharedSession.configuration", android.hardware.camera2.params.SharedSessionConfiguration.class); + /** * Mapping from INFO_SESSION_CONFIGURATION_QUERY_VERSION to session characteristics key. diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index fb381d97adc3..852f04793f15 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -446,6 +446,17 @@ public abstract class CameraDevice implements AutoCloseable { public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = 1; // ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE; + /** + * Shared camera operation mode. + * + * @see #CameraSharedCaptureSession + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + public static final int SESSION_OPERATION_MODE_SHARED = + 2; // ICameraDeviceUser.SHARED_MODE; + /** * First vendor-specific operating mode * @@ -461,6 +472,7 @@ public abstract class CameraDevice implements AutoCloseable { @IntDef(prefix = {"SESSION_OPERATION_MODE"}, value = {SESSION_OPERATION_MODE_NORMAL, SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED, + SESSION_OPERATION_MODE_SHARED, SESSION_OPERATION_MODE_VENDOR_START}) public @interface SessionOperatingMode {}; @@ -1240,7 +1252,6 @@ public abstract class CameraDevice implements AutoCloseable { * * </ul> * - * * @param config A session configuration (see {@link SessionConfiguration}). * * @throws IllegalArgumentException In case the session configuration is invalid; or the output @@ -1559,6 +1570,48 @@ public abstract class CameraDevice implements AutoCloseable { public abstract void onOpened(@NonNull CameraDevice camera); // Must implement /** + * The method called when a camera device has finished opening in shared mode, + * where there can be more than one client accessing the same camera. + * + * <p>At this point, the camera device is ready to use, and + * {@link CameraDevice#createCaptureSession} can be called to set up the shared capture + * session.</p> + * + * @param camera the camera device that has become opened + * @param isPrimaryClient true if the client opening the camera is currently the primary + * client. + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + public void onOpenedInSharedMode(@NonNull CameraDevice camera, boolean isPrimaryClient) { + // Default empty implementation + } + + /** + * The method called when client access priorities have changed for a camera device opened + * in shared mode where there can be more than one client accessing the same camera. + * + * If the client priority changed from secondary to primary, then it can now + * create capture request and change the capture request parameters. If client priority + * changed from primary to secondary, that implies that a higher priority client has also + * opened the camera in shared mode and the new client is now a primary client + * + * @param camera the camera device whose access priorities have changed. + * @param isPrimaryClient true if the client is now the primary client. + * false if another higher priority client also opened the + * camera and is now the new primary client and this client is + * now a secondary client. + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + public void onClientSharedAccessPriorityChanged(@NonNull CameraDevice camera, + boolean isPrimaryClient) { + // Default empty implementation + } + + /** * The method called when a camera device has been closed with * {@link CameraDevice#close}. * diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 75e20582b7b4..266efb7b759c 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -976,6 +976,46 @@ public final class CameraManager { } /** + * Checks if a camera device can be opened in a shared mode for a given {@code cameraId}. + * If this method returns false for a {@code cameraId}, calling {@link #openSharedCamera} + * for that {@code cameraId} will throw an {@link UnsupportedOperationException}. + * + * @param cameraId The unique identifier of the camera device for which sharing support is + * being queried. This identifier must be present in + * {@link #getCameraIdList()}. + * + * @return {@code true} if camera can be opened in shared mode + * for the provided {@code cameraId}; {@code false} otherwise. + * + * @throws IllegalArgumentException If {@code cameraId} is null, or if {@code cameraId} does not + * match any device in {@link #getCameraIdList()}. + * @throws CameraAccessException if the camera device has been disconnected. + * + * @see #getCameraIdList() + * @hide + */ + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + @SystemApi + public boolean isCameraDeviceSharingSupported(@NonNull String cameraId) + throws CameraAccessException { + if (cameraId == null) { + throw new IllegalArgumentException("Camera ID was null"); + } + + if (CameraManagerGlobal.sCameraServiceDisabled + || !Arrays.asList(CameraManagerGlobal.get().getCameraIdList(mContext.getDeviceId(), + getDevicePolicyFromContext(mContext))).contains(cameraId)) { + throw new IllegalArgumentException( + "Camera ID '" + cameraId + "' not available on device."); + } + + CameraCharacteristics chars = getCameraCharacteristics(cameraId); + long[] sharedOutputConfiguration = + chars.get(CameraCharacteristics.SHARED_SESSION_OUTPUT_CONFIGURATIONS); + return (sharedOutputConfiguration != null); + } + + /** * Retrieves the AttributionSourceState to pass to the CameraService. * * @param deviceIdOverride An override of the AttributionSource's deviceId, if not equal to @@ -1036,6 +1076,9 @@ public final class CameraManager { * @param cameraId The unique identifier of the camera device to open * @param callback The callback for the camera. Must not be null. * @param executor The executor to invoke the callback with. Must not be null. + * @param oomScoreOffset The minimum oom score that cameraservice must see for this client. + * @param rotationOverride The type of rotation override. + * @param sharedMode Parameter specifying if the camera should be opened in shared mode. * * @throws CameraAccessException if the camera is disabled by device policy, * too many camera devices are already open, or the cameraId does not match @@ -1051,7 +1094,8 @@ public final class CameraManager { */ private CameraDevice openCameraDeviceUserAsync(String cameraId, CameraDevice.StateCallback callback, Executor executor, - final int oomScoreOffset, int rotationOverride) throws CameraAccessException { + final int oomScoreOffset, int rotationOverride, boolean sharedMode) + throws CameraAccessException { CameraCharacteristics characteristics = getCameraCharacteristics(cameraId); CameraDevice device = null; synchronized (mLock) { @@ -1070,7 +1114,7 @@ public final class CameraManager { characteristics, this, mContext.getApplicationInfo().targetSdkVersion, - mContext, cameraDeviceSetup); + mContext, cameraDeviceSetup, sharedMode); ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks(); try { @@ -1091,7 +1135,7 @@ public final class CameraManager { mContext.getApplicationInfo().targetSdkVersion, rotationOverride, clientAttribution, - getDevicePolicyFromContext(mContext)); + getDevicePolicyFromContext(mContext), sharedMode); } catch (ServiceSpecificException e) { if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) { throw new AssertionError("Should've gone down the shim path"); @@ -1218,7 +1262,8 @@ public final class CameraManager { @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler) throws CameraAccessException { - openCameraImpl(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler)); + openCameraImpl(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler), + /*oomScoreOffset*/0, getRotationOverride(mContext), /*sharedMode*/false); } /** @@ -1258,7 +1303,7 @@ public final class CameraManager { /*oomScoreOffset*/0, overrideToPortrait ? ICameraService.ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT - : ICameraService.ROTATION_OVERRIDE_NONE); + : ICameraService.ROTATION_OVERRIDE_NONE, /*sharedMode*/false); } /** @@ -1303,9 +1348,56 @@ public final class CameraManager { if (executor == null) { throw new IllegalArgumentException("executor was null"); } - openCameraImpl(cameraId, callback, executor); + openCameraImpl(cameraId, callback, executor, /*oomScoreOffset*/0, + getRotationOverride(mContext), /*sharedMode*/false); + } + + /** + * Opens a shared connection to a camera with the given ID. + * + * <p>The behavior of this method matches that of + * {@link #openCamera(String, Executor, StateCallback)}, except that it opens the camera in + * shared mode where more than one client can access the camera at the same time.</p> + * + * @param cameraId The unique identifier of the camera device to open. + * @param executor The executor which will be used when invoking the callback. + * @param callback The callback which is invoked once the camera is opened + * + * @throws CameraAccessException if the camera is disabled by device policy, or is being used + * by a higher-priority client in non-shared mode or the device + * has reached its maximal resource and cannot open this camera + * device. + * + * @throws IllegalArgumentException if cameraId, the callback or the executor was null, + * or the cameraId does not match any currently or previously + * available camera device. + * + * @throws SecurityException if the application does not have permission to + * access the camera + * + * @see #getCameraIdList + * @see android.app.admin.DevicePolicyManager#setCameraDisabled + * + * @hide + */ + @SystemApi + @RequiresPermission(allOf = { + android.Manifest.permission.SYSTEM_CAMERA, + android.Manifest.permission.CAMERA, + }) + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + public void openSharedCamera(@NonNull String cameraId, + @NonNull @CallbackExecutor Executor executor, + @NonNull final CameraDevice.StateCallback callback) + throws CameraAccessException { + if (executor == null) { + throw new IllegalArgumentException("executor was null"); + } + openCameraImpl(cameraId, callback, executor, /*oomScoreOffset*/0, + getRotationOverride(mContext), /*sharedMode*/true); } + /** * Open a connection to a camera with the given ID. Also specify what oom score must be offset * by cameraserver for this client. This api can be useful for system @@ -1372,29 +1464,35 @@ public final class CameraManager { "oomScoreOffset < 0, cannot increase priority of camera client"); } openCameraImpl(cameraId, callback, executor, oomScoreOffset, - getRotationOverride(mContext)); + getRotationOverride(mContext), /*sharedMode*/false); } /** * Open a connection to a camera with the given ID, on behalf of another application. - * Also specify the minimum oom score and process state the application - * should have, as seen by the cameraserver. - * - * <p>The behavior of this method matches that of {@link #openCamera}, except that it allows - * the caller to specify the UID to use for permission/etc verification. This can only be - * done by services trusted by the camera subsystem to act on behalf of applications and - * to forward the real UID.</p> * + * @param cameraId + * The unique identifier of the camera device to open + * @param callback + * The callback which is invoked once the camera is opened + * @param executor + * The executor which will be used when invoking the callback. * @param oomScoreOffset * The minimum oom score that cameraservice must see for this client. * @param rotationOverride * The type of rotation override (none, override_to_portrait, rotation_only) * that should be followed for this camera id connection + * @param sharedMode + * Parameter specifying if the camera should be opened in shared mode. + * + * @throws CameraAccessException if the camera is disabled by device policy, + * has been disconnected, or is being used by a higher-priority camera API client in + * non shared mode. + * * @hide */ public void openCameraImpl(@NonNull String cameraId, @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor, - int oomScoreOffset, int rotationOverride) + int oomScoreOffset, int rotationOverride, boolean sharedMode) throws CameraAccessException { if (cameraId == null) { @@ -1407,24 +1505,7 @@ public final class CameraManager { } openCameraDeviceUserAsync(cameraId, callback, executor, oomScoreOffset, - rotationOverride); - } - - /** - * Open a connection to a camera with the given ID, on behalf of another application. - * - * <p>The behavior of this method matches that of {@link #openCamera}, except that it allows - * the caller to specify the UID to use for permission/etc verification. This can only be - * done by services trusted by the camera subsystem to act on behalf of applications and - * to forward the real UID.</p> - * - * @hide - */ - public void openCameraImpl(@NonNull String cameraId, - @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor) - throws CameraAccessException { - openCameraImpl(cameraId, callback, executor, /*oomScoreOffset*/0, - getRotationOverride(mContext)); + rotationOverride, sharedMode); } /** @@ -2541,6 +2622,10 @@ public final class CameraManager { } @Override public void onCameraClosed(String id, int deviceId) { + } + @Override + public void onCameraOpenedInSharedMode(String id, String clientPackageId, + int deviceId, boolean primaryClient) { }}; String[] cameraIds; @@ -3325,6 +3410,11 @@ public final class CameraManager { } @Override + public void onCameraOpenedInSharedMode(String cameraId, String clientPackageId, + int deviceId, boolean primaryClient) { + } + + @Override public void onCameraOpened(String cameraId, String clientPackageId, int deviceId) { synchronized (mLock) { onCameraOpenedLocked(new DeviceCameraInfo(cameraId, deviceId), clientPackageId); diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 8d36fbdc8b10..d2fcfd62bfca 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -2121,6 +2121,38 @@ public abstract class CameraMetadata<TKey> { public static final int AUTOMOTIVE_LOCATION_EXTRA_RIGHT = 10; // + // Enumeration values for CameraCharacteristics#SHARED_SESSION_COLOR_SPACE + // + + /** + * @see CameraCharacteristics#SHARED_SESSION_COLOR_SPACE + * @hide + */ + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + public static final int SHARED_SESSION_COLOR_SPACE_UNSPECIFIED = -1; + + /** + * @see CameraCharacteristics#SHARED_SESSION_COLOR_SPACE + * @hide + */ + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + public static final int SHARED_SESSION_COLOR_SPACE_SRGB = 0; + + /** + * @see CameraCharacteristics#SHARED_SESSION_COLOR_SPACE + * @hide + */ + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + public static final int SHARED_SESSION_COLOR_SPACE_DISPLAY_P3 = 7; + + /** + * @see CameraCharacteristics#SHARED_SESSION_COLOR_SPACE + * @hide + */ + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + public static final int SHARED_SESSION_COLOR_SPACE_BT2020_HLG = 16; + + // // Enumeration values for CaptureRequest#COLOR_CORRECTION_MODE // diff --git a/core/java/android/hardware/camera2/CameraSharedCaptureSession.java b/core/java/android/hardware/camera2/CameraSharedCaptureSession.java new file mode 100644 index 000000000000..5426d4d10bec --- /dev/null +++ b/core/java/android/hardware/camera2/CameraSharedCaptureSession.java @@ -0,0 +1,180 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera2; + +import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.view.Surface; + +import com.android.internal.camera.flags.Flags; + +import java.util.List; +import java.util.concurrent.Executor; + +/** + * A shared capture session for a {@link CameraDevice}, when a camera device is opened in shared + * mode possibly by multiple clients at the same time. + * + * <p>An active shared capture session is a special type of capture session used exclusively + * for shared camera access by multiple applications, provided the camera device supports this + * mode. To determine if a camera device supports shared mode, use the + * {@link android.hardware.camera2.CameraManager#isCameraDeviceSharingSupported} API. + * If supported, multiple clients can open the camera by calling + * {@link android.hardware.camera2.CameraManager#openSharedCamera} and create a shared capture + * session by calling {@link CameraDevice#createCaptureSession(SessionConfiguration)} and using + * session type as {@link android.hardware.camera2.params.SessionConfiguration#SESSION_SHARED}</p> + * + * <p>When an application has opened a camera device in shared mode, it can only create a shared + * capture session using session type as + * {@link android.hardware.camera2.params.SessionConfiguration#SESSION_SHARED}. Any other session + * type value will trigger {@link IllegalArgumentException}. Once the configuration is complete and + * the session is ready to actually capture data, the provided + * {@link CameraCaptureSession.StateCallback}'s + * {@link CameraCaptureSession.StateCallback#onConfigured} callback will be called and will + * receive a CameraCaptureSession (castable to {@link CameraSharedCaptureSession}).</p> + * + * <p>Shared capture sessions uses a predefined configuration detailed in + * {@link CameraCharacteristics#SHARED_SESSION_CONFIGURATION}. Using different configuration values + * when creating session will result in an {@link IllegalArgumentException}.</p> + * + * <p>When camera is opened in shared mode, the highest priority client among all the clients will + * be the primary client while the others would be secondary clients. Clients will know if they are + * primary or secondary by the device state callback + * {@link CameraDevice.StateCallback#onOpenedInSharedMode}. Once the camera has been opened in + * shared mode, their access priorities of being a primary or secondary client can change if + * another higher priority client opens the camera later. Once the camera has been opened, + * any change in primary client status will be shared by the device state callback + * {@link CameraDevice.StateCallback#onClientSharedAccessPriorityChanged}.</p> + * + * <p>The priority of client access is determined by considering two factors: its current process + * state and its "out of memory" score. Clients operating in the background are assigned a lower + * priority. In contrast, clients running in the foreground, along with system-level clients, are + * given a higher priority.</p> + * + * <p>Primary clients can create capture requests, modify any capture parameters and send them to + * the capture session for a one-shot capture or as a repeating request using the following apis: + * </p> + * + * <ul> + * + * <li>{@link CameraSharedCaptureSession#capture}</li> + * + * <li>{@link CameraSharedCaptureSession#captureSingleRequest}</li> + * + * <li>{@link CameraSharedCaptureSession#setRepeatingRequest}</li> + * + * <li>{@link CameraSharedCaptureSession#setSingleRepeatingRequest}</li> + * + * <li>{@link CameraSharedCaptureSession#stopRepeating}</li> + * + * </ul> + * + * <p>Secondary clients cannot create a capture request and modify any capture parameters. However, + * they can start the camera streaming to desired surface targets using + * {@link CameraSharedCaptureSession#startStreaming}, which will apply default parameters. Once the + * streaming has successfully started, then they can stop the streaming using + * {@link CameraSharedCaptureSession#stopStreaming}.</p> + * + * <p>The following APIs are not supported in shared capture sessions by either the primary or + * secondary client.</p> + * + * <ul> + * + * <li>{@link CameraSharedCaptureSession#captureBurst}</li> + * + * <li>{@link CameraSharedCaptureSession#captureBurstRequests}</li> + * + * <li>{@link CameraSharedCaptureSession#setRepeatingBurst}</li> + * + * <li>{@link CameraSharedCaptureSession#setRepeatingBurstRequests}</li> + * + * <li>{@link CameraSharedCaptureSession#switchToOffline}</li> + * + * <li>{@link CameraSharedCaptureSession#updateOutputConfiguration}</li> + * + * <li>{@link CameraSharedCaptureSession#finalizeOutputConfigurations}</li> + * + * <li>{@link CameraSharedCaptureSession#prepare}</li> + * + * </ul> + * + * @hide + */ +@FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) +@SystemApi +public abstract class CameraSharedCaptureSession extends CameraCaptureSession { + + /** + * Request start of the streaming of camera images by this shared capture session. + * + * <p>With this method, the camera device will continually capture images + * using the settings provided by primary client if there is ongoing repeating request + * by the primary client or default settings if no ongoing streaming request in progress.</p> + * + * <p> startStreaming has lower priority than the capture requests submitted + * through {@link #capture} by primary client, so if {@link #capture} is called when a + * streaming is active, the capture request will be processed before any further + * streaming requests are processed.</p> + * + * <p>To stop the streaming, call {@link #stopStreaming}</p> + * + * <p>Calling this method will replace any earlier streaming set up by this method.</p> + * + * @param surfaces List of target surfaces to use for streaming. + * @param executor The executor which will be used for invoking the listener. + * @param listener The callback object to notify the status and progress of the image capture. + * + * @return int A unique capture sequence ID used by + * {@link CaptureCallback#onCaptureSequenceCompleted}. + * + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error + * @throws IllegalStateException if this session is no longer active, either because the session + * was explicitly closed, a new session has been created + * or the camera device has been closed. + * @throws IllegalArgumentException If the request references no surfaces or references surfaces + * that are not currently configured as outputs; or + * the executor is null, or the listener is null. + * @see #stopStreaming + * + * @hide + */ + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + @SystemApi + public abstract int startStreaming(@NonNull List<Surface> surfaces, + @NonNull @CallbackExecutor Executor executor, @NonNull CaptureCallback listener) + throws CameraAccessException; + + /** + * <p>Cancel any ongoing streaming started by {@link #startStreaming}</p> + * + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error + * @throws IllegalStateException if this session is no longer active, either because the session + * was explicitly closed, a new session has been created + * or the camera device has been closed. + * + * @see #startStreaming + * + * @hide + */ + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + @SystemApi + public abstract void stopStreaming() throws CameraAccessException; +} diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 84072585d7f0..ea70abb55b48 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -18,6 +18,7 @@ package android.hardware.camera2.impl; import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.compat.CompatChanges; @@ -41,12 +42,15 @@ import android.hardware.camera2.ICameraDeviceCallbacks; import android.hardware.camera2.ICameraDeviceUser; import android.hardware.camera2.ICameraOfflineSession; import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.DynamicRangeProfiles; import android.hardware.camera2.params.ExtensionSessionConfiguration; import android.hardware.camera2.params.InputConfiguration; import android.hardware.camera2.params.MultiResolutionStreamConfigurationMap; import android.hardware.camera2.params.MultiResolutionStreamInfo; import android.hardware.camera2.params.OutputConfiguration; import android.hardware.camera2.params.SessionConfiguration; +import android.hardware.camera2.params.SharedSessionConfiguration; +import android.hardware.camera2.params.SharedSessionConfiguration.SharedOutputConfiguration; import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.utils.SubmitInfo; import android.hardware.camera2.utils.SurfaceUtils; @@ -188,6 +192,8 @@ public class CameraDeviceImpl extends CameraDevice private ExecutorService mOfflineSwitchService; private CameraOfflineSessionImpl mOfflineSessionImpl; + private boolean mSharedMode; + private boolean mIsPrimaryClient; // Runnables for all state transitions, except error, which needs the // error code argument @@ -208,6 +214,25 @@ public class CameraDeviceImpl extends CameraDevice } }; + private final Runnable mCallOnOpenedInSharedMode = new Runnable() { + @Override + public void run() { + if (!Flags.cameraMultiClient()) { + return; + } + StateCallbackKK sessionCallback = null; + synchronized (mInterfaceLock) { + if (mRemoteDevice == null) return; // Camera already closed + + sessionCallback = mSessionStateCallback; + } + if (sessionCallback != null) { + sessionCallback.onOpenedInSharedMode(CameraDeviceImpl.this, mIsPrimaryClient); + } + mDeviceCallback.onOpenedInSharedMode(CameraDeviceImpl.this, mIsPrimaryClient); + } + }; + private final Runnable mCallOnUnconfigured = new Runnable() { @Override public void run() { @@ -322,6 +347,32 @@ public class CameraDeviceImpl extends CameraDevice } }); } + + public void onOpenedInSharedMode(@NonNull CameraDevice camera, boolean primaryClient) { + if (!Flags.cameraMultiClient()) { + return; + } + mClientExecutor.execute(new Runnable() { + @Override + public void run() { + mClientStateCallback.onOpenedInSharedMode(camera, primaryClient); + } + }); + } + + public void onClientSharedAccessPriorityChanged(@NonNull CameraDevice camera, + boolean primaryClient) { + if (!Flags.cameraMultiClient()) { + return; + } + mClientExecutor.execute(new Runnable() { + @Override + public void run() { + mClientStateCallback.onClientSharedAccessPriorityChanged(camera, primaryClient); + } + }); + } + @Override public void onOpened(@NonNull CameraDevice camera) { mClientExecutor.execute(new Runnable() { @@ -358,7 +409,8 @@ public class CameraDeviceImpl extends CameraDevice @NonNull CameraManager manager, int appTargetSdkVersion, Context ctx, - @Nullable CameraDevice.CameraDeviceSetup cameraDeviceSetup) { + @Nullable CameraDevice.CameraDeviceSetup cameraDeviceSetup, + boolean sharedMode) { if (cameraId == null || callback == null || executor == null || characteristics == null || manager == null) { throw new IllegalArgumentException("Null argument given"); @@ -375,6 +427,7 @@ public class CameraDeviceImpl extends CameraDevice mAppTargetSdkVersion = appTargetSdkVersion; mContext = ctx; mCameraDeviceSetup = cameraDeviceSetup; + mSharedMode = sharedMode; final int MAX_TAG_LEN = 23; String tag = String.format("CameraDevice-JV-%s", mCameraId); @@ -438,7 +491,12 @@ public class CameraDeviceImpl extends CameraDevice } } - mDeviceExecutor.execute(mCallOnOpened); + if (Flags.cameraMultiClient() && mSharedMode) { + mIsPrimaryClient = mRemoteDevice.isPrimaryClient(); + mDeviceExecutor.execute(mCallOnOpenedInSharedMode); + } else { + mDeviceExecutor.execute(mCallOnOpened); + } mDeviceExecutor.execute(mCallOnUnconfigured); mRemoteDeviceInit = true; @@ -576,7 +634,11 @@ public class CameraDeviceImpl extends CameraDevice stopRepeating(); try { - waitUntilIdle(); + // if device is opened in shared mode, there can be multiple clients accessing the + // camera device. So do not wait for idle if the device is opened in shared mode. + if (!mSharedMode) { + waitUntilIdle(); + } mRemoteDevice.beginConfigure(); @@ -764,6 +826,54 @@ public class CameraDeviceImpl extends CameraDevice checkAndWrapHandler(handler), operatingMode, /*sessionParams*/ null); } + private boolean checkSharedOutputConfiguration(OutputConfiguration outConfig) { + if (!Flags.cameraMultiClient()) { + return false; + } + SharedSessionConfiguration sharedSessionConfiguration = + mCharacteristics.get(CameraCharacteristics.SHARED_SESSION_CONFIGURATION); + if (sharedSessionConfiguration == null) { + return false; + } + + List<SharedOutputConfiguration> sharedConfigs = + sharedSessionConfiguration.getOutputStreamsInformation(); + for (SharedOutputConfiguration sharedConfig : sharedConfigs) { + if (outConfig.getConfiguredSize().equals(sharedConfig.getSize()) + && (outConfig.getConfiguredFormat() == sharedConfig.getFormat()) + && (outConfig.getSurfaceGroupId() == OutputConfiguration.SURFACE_GROUP_ID_NONE) + && (outConfig.getSurfaceType() == sharedConfig.getSurfaceType()) + && (outConfig.getMirrorMode() == sharedConfig.getMirrorMode()) + && (outConfig.getUsage() == sharedConfig.getUsage()) + && (outConfig.isReadoutTimestampEnabled() + == sharedConfig.isReadoutTimestampEnabled()) + && (outConfig.getTimestampBase() == sharedConfig.getTimestampBase()) + && (outConfig.getStreamUseCase() == sharedConfig.getStreamUseCase()) + && (outConfig.getColorSpace().equals( + sharedSessionConfiguration.getColorSpace())) + && (outConfig.getDynamicRangeProfile() + == DynamicRangeProfiles.STANDARD) + && (outConfig.getConfiguredDataspace() == sharedConfig.getDataspace()) + && (Objects.equals(outConfig.getPhysicalCameraId(), + sharedConfig.getPhysicalCameraId())) + && (outConfig.getSensorPixelModes().isEmpty()) + && (!outConfig.isShared())) { + //Found valid config, return true + return true; + } + } + return false; + } + + private boolean checkSharedSessionConfiguration(List<OutputConfiguration> outputConfigs) { + for (OutputConfiguration out : outputConfigs) { + if (!checkSharedOutputConfiguration(out)) { + return false; + } + } + return true; + } + @Override public void createCaptureSession(SessionConfiguration config) throws CameraAccessException { @@ -778,6 +888,14 @@ public class CameraDeviceImpl extends CameraDevice if (config.getExecutor() == null) { throw new IllegalArgumentException("Invalid executor"); } + if (mSharedMode) { + if (config.getSessionType() != SessionConfiguration.SESSION_SHARED) { + throw new IllegalArgumentException("Invalid session type"); + } + if (!checkSharedSessionConfiguration(outputConfigs)) { + throw new IllegalArgumentException("Invalid output configurations"); + } + } createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs, config.getStateCallback(), config.getExecutor(), config.getSessionType(), config.getSessionParameters()); @@ -801,6 +919,11 @@ public class CameraDeviceImpl extends CameraDevice throw new IllegalArgumentException("Constrained high speed session doesn't support" + " input configuration yet."); } + boolean isSharedSession = (operatingMode == ICameraDeviceUser.SHARED_MODE); + if (isSharedSession && inputConfig != null) { + throw new IllegalArgumentException("Shared capture session doesn't support" + + " input configuration yet."); + } if (mCurrentExtensionSession != null) { mCurrentExtensionSession.commitStats(); @@ -860,6 +983,10 @@ public class CameraDeviceImpl extends CameraDevice newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++, callback, executor, this, mDeviceExecutor, configureSuccess, mCharacteristics); + } else if (isSharedSession) { + newSession = new CameraSharedCaptureSessionImpl(mNextSessionId++, + callback, executor, this, mDeviceExecutor, configureSuccess, + mIsPrimaryClient); } else { newSession = new CameraCaptureSessionImpl(mNextSessionId++, input, callback, executor, this, mDeviceExecutor, configureSuccess); @@ -1882,6 +2009,40 @@ public class CameraDeviceImpl extends CameraDevice } } + /** + * Callback when client access priorities change when camera is opened in shared mode. + */ + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + public void onClientSharedAccessPriorityChanged(boolean primaryClient) { + if (DEBUG) { + Log.d(TAG, String.format( + "onClientSharedAccessPriorityChanged received, primary client = " + + primaryClient)); + } + synchronized (mInterfaceLock) { + if (mRemoteDevice == null && mRemoteDeviceInit) { + return; // Camera already closed, user is not interested in this callback anymore. + } + final long ident = Binder.clearCallingIdentity(); + try { + mDeviceExecutor.execute(obtainRunnable( + CameraDeviceImpl::notifyClientSharedAccessPriorityChanged, this, + primaryClient).recycleOnUse()); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + private void notifyClientSharedAccessPriorityChanged(boolean primaryClient) { + if (!CameraDeviceImpl.this.isClosed()) { + mIsPrimaryClient = primaryClient; + mDeviceCallback.onClientSharedAccessPriorityChanged(CameraDeviceImpl.this, + primaryClient); + } + } + public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) { if (DEBUG) { Log.d(TAG, String.format( @@ -2447,6 +2608,12 @@ public class CameraDeviceImpl extends CameraDevice } @Override + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + public void onClientSharedAccessPriorityChanged(boolean primaryClient) { + CameraDeviceImpl.this.onClientSharedAccessPriorityChanged(primaryClient); + } + + @Override public void onPrepared(int streamId) { final OutputConfiguration output; final StateCallbackKK sessionCallback; diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 1cc085658bfa..c0a5928a369b 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -63,6 +63,7 @@ import android.hardware.camera2.params.OisSample; import android.hardware.camera2.params.RecommendedStreamConfiguration; import android.hardware.camera2.params.RecommendedStreamConfigurationMap; import android.hardware.camera2.params.ReprocessFormatsMap; +import android.hardware.camera2.params.SharedSessionConfiguration; import android.hardware.camera2.params.StreamConfiguration; import android.hardware.camera2.params.StreamConfigurationDuration; import android.hardware.camera2.params.StreamConfigurationMap; @@ -866,6 +867,15 @@ public class CameraMetadataNative implements Parcelable { return (T) metadata.getLensIntrinsicSamples(); } }); + sGetCommandMap.put( + CameraCharacteristics.SHARED_SESSION_CONFIGURATION.getNativeKey(), + new GetCommand() { + @Override + @SuppressWarnings("unchecked") + public <T> T getValue(CameraMetadataNative metadata, Key<T> key) { + return (T) metadata.getSharedSessionConfiguration(); + } + }); } private int[] getAvailableFormats() { @@ -1658,6 +1668,22 @@ public class CameraMetadataNative implements Parcelable { listHighResolution); } + private SharedSessionConfiguration getSharedSessionConfiguration() { + if (!Flags.cameraMultiClient()) { + return null; + } + Integer sharedSessionColorSpace = getBase( + CameraCharacteristics.SHARED_SESSION_COLOR_SPACE); + long[] sharedOutputConfigurations = getBase( + CameraCharacteristics.SHARED_SESSION_OUTPUT_CONFIGURATIONS); + + if ((sharedSessionColorSpace == null) || (sharedOutputConfigurations == null)) { + return null; + } + + return new SharedSessionConfiguration(sharedSessionColorSpace, sharedOutputConfigurations); + } + private StreamConfigurationMap getStreamConfigurationMapMaximumResolution() { StreamConfiguration[] configurations = getBase( CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION); diff --git a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java index eb2ff88ec1b2..1769c4638805 100644 --- a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java @@ -16,11 +16,13 @@ package android.hardware.camera2.impl; +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.annotation.FlaggedApi; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; -import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraOfflineSession; import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback; import android.hardware.camera2.CaptureFailure; @@ -40,15 +42,15 @@ import android.util.Range; import android.util.SparseArray; import android.view.Surface; +import com.android.internal.camera.flags.Flags; + import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.Executor; - -import static com.android.internal.util.Preconditions.*; +import java.util.concurrent.atomic.AtomicBoolean; public class CameraOfflineSessionImpl extends CameraOfflineSession implements IBinder.DeathRecipient { @@ -176,6 +178,12 @@ public class CameraOfflineSessionImpl extends CameraOfflineSession } @Override + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + public void onClientSharedAccessPriorityChanged(boolean primaryClient) { + Log.v(TAG, "onClientSharedAccessPriorityChanged primaryClient = " + primaryClient); + } + + @Override public void onDeviceIdle() { synchronized(mInterfaceLock) { if (mRemoteSession == null) { diff --git a/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java new file mode 100644 index 000000000000..a1f31c0ced5e --- /dev/null +++ b/core/java/android/hardware/camera2/impl/CameraSharedCaptureSessionImpl.java @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.camera2.impl; + +import android.annotation.FlaggedApi; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraSharedCaptureSession; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.OutputConfiguration; +import android.os.ConditionVariable; +import android.os.Handler; +import android.view.Surface; + +import com.android.internal.camera.flags.Flags; + +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Standard implementation of CameraSharedCaptureSession. + * + * <p> + * Mostly just forwards calls to an instance of CameraCaptureSessionImpl, + * but implements the few necessary behavior changes and additional methods required + * for the shared session mode. + * </p> + */ +@FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) +public class CameraSharedCaptureSessionImpl + extends CameraSharedCaptureSession implements CameraCaptureSessionCore { + private static final String TAG = "CameraSharedCaptureSessionImpl"; + private final CameraCaptureSessionImpl mSessionImpl; + private final ConditionVariable mInitialized = new ConditionVariable(); + private boolean mIsPrimary; + + /** + * Create a new CameraCaptureSession. + */ + CameraSharedCaptureSessionImpl(int id, + CameraCaptureSession.StateCallback callback, Executor stateExecutor, + android.hardware.camera2.impl.CameraDeviceImpl deviceImpl, + Executor deviceStateExecutor, boolean configureSuccess, boolean isPrimary) { + CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback); + mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, wrapperCallback, + stateExecutor, deviceImpl, deviceStateExecutor, configureSuccess); + mIsPrimary = isPrimary; + mInitialized.open(); + } + + @Override + public int startStreaming(List<Surface> surfaces, Executor executor, CaptureCallback listener) + throws CameraAccessException { + // Todo: Need to add implementation. + return 0; + } + + @Override + public void stopStreaming() throws CameraAccessException { + // Todo: Need to add implementation. + } + + @Override + public void close() { + mSessionImpl.close(); + } + + @Override + public Surface getInputSurface() { + return null; + } + + @Override + public boolean isReprocessable() { + return false; + } + + @Override + public void abortCaptures() throws CameraAccessException { + if (mIsPrimary) { + mSessionImpl.abortCaptures(); + } + } + + @Override + public int setRepeatingRequest(CaptureRequest request, CaptureCallback listener, + Handler handler) throws CameraAccessException { + if (mIsPrimary) { + return mSessionImpl.setRepeatingRequest(request, listener, handler); + } + throw new UnsupportedOperationException("Shared capture session only supports this method" + + " for primary clients"); + } + + @Override + public void stopRepeating() throws CameraAccessException { + if (mIsPrimary) { + mSessionImpl.stopRepeating(); + } + } + + @Override + public int capture(CaptureRequest request, CaptureCallback listener, Handler handler) + throws CameraAccessException { + if (mIsPrimary) { + return mSessionImpl.capture(request, listener, handler); + } + throw new UnsupportedOperationException("Shared capture session only supports this method" + + " for primary clients"); + } + + @Override + public void tearDown(Surface surface) throws CameraAccessException { + mSessionImpl.tearDown(surface); + } + + @Override + public CameraDevice getDevice() { + return mSessionImpl.getDevice(); + } + + @Override + public boolean isAborting() { + return mSessionImpl.isAborting(); + } + + @Override + public CameraDeviceImpl.StateCallbackKK getDeviceStateCallback() { + return mSessionImpl.getDeviceStateCallback(); + } + + @Override + public void replaceSessionClose() { + mSessionImpl.replaceSessionClose(); + } + + @Override + public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback listener, + Handler handler) throws CameraAccessException { + throw new UnsupportedOperationException("Shared Capture session doesn't support" + + " this method"); + } + + @Override + public int captureBurst(List<CaptureRequest> requests, CaptureCallback listener, + Handler handler) throws CameraAccessException { + throw new UnsupportedOperationException("Shared Capture session doesn't support" + + " this method"); + } + + @Override + public void updateOutputConfiguration(OutputConfiguration config) + throws CameraAccessException { + throw new UnsupportedOperationException("Shared capture session doesn't support" + + " this method"); + } + + @Override + public void finalizeOutputConfigurations(List<OutputConfiguration> deferredOutputConfigs) + throws CameraAccessException { + throw new UnsupportedOperationException("Shared capture session doesn't support" + + " this method"); + } + + @Override + public void prepare(Surface surface) throws CameraAccessException { + throw new UnsupportedOperationException("Shared capture session doesn't support" + + " this method"); + } + + @Override + public void prepare(int maxCount, Surface surface) throws CameraAccessException { + throw new UnsupportedOperationException("Shared capture session doesn't support" + + " this method"); + } + + @Override + public void closeWithoutDraining() { + throw new UnsupportedOperationException("Shared capture session doesn't support" + + " this method"); + } + + private class WrapperCallback extends StateCallback { + private final StateCallback mCallback; + + WrapperCallback(StateCallback callback) { + mCallback = callback; + } + + @Override + public void onConfigured(CameraCaptureSession session) { + mInitialized.block(); + mCallback.onConfigured(CameraSharedCaptureSessionImpl.this); + } + + @Override + public void onConfigureFailed(CameraCaptureSession session) { + mInitialized.block(); + mCallback.onConfigureFailed(CameraSharedCaptureSessionImpl.this); + } + + @Override + public void onReady(CameraCaptureSession session) { + mCallback.onReady(CameraSharedCaptureSessionImpl.this); + } + + @Override + public void onActive(CameraCaptureSession session) { + mCallback.onActive(CameraSharedCaptureSessionImpl.this); + } + + @Override + public void onCaptureQueueEmpty(CameraCaptureSession session) { + mCallback.onCaptureQueueEmpty(CameraSharedCaptureSessionImpl.this); + } + + @Override + public void onClosed(CameraCaptureSession session) { + mCallback.onClosed(CameraSharedCaptureSessionImpl.this); + } + + @Override + public void onSurfacePrepared(CameraCaptureSession session, Surface surface) { + mCallback.onSurfacePrepared(CameraSharedCaptureSessionImpl.this, + surface); + } + } +} diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java index aec2cff61d99..831c75ec5d33 100644 --- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java +++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java @@ -301,4 +301,16 @@ public class ICameraDeviceUserWrapper { } } + /** + * API to check if the client is primary client when camera device is opened in shared mode. + */ + public boolean isPrimaryClient() throws CameraAccessException { + try { + return mRemoteDevice.isPrimaryClient(); + } catch (ServiceSpecificException e) { + throw ExceptionUtils.throwAsPublicException(e); + } catch (RemoteException e) { + throw ExceptionUtils.throwAsPublicException(e); + } + } } diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index d38be9b7b694..e12c46322d8c 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -29,6 +29,7 @@ import android.annotation.TestApi; import android.graphics.ColorSpace; import android.graphics.ImageFormat; import android.graphics.ImageFormat.Format; +import android.hardware.DataSpace.NamedDataSpace; import android.hardware.HardwareBuffer; import android.hardware.HardwareBuffer.Usage; import android.hardware.camera2.CameraCaptureSession; @@ -1729,6 +1730,79 @@ public final class OutputConfiguration implements Parcelable { } /** + * Get the configured format associated with this {@link OutputConfiguration}. + * + * @return {@link android.graphics.ImageFormat#Format} associated with this + * {@link OutputConfiguration}. + * + * @hide + */ + public @Format int getConfiguredFormat() { + return mConfiguredFormat; + } + + /** + * Get the usage flag associated with this {@link OutputConfiguration}. + * + * @return {@link HardwareBuffer#Usage} associated with this {@link OutputConfiguration}. + * + * @hide + */ + public @Usage long getUsage() { + return mUsage; + } + + /** + * Get the surface type associated with this {@link OutputConfiguration}. + * + * @return The surface type associated with this {@link OutputConfiguration}. + * + * @see #SURFACE_TYPE_SURFACE_VIEW + * @see #SURFACE_TYPE_SURFACE_TEXTURE + * @see #SURFACE_TYPE_MEDIA_RECORDER + * @see #SURFACE_TYPE_MEDIA_CODEC + * @see #SURFACE_TYPE_IMAGE_READER + * @see #SURFACE_TYPE_UNKNOWN + * @hide + */ + public int getSurfaceType() { + return mSurfaceType; + } + + /** + * Get the sensor pixel modes associated with this {@link OutputConfiguration}. + * + * @return List of {@link #SensorPixelMode} associated with this {@link OutputConfiguration}. + * + * @hide + */ + public @NonNull List<Integer> getSensorPixelModes() { + return mSensorPixelModesUsed; + } + + /** + * Get the sharing mode associated with this {@link OutputConfiguration}. + * + * @return true if surface sharing is enabled with this {@link OutputConfiguration}. + * + * @hide + */ + public boolean isShared() { + return mIsShared; + } + + /** + * Get the dataspace associated with this {@link OutputConfiguration}. + * + * @return {@link Dataspace#NamedDataSpace} for this {@link OutputConfiguration}. + * + * @hide + */ + public @NamedDataSpace int getConfiguredDataspace() { + return mConfiguredDataspace; + } + + /** * Get the physical camera ID associated with this {@link OutputConfiguration}. * * <p>If this OutputConfiguration isn't targeting a physical camera of a logical diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java index 50c6b5b8b995..82aa64b1474c 100644 --- a/core/java/android/hardware/camera2/params/SessionConfiguration.java +++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java @@ -23,6 +23,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.graphics.ColorSpace; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; @@ -78,6 +79,19 @@ public final class SessionConfiguration implements Parcelable { CameraDevice.SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED; /** + * A shared session type containing instances of {@link OutputConfiguration} from a set of + * predefined stream configurations. A shared session can be shared among multiple clients. + * Shared session does not have any {@link InputConfiguration} as it does not support + * reprocessable sessions. + * + * @see CameraDevice#createCaptureSession(SessionConfiguration) + * @hide + */ + @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) + @SystemApi + public static final int SESSION_SHARED = CameraDevice.SESSION_OPERATION_MODE_SHARED; + + /** * First vendor-specific session mode * @hide */ diff --git a/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java b/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java new file mode 100644 index 000000000000..cdcc92ce4404 --- /dev/null +++ b/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera2.params; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.graphics.ColorSpace; +import android.graphics.ImageFormat.Format; +import android.hardware.DataSpace.NamedDataSpace; +import android.hardware.HardwareBuffer.Usage; +import android.hardware.camera2.params.OutputConfiguration.MirrorMode; +import android.hardware.camera2.params.OutputConfiguration.StreamUseCase; +import android.hardware.camera2.params.OutputConfiguration.TimestampBase; +import android.util.Log; +import android.util.Size; + +import com.android.internal.camera.flags.Flags; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Immutable class to store the shared session configuration + * {@link CameraCharacteristics#SHARED_SESSION_CONFIGURATION} to set up + * {@link android.view.Surface Surfaces} for creating a + * {@link android.hardware.camera2.CameraSharedCaptureSession capture session} using + * {@link android.hardware.camera2.CameraDevice#createCaptureSession(SessionConfiguration)} and + * {@link android.hardware.camera2.params.SessionConfiguration#SESSION_SHARED + * shared capture session} when camera has been opened in shared mode using + * {@link #openSharedCamera(String, Executor, StateCallback)}. + * + * <p>This is the authoritative list for all output configurations that are supported by a camera + * device when opened in shared mode.</p> + * + * <p>An instance of this object is available from {@link CameraCharacteristics} using + * the {@link CameraCharacteristics#SHARED_SESSION_CONFIGURATION} key and the + * {@link CameraCharacteristics#get} method.</p> + * + * <pre><code>{@code + * CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId); + * StreamConfigurationMap configs = characteristics.get( + * CameraCharacteristics.SHARED_SESSION_CONFIGURATION); + * }</code></pre> + * + * @see CameraCharacteristics#SHARED_SESSION_CONFIGURATION + * @see CameraDevice#createCaptureSession(SessionConfiguration) + * @see SessionConfiguration#SESSION_SHARED + * @see CameraManager#openSharedCamera(String, Executor, StateCallback) + * + * @hide + */ +@SystemApi +@FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) +public final class SharedSessionConfiguration { + private static final String TAG = "SharedSessionConfiguration"; + // Metadata android.info.availableSharedOutputConfigurations has list of shared output + // configurations. Each output configuration has minimum of 11 entries of size long + // followed by the physical camera id if present. + // See android.info.availableSharedOutputConfigurations for details. + private static final int SHARED_OUTPUT_CONFIG_NUM_OF_ENTRIES = 11; + /** + * Immutable class to store shared output stream information. + */ + public static final class SharedOutputConfiguration { + private final int mSurfaceType; + private final Size mSize; + private final int mFormat; + private final int mDataspace; + private final long mStreamUseCase; + private String mPhysicalCameraId; + private final long mUsage; + private int mTimestampBase; + private int mMirrorMode; + private boolean mReadoutTimestampEnabled; + + /** + * Create a new {@link SharedOutputConfiguration}. + * + * @param surfaceType Surface Type for this output configuration. + * @param sz Size for this output configuration. + * @param format {@link android.graphics.ImageFormat#Format} associated with this + * {@link OutputConfiguration}. + * @param mirrorMode {@link OutputConfiguration#MirrorMode} for this output configuration. + * @param readoutTimeStampEnabled Flag indicating whether readout timestamp is enabled + * for this output configuration. + * @param timestampBase {@link OutputConfiguration#TimestampBase} for this output + * configuration. + * @param dataspace {@link Dataspace#NamedDataSpace} for this output configuration. + * @param usage {@link HardwareBuffer#Usage} for this output configuration. + * @param streamUseCase {@link OutputConfiguration#StreamUseCase} for this output + * configuration. + * @param physicalCamId Physical Camera Id for this output configuration. + * + * @hide + */ + public SharedOutputConfiguration(int surfaceType, @NonNull Size sz, @Format int format, + @MirrorMode int mirrorMode, boolean readoutTimeStampEnabled, + @TimestampBase int timestampBase, @NamedDataSpace int dataspace, @Usage long usage, + @StreamUseCase long streamUseCase, @Nullable String physicalCamId) { + mSurfaceType = surfaceType; + mSize = sz; + mFormat = format; + mMirrorMode = mirrorMode; + mReadoutTimestampEnabled = readoutTimeStampEnabled; + mTimestampBase = timestampBase; + mDataspace = dataspace; + mUsage = usage; + mStreamUseCase = streamUseCase; + mPhysicalCameraId = physicalCamId; + } + + /** + * Returns the surface type configured for the shared output configuration. + * @return SURFACE_TYPE_UNKNOWN = -1 + * SURFACE_TYPE_SURFACE_VIEW = 0 + * SURFACE_TYPE_SURFACE_TEXTURE = 1 + * SURFACE_TYPE_MEDIA_RECORDER = 2 + * SURFACE_TYPE_MEDIA_CODEC = 3 + * SURFACE_TYPE_IMAGE_READER = 4 + */ + public int getSurfaceType() { + return mSurfaceType; + } + + /** + * Returns the format of the shared output configuration. + * @return format The format of the configured output. This must be one of the + * {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} + * constants. Note that not all formats are supported by the camera device. + */ + public @Format int getFormat() { + return mFormat; + } + + /** + * Returns the configured size for the shared output configuration. + * @return surfaceSize Size for the shared output configuration + * + */ + public @NonNull Size getSize() { + return mSize; + } + + /** + * Return datatspace configured for the shared output configuration. + * + * @return {@link Dataspace#NamedDataSpace} configured for shared session + */ + public @NamedDataSpace int getDataspace() { + return mDataspace; + } + + /** + * Get the mirroring mode configured for the shared output configuration. + * + * @return {@link OutputConfiguration#MirrorMode} configured for the shared session + */ + public @MirrorMode int getMirrorMode() { + return mMirrorMode; + } + + /** + * Get the stream use case configured for the shared output configuration. + * + * @return {@link OutputConfiguration#StreamUseCase} configured for the shared session + */ + public @StreamUseCase long getStreamUseCase() { + return mStreamUseCase; + } + + /** + * Get the timestamp base configured for the shared output configuration. + * + * @return {@link OutputConfiguration#TimestampBase} configured for the shared session + */ + public @TimestampBase int getTimestampBase() { + return mTimestampBase; + } + + /** Whether readout timestamp is used for this shared output configuration. + * + */ + public boolean isReadoutTimestampEnabled() { + return mReadoutTimestampEnabled; + } + + /** Returns the usage if set for this shared output configuration. + * + * @return {@link HardwareBuffer#Usage} flags if set for shared output configuration with + * the ImageReader output surface. + */ + public @Usage long getUsage() { + return mUsage; + } + + public @Nullable String getPhysicalCameraId() { + return mPhysicalCameraId; + } + } + + /** + * Create a new {@link SharedSessionConfiguration}. + * + * <p>The array parameters ownership is passed to this object after creation; do not + * write to them after this constructor is invoked.</p> + * + * @param sharedColorSpace the colorspace to be used for the shared output configurations. + * @param sharedOutputConfigurations a non-{@code null} array of metadata + * android.info.availableSharedOutputConfigurations + * + * @hide + */ + public SharedSessionConfiguration(int sharedColorSpace, + @NonNull long[] sharedOutputConfigurations) { + mColorSpace = sharedColorSpace; + byte physicalCameraIdLen; + int surfaceType, width, height, format, mirrorMode, timestampBase, dataspace; + long usage, streamUseCase; + boolean isReadOutTimestampEnabled; + // Parse metadata android.info.availableSharedOutputConfigurations which contains + // list of shared output configurations. + int numOfEntries = sharedOutputConfigurations.length; + int i = 0; + while (numOfEntries >= SharedSessionConfiguration.SHARED_OUTPUT_CONFIG_NUM_OF_ENTRIES) { + surfaceType = (int) sharedOutputConfigurations[i]; + width = (int) sharedOutputConfigurations[i + 1]; + height = (int) sharedOutputConfigurations[i + 2]; + format = (int) sharedOutputConfigurations[i + 3]; + mirrorMode = (int) sharedOutputConfigurations[i + 4]; + isReadOutTimestampEnabled = (sharedOutputConfigurations[i + 5] != 0); + timestampBase = (int) sharedOutputConfigurations[i + 6]; + dataspace = (int) sharedOutputConfigurations[i + 7]; + usage = sharedOutputConfigurations[i + 8]; + streamUseCase = sharedOutputConfigurations[i + 9]; + physicalCameraIdLen = (byte) sharedOutputConfigurations[i + 10]; + numOfEntries -= SharedSessionConfiguration.SHARED_OUTPUT_CONFIG_NUM_OF_ENTRIES; + i += SharedSessionConfiguration.SHARED_OUTPUT_CONFIG_NUM_OF_ENTRIES; + if (numOfEntries < physicalCameraIdLen) { + Log.e(TAG, "Number of remaining data in shared configuration is less than" + + " physical camera id length . Malformed metadata" + + " android.info.availableSharedOutputConfigurations."); + break; + } + StringBuilder physicalCameraId = new StringBuilder(); + long asciiValue; + for (int j = 0; j < physicalCameraIdLen; j++) { + asciiValue = sharedOutputConfigurations[i + j]; + if (asciiValue == 0) { // Check for null terminator + break; + } + physicalCameraId.append((char) asciiValue); + } + SharedOutputConfiguration outputInfo; + outputInfo = new SharedOutputConfiguration(surfaceType, new Size(width, height), + format, mirrorMode, isReadOutTimestampEnabled, timestampBase, + dataspace, usage, streamUseCase, physicalCameraId.toString()); + mOutputStreamConfigurations.add(outputInfo); + i += physicalCameraIdLen; + numOfEntries -= physicalCameraIdLen; + } + if (numOfEntries != 0) { + Log.e(TAG, "Unexpected entries left in shared output configuration." + + " Malformed metadata android.info.availableSharedOutputConfigurations."); + } + } + + /** + * Return the shared session color space which is configured. + * + * @return the shared session color space + */ + @SuppressLint("MethodNameUnits") + public @Nullable ColorSpace getColorSpace() { + if (mColorSpace != ColorSpaceProfiles.UNSPECIFIED) { + return ColorSpace.get(ColorSpace.Named.values()[mColorSpace]); + } else { + return null; + } + } + /** + * Get information about each shared output configuarion in the shared session. + * + * @return Non-modifiable list of output configuration. + * + */ + public @NonNull List<SharedOutputConfiguration> getOutputStreamsInformation() { + return Collections.unmodifiableList(mOutputStreamConfigurations); + } + + private int mColorSpace; + private final ArrayList<SharedOutputConfiguration> mOutputStreamConfigurations = + new ArrayList<SharedOutputConfiguration>(); +} + diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java index ac85ab7f6a6e..88c1c434cc0d 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java @@ -257,6 +257,16 @@ public class CameraBinderTest extends AndroidTestCase { public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) { // TODO Auto-generated method stub } + + /* + * (non-Javadoc) + * @see android.hardware.camera2.ICameraDeviceCallbacks#onClientSharedAccessPriorityChanged + */ + @Override + public void onClientSharedAccessPriorityChanged(boolean primaryClient) { + // TODO Auto-generated method stub + } + } @SmallTest @@ -276,7 +286,7 @@ public class CameraBinderTest extends AndroidTestCase { 0 /*oomScoreOffset*/, getContext().getApplicationInfo().targetSdkVersion, ICameraService.ROTATION_OVERRIDE_NONE, clientAttribution, - DEVICE_POLICY_DEFAULT); + DEVICE_POLICY_DEFAULT, false/*sharedMode*/); assertNotNull(String.format("Camera %s was null", cameraId), cameraUser); Log.v(TAG, String.format("Camera %s connected", cameraId)); @@ -320,6 +330,13 @@ public class CameraBinderTest extends AndroidTestCase { Log.v(TAG, String.format("Camera " + cameraId + " torch strength level changed to " + torchStrength )); } + @Override + public void onCameraOpenedInSharedMode(String cameraId, String clientPackageName, + int deviceId, boolean primaryClient) { + Log.v(TAG, "Camera " + cameraId + " is opened in shared mode by " + + "client package " + clientPackageName + " as primary client=" + + primaryClient); + } } /** diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java index 35ad924cee74..3758c515c1a5 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java @@ -175,6 +175,15 @@ public class CameraDeviceBinderTest extends AndroidTestCase { public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) { // TODO Auto-generated method stub } + + /** + * (non-Javadoc) + * @see android.hardware.camera2.ICameraDeviceCallbacks#onClientSharedAccessPriorityChanged + */ + @Override + public void onClientSharedAccessPriorityChanged(boolean primaryClient) { + // TODO Auto-generated method stub + } } class IsMetadataNotEmpty implements ArgumentMatcher<CameraMetadataNative> { @@ -250,7 +259,8 @@ public class CameraDeviceBinderTest extends AndroidTestCase { mCameraUser = mUtils.getCameraService().connectDevice(mMockCb, mCameraId, /*oomScoreOffset*/0, getContext().getApplicationInfo().targetSdkVersion, - ICameraService.ROTATION_OVERRIDE_NONE, clientAttribution, DEVICE_POLICY_DEFAULT); + ICameraService.ROTATION_OVERRIDE_NONE, clientAttribution, DEVICE_POLICY_DEFAULT, + /*sharedMode*/false); assertNotNull(String.format("Camera %s was null", mCameraId), mCameraUser); mHandlerThread = new HandlerThread(TAG); mHandlerThread.start(); |