Create API for reporting color space support to Camera2 framework consumers.

Test: Ran CtsCameraTestCases
Test: Ran new VTS test for color space reporting
Test: Created app to test display P3 camera, switching between color spaces
Bug: 238359088
Change-Id: I5c74ea438970cbe55f93b925a6744f4343bc545a
diff --git a/core/api/current.txt b/core/api/current.txt
index 74ff672..bcce8bf 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17626,6 +17626,7 @@
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REPROCESS_MAX_CAPTURE_STALL;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> REQUEST_AVAILABLE_CAPABILITIES;
+    field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.ColorSpaceProfiles> REQUEST_AVAILABLE_COLOR_SPACE_PROFILES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.DynamicRangeProfiles> REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_INPUT_STREAMS;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC;
@@ -17989,6 +17990,7 @@
     field public static final int NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG = 4; // 0x4
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_COLOR_SPACE_PROFILES = 20; // 0x14
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9; // 0x9
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; // 0x8
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT = 18; // 0x12
@@ -18340,6 +18342,15 @@
     method @NonNull public android.util.Range<java.lang.Float> getZoomRatioRange();
   }
 
+  public final class ColorSpaceProfiles {
+    ctor public ColorSpaceProfiles(@NonNull long[]);
+    method @NonNull public java.util.Set<android.graphics.ColorSpace.Named> getSupportedColorSpaces(int);
+    method @NonNull public java.util.Set<android.graphics.ColorSpace.Named> getSupportedColorSpacesForDynamicRange(int, long);
+    method @NonNull public java.util.Set<java.lang.Long> getSupportedDynamicRangeProfiles(@NonNull android.graphics.ColorSpace.Named, int);
+    method @NonNull public java.util.Set<java.lang.Integer> getSupportedImageFormatsForColorSpace(@NonNull android.graphics.ColorSpace.Named);
+    field public static final int UNSPECIFIED = -1; // 0xffffffff
+  }
+
   public final class ColorSpaceTransform {
     ctor public ColorSpaceTransform(android.util.Rational[]);
     ctor public ColorSpaceTransform(int[]);
@@ -18571,13 +18582,16 @@
 
   public final class SessionConfiguration implements android.os.Parcelable {
     ctor public SessionConfiguration(int, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback);
+    method public void clearColorSpace();
     method public int describeContents();
+    method @Nullable public android.graphics.ColorSpace getColorSpace();
     method public java.util.concurrent.Executor getExecutor();
     method public android.hardware.camera2.params.InputConfiguration getInputConfiguration();
     method public java.util.List<android.hardware.camera2.params.OutputConfiguration> getOutputConfigurations();
     method public android.hardware.camera2.CaptureRequest getSessionParameters();
     method public int getSessionType();
     method public android.hardware.camera2.CameraCaptureSession.StateCallback getStateCallback();
+    method public void setColorSpace(@NonNull android.graphics.ColorSpace.Named);
     method public void setInputConfiguration(@NonNull android.hardware.camera2.params.InputConfiguration);
     method public void setSessionParameters(android.hardware.camera2.CaptureRequest);
     method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 1e4023e..2602e78 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1170,6 +1170,20 @@
 
 }
 
+package android.hardware.camera2.params {
+
+  public final class ColorSpaceProfiles {
+    method @NonNull public java.util.Map<android.graphics.ColorSpace.Named,java.util.Map<java.lang.Integer,java.util.Set<java.lang.Long>>> getProfileMap();
+  }
+
+  public final class OutputConfiguration implements android.os.Parcelable {
+    method public void clearColorSpace();
+    method @Nullable public android.graphics.ColorSpace getColorSpace();
+    method public void setColorSpace(@NonNull android.graphics.ColorSpace.Named);
+  }
+
+}
+
 package android.hardware.devicestate {
 
   public final class DeviceStateManager {
diff --git a/core/java/android/hardware/CameraStreamStats.java b/core/java/android/hardware/CameraStreamStats.java
index 3952467..aed5a12 100644
--- a/core/java/android/hardware/CameraStreamStats.java
+++ b/core/java/android/hardware/CameraStreamStats.java
@@ -16,6 +16,7 @@
 package android.hardware;
 
 import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.params.ColorSpaceProfiles;
 import android.hardware.camera2.params.DynamicRangeProfiles;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -50,6 +51,7 @@
     private long[] mHistogramCounts;
     private long mDynamicRangeProfile;
     private long mStreamUseCase;
+    private int mColorSpace;
 
     private static final String TAG = "CameraStreamStats";
 
@@ -68,12 +70,13 @@
         mHistogramType = HISTOGRAM_TYPE_UNKNOWN;
         mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
         mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
+        mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
     }
 
     public CameraStreamStats(int width, int height, int format, float maxPreviewFps,
             int dataSpace, long usage, long requestCount, long errorCount,
             int startLatencyMs, int maxHalBuffers, int maxAppBuffers, long dynamicRangeProfile,
-            long streamUseCase) {
+            long streamUseCase, int colorSpace) {
         mWidth = width;
         mHeight = height;
         mFormat = format;
@@ -88,6 +91,7 @@
         mHistogramType = HISTOGRAM_TYPE_UNKNOWN;
         mDynamicRangeProfile = dynamicRangeProfile;
         mStreamUseCase = streamUseCase;
+        mColorSpace = colorSpace;
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<CameraStreamStats> CREATOR =
@@ -136,6 +140,7 @@
         dest.writeLongArray(mHistogramCounts);
         dest.writeLong(mDynamicRangeProfile);
         dest.writeLong(mStreamUseCase);
+        dest.writeInt(mColorSpace);
     }
 
     public void readFromParcel(Parcel in) {
@@ -155,6 +160,7 @@
         mHistogramCounts = in.createLongArray();
         mDynamicRangeProfile = in.readLong();
         mStreamUseCase = in.readLong();
+        mColorSpace = in.readInt();
     }
 
     public int getWidth() {
@@ -217,6 +223,10 @@
         return mDynamicRangeProfile;
     }
 
+    public int getColorSpace() {
+        return mColorSpace;
+    }
+
     public long getStreamUseCase() {
         return mStreamUseCase;
     }
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 8873807..f634726 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2224,6 +2224,7 @@
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING REMOSAIC_REPROCESSING}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT DYNAMIC_RANGE_TEN_BIT}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE STREAM_USE_CASE}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_COLOR_SPACE_PROFILES COLOR_SPACE_PROFILES}</li>
      * </ul>
      *
      * <p>This key is available on all devices.</p>
