Merge "Add multi-client support in camera2" into main
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 8143066..a46f872 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5048,15 +5048,27 @@
 
 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 {
@@ -5064,6 +5076,12 @@
     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 {
@@ -5172,6 +5190,28 @@
     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.contexthub {
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 58e524e..5533a64 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -19,9 +19,9 @@
 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 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 fb381d9..852f047 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -446,6 +446,17 @@
     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 @@
     @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 @@
      *
      * </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 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 75e2058..266efb7 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -976,6 +976,46 @@
     }
 
     /**
+     * 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 @@
      * @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 @@
      */
     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 @@
                         characteristics,
                         this,
                         mContext.getApplicationInfo().targetSdkVersion,
-                        mContext, cameraDeviceSetup);
+                        mContext, cameraDeviceSetup, sharedMode);
             ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
 
             try {
@@ -1091,7 +1135,7 @@
                                 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 @@
             @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 @@
                          /*oomScoreOffset*/0,
                          overrideToPortrait
                                  ? ICameraService.ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT
-                                 : ICameraService.ROTATION_OVERRIDE_NONE);
+                                 : ICameraService.ROTATION_OVERRIDE_NONE, /*sharedMode*/false);
     }
 
     /**
@@ -1303,10 +1348,57 @@
         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
      * components which want to assume a lower priority (for camera arbitration) than other clients
@@ -1372,29 +1464,35 @@
                     "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 @@
         }
 
         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 @@
                 }
                 @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 @@
         }
 
         @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 8d36fbd..d2fcfd6 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2121,6 +2121,38 @@
     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 0000000..5426d4d
--- /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 8407258..ea70abb 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -18,6 +18,7 @@
 
 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.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 @@
 
     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 @@
         }
     };
 
+    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 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 @@
                         @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 @@
         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 @@
                 }
             }
 
-            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 @@
             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 @@
                 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 @@
         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 @@
                 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 @@
                 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 @@
         }
     }
 
+    /**
+     * 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 @@
         }
 
         @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 1cc0856..c0a5928 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.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 @@
                         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 @@
                 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 eb2ff88..1769c46 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.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 @@
         }
 
         @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 0000000..a1f31c0
--- /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 aec2cff..831c75ec 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -301,4 +301,16 @@
         }
     }
 
+    /**
+     * 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 d38be9b..e12c463 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.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 @@
     }
 
     /**
+     * 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 50c6b5b..82aa64b 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.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 @@
         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 0000000..cdcc92c
--- /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 ac85ab7..88c1c43 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 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 @@
                         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 @@
             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 35ad924..3758c51 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 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 @@
 
         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();