@@ -2249,6 +2250,7 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING
      * @see #REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT
      * @see #REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_COLOR_SPACE_PROFILES
      */
     @PublicKey
     @NonNull
@@ -2473,6 +2475,82 @@
             new Key<Long>("android.request.recommendedTenBitDynamicRangeProfile", long.class);
 
     /**
+     * <p>An interface for querying the color space profiles supported by a camera device.</p>
+     * <p>A color space profile is a combination of a color space, an image format, and a dynamic
+     * range profile. Camera clients can retrieve the list of supported color spaces by calling
+     * {@link android.hardware.camera2.params.ColorSpaceProfiles#getSupportedColorSpaces } or
+     * {@link android.hardware.camera2.params.ColorSpaceProfiles#getSupportedColorSpacesForDynamicRange }.
+     * If a camera does not support the
+     * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
+     * capability, the dynamic range profile will always be
+     * {@link android.hardware.camera2.params.DynamicRangeProfiles#STANDARD }. Color space
+     * capabilities are queried in combination with an {@link android.graphics.ImageFormat }.
+     * If a camera client wants to know the general color space capabilities of a camera device
+     * regardless of image format, it can specify {@link android.graphics.ImageFormat#UNKNOWN }.
+     * The color space for a session can be configured by setting the SessionConfiguration
+     * color space via {@link android.hardware.camera2.params.SessionConfiguration#setColorSpace }.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     */
+    @PublicKey
+    @NonNull
+    @SyntheticKey
+    public static final Key<android.hardware.camera2.params.ColorSpaceProfiles> REQUEST_AVAILABLE_COLOR_SPACE_PROFILES =
+            new Key<android.hardware.camera2.params.ColorSpaceProfiles>("android.request.availableColorSpaceProfiles", android.hardware.camera2.params.ColorSpaceProfiles.class);
+
+    /**
+     * <p>A list of all possible color space profiles supported by a camera device.</p>
+     * <p>A color space profile is a combination of a color space, an image format, and a dynamic range
+     * profile. If a camera does not support the
+     * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT }
+     * capability, the dynamic range profile will always be
+     * {@link android.hardware.camera2.params.DynamicRangeProfiles#STANDARD }. Camera clients can
+     * use {@link android.hardware.camera2.params.SessionConfiguration#setColorSpace } to select
+     * a color space.</p>
+     * <p><b>Possible values:</b></p>
+     * <ul>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_UNSPECIFIED UNSPECIFIED}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_SRGB SRGB}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_LINEAR_SRGB LINEAR_SRGB}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_EXTENDED_SRGB EXTENDED_SRGB}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_LINEAR_EXTENDED_SRGB LINEAR_EXTENDED_SRGB}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_BT709 BT709}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_BT2020 BT2020}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_DCI_P3 DCI_P3}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_DISPLAY_P3 DISPLAY_P3}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_NTSC_1953 NTSC_1953}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_SMPTE_C SMPTE_C}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_ADOBE_RGB ADOBE_RGB}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_PRO_PHOTO_RGB PRO_PHOTO_RGB}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_ACES ACES}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_ACESCG ACESCG}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_CIE_XYZ CIE_XYZ}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_CIE_LAB CIE_LAB}</li>
+     * </ul>
+     *
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_UNSPECIFIED
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_SRGB
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_LINEAR_SRGB
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_EXTENDED_SRGB
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_LINEAR_EXTENDED_SRGB
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_BT709
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_BT2020
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_DCI_P3
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_DISPLAY_P3
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_NTSC_1953
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_SMPTE_C
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_ADOBE_RGB
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_PRO_PHOTO_RGB
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_ACES
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_ACESCG
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_CIE_XYZ
+     * @see #REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_CIE_LAB
+     * @hide
+     */
+    public static final Key<long[]> REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP =
+            new Key<long[]>("android.request.availableColorSpaceProfilesMap", long[].class);
+
+    /**
      * <p>The list of image formats that are supported by this
      * camera device for output streams.</p>
      * <p>All camera devices will support JPEG and YUV_420_888 formats.</p>
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index c67a560..1e1d443 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -1257,6 +1257,24 @@
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE = 19;
 
+    /**
+     * <p>The device supports querying the possible combinations of color spaces, image
+     * formats, and dynamic range profiles supported by the camera and requesting a
+     * particular color space for a session via
+     * {@link android.hardware.camera2.params.SessionConfiguration#setColorSpace }.</p>
+     * <p>Cameras that enable this capability may or may not also implement dynamic range
+     * profiles. If they don't,
+     * {@link android.hardware.camera2.params.ColorSpaceProfiles#getSupportedDynamicRangeProfiles }
+     * will return only
+     * {@link android.hardware.camera2.params.DynamicRangeProfiles#STANDARD } and
+     * {@link android.hardware.camera2.params.ColorSpaceProfiles#getSupportedColorSpacesForDynamicRange }
+     * will assume support of the
+     * {@link android.hardware.camera2.params.DynamicRangeProfiles#STANDARD }
+     * profile in all combinations of color spaces and image formats.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_COLOR_SPACE_PROFILES = 20;
+
     //
     // Enumeration values for CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
     //
@@ -1367,6 +1385,18 @@
     public static final int REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_MAX = 0x1000;
 
     //
+    // Enumeration values for CameraCharacteristics#REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP
+    //
+
+    /**
+     * <p>Default value, when not explicitly specified. The Camera device will choose the color
+     * space to employ.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP
+     * @hide
+     */
+    public static final int REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_UNSPECIFIED = -1;
+
+    //
     // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
     //
 
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index ee12df5..012fad5 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -50,6 +50,7 @@
 import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration;
 import android.hardware.camera2.marshal.impl.MarshalQueryableString;
 import android.hardware.camera2.params.Capability;
+import android.hardware.camera2.params.ColorSpaceProfiles;
 import android.hardware.camera2.params.DeviceStateSensorOrientationMap;
 import android.hardware.camera2.params.DynamicRangeProfiles;
 import android.hardware.camera2.params.Face;
@@ -813,6 +814,15 @@
                     }
                 });
         sGetCommandMap.put(
+                CameraCharacteristics.REQUEST_AVAILABLE_COLOR_SPACE_PROFILES.getNativeKey(),
+                        new GetCommand() {
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+                        return (T) metadata.getColorSpaceProfiles();
+                    }
+                });
+        sGetCommandMap.put(
                 CaptureResult.STATISTICS_OIS_SAMPLES.getNativeKey(),
                         new GetCommand() {
                     @Override
@@ -1068,6 +1078,17 @@
         return new DynamicRangeProfiles(profileArray);
     }
 
+    private ColorSpaceProfiles getColorSpaceProfiles() {
+        long[] profileArray = getBase(
+                CameraCharacteristics.REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP);
+
+        if (profileArray == null) {
+            return null;
+        }
+
+        return new ColorSpaceProfiles(profileArray);
+    }
+
     private Location getGpsLocation() {
         String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
         double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES);
diff --git a/core/java/android/hardware/camera2/params/ColorSpaceProfiles.java b/core/java/android/hardware/camera2/params/ColorSpaceProfiles.java
new file mode 100644
index 0000000..2e3af80
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/ColorSpaceProfiles.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2022 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.NonNull;
+import android.annotation.TestApi;
+import android.graphics.ColorSpace;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraMetadata;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Immutable class with information about supported color space profiles.
+ *
+ * <p>An instance of this class can be queried by retrieving the value of
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_COLOR_SPACE_PROFILES}.
+ * </p>
+ *
+ * <p>All camera devices supporting the
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_COLOR_SPACE_PROFILES}
+ * capability must advertise the supported color space profiles in
+ * {@link #getSupportedColorSpaces}</p>
+ *
+ * @see SessionConfiguration#setColorSpace
+ */
+public final class ColorSpaceProfiles {
+    /*
+     * @hide
+     */
+    public static final int UNSPECIFIED =
+            CameraMetadata.REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_UNSPECIFIED;
+
+    private final Map<ColorSpace.Named, Map<Integer, Set<Long>>> mProfileMap = new ArrayMap<>();
+
+    /**
+     * Create a new immutable ColorSpaceProfiles instance.
+     *
+     * <p>This constructor takes over the array; do not write to the array afterwards.</p>
+     *
+     * <p>Do note that the constructor is available for testing purposes only!
+     * Camera clients must always retrieve the value of
+     * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_COLOR_SPACE_PROFILES}.
+     * for a given camera id in order to retrieve the device capabilities.</p>
+     *
+     * @param elements
+     *          An array of elements describing the map. It contains three elements per entry which
+     *          describe the supported color space profile value in the first element, a compatible
+     *          image format in the second, and in the third element a bitmap of compatible dynamic
+     *          range profiles (see {@link DynamicRangeProfiles#STANDARD} and others for the
+     *          individual bitmap components).
+     *
+     * @throws IllegalArgumentException
+     *            if the {@code elements} array length is invalid, not divisible by 3 or contains
+     *            invalid element values
+     * @throws NullPointerException
+     *            if {@code elements} is {@code null}
+     *
+     */
+    public ColorSpaceProfiles(@NonNull final long[] elements) {
+        if ((elements.length % 3) != 0) {
+            throw new IllegalArgumentException("Color space profile map length "
+                    + elements.length + " is not divisible by 3!");
+        }
+
+        for (int i = 0; i < elements.length; i += 3) {
+            int colorSpace = (int) elements[i];
+            checkProfileValue(colorSpace);
+            ColorSpace.Named namedColorSpace = ColorSpace.Named.values()[colorSpace];
+            int imageFormat = (int) elements[i + 1];
+            long dynamicRangeProfileBitmap = elements[i + 2];
+
+            if (!mProfileMap.containsKey(namedColorSpace)) {
+                ArrayMap<Integer, Set<Long>> imageFormatMap = new ArrayMap<>();
+                mProfileMap.put(namedColorSpace, imageFormatMap);
+            }
+
+            if (!mProfileMap.get(namedColorSpace).containsKey(imageFormat)) {
+                ArraySet<Long> dynamicRangeProfiles = new ArraySet<>();
+                mProfileMap.get(namedColorSpace).put(imageFormat, dynamicRangeProfiles);
+            }
+
+            if (dynamicRangeProfileBitmap != 0) {
+                for (long dynamicRangeProfile = DynamicRangeProfiles.STANDARD;
+                        dynamicRangeProfile < DynamicRangeProfiles.PUBLIC_MAX;
+                        dynamicRangeProfile <<= 1) {
+                    if ((dynamicRangeProfileBitmap & dynamicRangeProfile) != 0) {
+                        mProfileMap.get(namedColorSpace).get(imageFormat).add(dynamicRangeProfile);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static void checkProfileValue(int colorSpace) {
+        boolean found = false;
+        for (ColorSpace.Named value : ColorSpace.Named.values()) {
+            if (colorSpace == value.ordinal()) {
+                found = true;
+                break;
+            }
+        }
+
+        if (!found) {
+            throw new IllegalArgumentException("Unknown ColorSpace " + colorSpace);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public @NonNull Map<ColorSpace.Named, Map<Integer, Set<Long>>> getProfileMap() {
+        return mProfileMap;
+    }
+
+    /**
+     * Return a list of color spaces that are compatible with an ImageFormat. If ImageFormat.UNKNOWN
+     * is provided, this function will return a set of all unique color spaces supported by the
+     * device, regardless of image format.
+     *
+     * Color spaces which are compatible with ImageFormat.PRIVATE are able to be used with
+     * SurfaceView, SurfaceTexture, MediaCodec and MediaRecorder.
+     *
+     * @return set of color spaces
+     * @see SessionConfiguration#setColorSpace
+     * @see ColorSpace.Named
+     */
+    public @NonNull Set<ColorSpace.Named> getSupportedColorSpaces(
+            @ImageFormat.Format int imageFormat) {
+        ArraySet<ColorSpace.Named> supportedColorSpaceProfiles = new ArraySet<>();
+        for (ColorSpace.Named colorSpace : mProfileMap.keySet()) {
+            if (imageFormat == ImageFormat.UNKNOWN) {
+                supportedColorSpaceProfiles.add(colorSpace);
+            } else {
+                Map<Integer, Set<Long>> imageFormatMap = mProfileMap.get(colorSpace);
+                if (imageFormatMap.containsKey(imageFormat)) {
+                    supportedColorSpaceProfiles.add(colorSpace);
+                }
+            }
+        }
+        return supportedColorSpaceProfiles;
+    }
+
+    /**
+     * Return a list of image formats that are compatible with a color space.
+     *
+     * Color spaces which are compatible with ImageFormat.PRIVATE are able to be used with
+     * SurfaceView, SurfaceTexture, MediaCodec and MediaRecorder.
+     *
+     * @return set of image formats
+     * @see SessionConfiguration#setColorSpace
+     * @see ColorSpace.Named
+     */
+    public @NonNull Set<Integer> getSupportedImageFormatsForColorSpace(
+            @NonNull ColorSpace.Named colorSpace) {
+        Map<Integer, Set<Long>> imageFormatMap = mProfileMap.get(colorSpace);
+        if (imageFormatMap == null) {
+            return new ArraySet<Integer>();
+        }
+
+        return imageFormatMap.keySet();
+    }
+
+    /**
+     * Return a list of dynamic range profiles that are compatible with a color space and
+     * ImageFormat. If ImageFormat.UNKNOWN is provided, this function will return a set of
+     * all unique dynamic range profiles supported by the device given a color space,
+     * regardless of image format.
+     *
+     * @return set of dynamic range profiles.
+     * @see OutputConfiguration#setDynamicRangeProfile
+     * @see SessionConfiguration#setColorSpace
+     * @see ColorSpace.Named
+     * @see DynamicRangeProfiles.Profile
+     */
+    public @NonNull Set<Long> getSupportedDynamicRangeProfiles(@NonNull ColorSpace.Named colorSpace,
+            @ImageFormat.Format int imageFormat) {
+        Map<Integer, Set<Long>> imageFormatMap = mProfileMap.get(colorSpace);
+        if (imageFormatMap == null) {
+            return new ArraySet<Long>();
+        }
+
+        Set<Long> dynamicRangeProfiles = null;
+        if (imageFormat == ImageFormat.UNKNOWN) {
+            dynamicRangeProfiles = new ArraySet<>();
+            for (int supportedImageFormat : imageFormatMap.keySet()) {
+                Set<Long> supportedDynamicRangeProfiles = imageFormatMap.get(
+                        supportedImageFormat);
+                for (Long supportedDynamicRangeProfile : supportedDynamicRangeProfiles) {
+                    dynamicRangeProfiles.add(supportedDynamicRangeProfile);
+                }
+            }
+        } else {
+            dynamicRangeProfiles = imageFormatMap.get(imageFormat);
+            if (dynamicRangeProfiles == null) {
+                return new ArraySet<>();
+            }
+        }
+
+        return dynamicRangeProfiles;
+    }
+
+    /**
+     * Return a list of color spaces that are compatible with an ImageFormat and a dynamic range
+     * profile. If ImageFormat.UNKNOWN is provided, this function will return a set of all unique
+     * color spaces compatible with the given dynamic range profile, regardless of image format.
+     *
+     * @return set of color spaces
+     * @see SessionConfiguration#setColorSpace
+     * @see OutputConfiguration#setDynamicRangeProfile
+     * @see ColorSpace.Named
+     * @see DynamicRangeProfiles.Profile
+     */
+    public @NonNull Set<ColorSpace.Named> getSupportedColorSpacesForDynamicRange(
+            @ImageFormat.Format int imageFormat,
+            @DynamicRangeProfiles.Profile long dynamicRangeProfile) {
+        ArraySet<ColorSpace.Named> supportedColorSpaceProfiles = new ArraySet<>();
+        for (ColorSpace.Named colorSpace : mProfileMap.keySet()) {
+            Map<Integer, Set<Long>> imageFormatMap = mProfileMap.get(colorSpace);
+            if (imageFormat == ImageFormat.UNKNOWN) {
+                for (int supportedImageFormat : imageFormatMap.keySet()) {
+                    Set<Long> dynamicRangeProfiles = imageFormatMap.get(supportedImageFormat);
+                    if (dynamicRangeProfiles.contains(dynamicRangeProfile)) {
+                        supportedColorSpaceProfiles.add(colorSpace);
+                    }
+                }
+            } else if (imageFormatMap.containsKey(imageFormat)) {
+                Set<Long> dynamicRangeProfiles = imageFormatMap.get(imageFormat);
+                if (dynamicRangeProfiles.contains(dynamicRangeProfile)) {
+                    supportedColorSpaceProfiles.add(colorSpace);
+                }
+            }
+        }
+        return supportedColorSpaceProfiles;
+    }
+}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 90e92db..a0d8f8d 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -22,7 +22,10 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.graphics.ColorSpace;
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
@@ -30,7 +33,6 @@
 import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.MultiResolutionImageReader;
 import android.hardware.camera2.params.DynamicRangeProfiles;
-import android.hardware.camera2.params.DynamicRangeProfiles.Profile;
 import android.hardware.camera2.params.MultiResolutionStreamInfo;
 import android.hardware.camera2.utils.HashCodeHelpers;
 import android.hardware.camera2.utils.SurfaceUtils;
@@ -454,7 +456,7 @@
      * {@link android.media.MediaCodec} etc.)
      * or {@link ImageFormat#YCBCR_P010}.</p>
      */
-    public void setDynamicRangeProfile(@Profile long profile) {
+    public void setDynamicRangeProfile(@DynamicRangeProfiles.Profile long profile) {
         mDynamicRangeProfile = profile;
     }
 
@@ -463,11 +465,54 @@
      *
      * @return the currently set dynamic range profile
      */
-    public @Profile long getDynamicRangeProfile() {
+    public @DynamicRangeProfiles.Profile long getDynamicRangeProfile() {
         return mDynamicRangeProfile;
     }
 
     /**
+     * Set a specific device-supported color space.
+     *
+     * <p>Clients can choose from any profile advertised as supported in
+     * {@link CameraCharacteristics#REQUEST_AVAILABLE_COLOR_SPACE_PROFILES}
+     * queried using {@link ColorSpaceProfiles#getSupportedColorSpaces}.
+     * When set, the colorSpace will override the default color spaces of the output targets,
+     * or the color space implied by the dataSpace passed into an {@link ImageReader}'s
+     * constructor.</p>
+     *
+     * @hide
+     */
+    @TestApi
+    public void setColorSpace(@NonNull ColorSpace.Named colorSpace) {
+        mColorSpace = colorSpace.ordinal();
+    }
+
+    /**
+     * Clear the color space, such that the default color space will be used.
+     *
+     * @hide
+     */
+    @TestApi
+    public void clearColorSpace() {
+        mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
+    }
+
+    /**
+     * Return the current color space.
+     *
+     * @return the currently set color space
+     * @hide
+     */
+    @TestApi
+    @SuppressLint("MethodNameUnits")
+    public @Nullable ColorSpace getColorSpace() {
+        if (mColorSpace != ColorSpaceProfiles.UNSPECIFIED) {
+            return ColorSpace.get(ColorSpace.Named.values()[mColorSpace]);
+        } else {
+            return null;
+        }
+    }
+
+    /**
      * Create a new {@link OutputConfiguration} instance.
      *
      * <p>This constructor takes an argument for desired camera rotation</p>
@@ -530,6 +575,7 @@
         mIsMultiResolution = false;
         mSensorPixelModesUsed = new ArrayList<Integer>();
         mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
+        mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
         mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
         mTimestampBase = TIMESTAMP_BASE_DEFAULT;
         mMirrorMode = MIRROR_MODE_AUTO;
@@ -631,6 +677,7 @@
         mIsMultiResolution = false;
         mSensorPixelModesUsed = new ArrayList<Integer>();
         mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
+        mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
         mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
     }
 
@@ -1079,6 +1126,7 @@
         this.mIsMultiResolution = other.mIsMultiResolution;
         this.mSensorPixelModesUsed = other.mSensorPixelModesUsed;
         this.mDynamicRangeProfile = other.mDynamicRangeProfile;
+        this.mColorSpace = other.mColorSpace;
         this.mStreamUseCase = other.mStreamUseCase;
         this.mTimestampBase = other.mTimestampBase;
         this.mMirrorMode = other.mMirrorMode;
@@ -1105,6 +1153,7 @@
         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
         long dynamicRangeProfile = source.readLong();
         DynamicRangeProfiles.checkProfileValue(dynamicRangeProfile);
+        int colorSpace = source.readInt();
 
         int timestampBase = source.readInt();
         int mirrorMode = source.readInt();
@@ -1132,6 +1181,7 @@
         mIsMultiResolution = isMultiResolutionOutput;
         mSensorPixelModesUsed = convertIntArrayToIntegerList(sensorPixelModesUsed);
         mDynamicRangeProfile = dynamicRangeProfile;
+        mColorSpace = colorSpace;
         mStreamUseCase = streamUseCase;
         mTimestampBase = timestampBase;
         mMirrorMode = mirrorMode;
@@ -1251,6 +1301,7 @@
         // writeList doesn't seem to work well with Integer list.
         dest.writeIntArray(convertIntegerToIntList(mSensorPixelModesUsed));
         dest.writeLong(mDynamicRangeProfile);
+        dest.writeInt(mColorSpace);
         dest.writeLong(mStreamUseCase);
         dest.writeInt(mTimestampBase);
         dest.writeInt(mMirrorMode);
@@ -1305,6 +1356,9 @@
             if (mDynamicRangeProfile != other.mDynamicRangeProfile) {
                 return false;
             }
+            if (mColorSpace != other.mColorSpace) {
+                return false;
+            }
 
             return true;
         }
@@ -1325,7 +1379,8 @@
                     mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0,
                     mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
                     mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
-                    mDynamicRangeProfile, mStreamUseCase, mTimestampBase, mMirrorMode);
+                    mDynamicRangeProfile, mColorSpace, mStreamUseCase,
+                    mTimestampBase, mMirrorMode);
         }
 
         return HashCodeHelpers.hashCode(
@@ -1334,7 +1389,7 @@
                 mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0,
                 mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
                 mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
-                mDynamicRangeProfile, mStreamUseCase, mTimestampBase,
+                mDynamicRangeProfile, mColorSpace, mStreamUseCase, mTimestampBase,
                 mMirrorMode);
     }
 
@@ -1369,6 +1424,8 @@
     private ArrayList<Integer> mSensorPixelModesUsed;
     // Dynamic range profile
     private long mDynamicRangeProfile;
+    // Color space
+    private int mColorSpace;
     // Stream use case
     private long mStreamUseCase;
     // Timestamp base
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index cfb6efa..385f107 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -23,6 +23,8 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.graphics.ColorSpace;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
@@ -30,6 +32,7 @@
 import android.hardware.camera2.params.InputConfiguration;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.utils.HashCodeHelpers;
+import android.media.ImageReader;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -94,6 +97,7 @@
     private Executor mExecutor = null;
     private InputConfiguration mInputConfig = null;
     private CaptureRequest mSessionParameters = null;
+    private int mColorSpace;
 
     /**
      * Create a new {@link SessionConfiguration}.
@@ -314,4 +318,45 @@
     public CaptureRequest getSessionParameters() {
         return mSessionParameters;
     }
+
+    /**
+     * Set a specific device-supported color space.
+     *
+     * <p>Clients can choose from any profile advertised as supported in
+     * {@link CameraCharacteristics#REQUEST_AVAILABLE_COLOR_SPACE_PROFILES}
+     * queried using {@link ColorSpaceProfiles#getSupportedColorSpaces}.
+     * When set, the colorSpace will override the default color spaces of the output targets,
+     * or the color space implied by the dataSpace passed into an {@link ImageReader}'s
+     * constructor.</p>
+     */
+    public void setColorSpace(@NonNull ColorSpace.Named colorSpace) {
+        mColorSpace = colorSpace.ordinal();
+        for (OutputConfiguration outputConfiguration : mOutputConfigurations) {
+            outputConfiguration.setColorSpace(colorSpace);
+        }
+    }
+
+    /**
+     * Clear the color space, such that the default color space will be used.
+     */
+    public void clearColorSpace() {
+        mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
+        for (OutputConfiguration outputConfiguration : mOutputConfigurations) {
+            outputConfiguration.clearColorSpace();
+        }
+    }
+
+    /**
+     * Return the current color space.
+     *
+     * @return the currently set color space
+     */
+    @SuppressLint("MethodNameUnits")
+    public @Nullable ColorSpace getColorSpace() {
+        if (mColorSpace != ColorSpaceProfiles.UNSPECIFIED) {
+            return ColorSpace.get(ColorSpace.Named.values()[mColorSpace]);
+        } else {
+            return null;
+        }
+    }
 }
diff --git a/proto/src/camera.proto b/proto/src/camera.proto
index 38d74e4..205e806 100644
--- a/proto/src/camera.proto
+++ b/proto/src/camera.proto
@@ -67,4 +67,6 @@
     optional int64 dynamic_range_profile = 14;
     // The stream use case
     optional int64 stream_use_case = 15;
+    // The color space of the stream
+    optional int32 color_space = 16;
 }