APIs for HDR output control

New APIs are added to DisplayManager to set/get HDR conversion
properties. The setter is protected by a new permission.

Bug: 251168516
Test: atest HdrConversionTest
Change-Id: I644ee747b669612f78413117e4f8617dccf88f41
diff --git a/core/api/current.txt b/core/api/current.txt
index f50d3ca..65a89fe 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -49502,6 +49502,7 @@
     field public static final int HDR_TYPE_HDR10 = 2; // 0x2
     field public static final int HDR_TYPE_HDR10_PLUS = 4; // 0x4
     field public static final int HDR_TYPE_HLG = 3; // 0x3
+    field public static final int HDR_TYPE_INVALID = -1; // 0xffffffff
     field public static final float INVALID_LUMINANCE = -1.0f;
   }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8c64e40..a48a103 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -29,6 +29,7 @@
     field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
     field public static final String MANAGE_TOAST_RATE_LIMITING = "android.permission.MANAGE_TOAST_RATE_LIMITING";
+    field public static final String MODIFY_HDR_CONVERSION_MODE = "android.permission.MODIFY_HDR_CONVERSION_MODE";
     field public static final String MODIFY_REFRESH_RATE_SWITCHING_TYPE = "android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE";
     field public static final String MODIFY_USER_PREFERRED_DISPLAY_MODE = "android.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE";
     field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
@@ -1332,11 +1333,14 @@
     method public boolean areUserDisabledHdrTypesAllowed();
     method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void clearGlobalUserPreferredDisplayMode();
     method @Nullable public android.view.Display.Mode getGlobalUserPreferredDisplayMode();
+    method @NonNull public android.hardware.display.HdrConversionMode getHdrConversionMode();
+    method @NonNull public int[] getSupportedHdrOutputTypes();
     method @NonNull public int[] getUserDisabledHdrTypes();
     method public boolean isMinimalPostProcessingRequested(int);
     method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public void overrideHdrTypes(int, @NonNull int[]);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAreUserDisabledHdrTypesAllowed(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setGlobalUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_HDR_CONVERSION_MODE) public void setHdrConversionMode(@NonNull android.hardware.display.HdrConversionMode);
     method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public void setRefreshRateSwitchingType(int);
     method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setUserDisabledHdrTypes(@NonNull int[]);
@@ -1349,6 +1353,19 @@
     field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200
   }
 
+  public final class HdrConversionMode implements android.os.Parcelable {
+    ctor public HdrConversionMode(int, int);
+    ctor public HdrConversionMode(int);
+    method public int describeContents();
+    method public int getConversionMode();
+    method public int getPreferredHdrOutputType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.HdrConversionMode> CREATOR;
+    field public static final int HDR_CONVERSION_FORCE = 3; // 0x3
+    field public static final int HDR_CONVERSION_PASSTHROUGH = 1; // 0x1
+    field public static final int HDR_CONVERSION_SYSTEM = 2; // 0x2
+  }
+
 }
 
 package android.hardware.fingerprint {
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index b333f5a..08238ca 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1301,6 +1301,54 @@
     }
 
     /**
+     * Sets the HDR conversion mode for the device.
+     *
+     * @param hdrConversionMode The {@link HdrConversionMode} to set.
+     * Note, {@code HdrConversionMode.preferredHdrOutputType} is only applicable when
+     * {@code HdrConversionMode.conversionMode} is {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
+     *
+     * @throws IllegalArgumentException if hdrConversionMode.preferredHdrOutputType is not set
+     * when hdrConversionMode.conversionMode is {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
+     * @throws IllegalArgumentException if hdrConversionMode.preferredHdrOutputType is set but
+     * hdrConversionMode.conversionMode is not {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
+     *
+     * @see #getHdrConversionMode
+     * @see #getSupportedHdrOutputTypes
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.MODIFY_HDR_CONVERSION_MODE)
+    public void setHdrConversionMode(@NonNull HdrConversionMode hdrConversionMode) {
+        mGlobal.setHdrConversionMode(hdrConversionMode);
+    }
+
+    /**
+     * Returns the {@link HdrConversionMode} of the device, which is set by the user.
+     *
+     * @see #setHdrConversionMode
+     * @see #getSupportedHdrOutputTypes
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public HdrConversionMode getHdrConversionMode() {
+        return mGlobal.getHdrConversionMode();
+    }
+
+    /**
+     * Returns the HDR output types supported by the device.
+     *
+     * @see #getHdrConversionMode
+     * @see #setHdrConversionMode
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public @HdrType int[] getSupportedHdrOutputTypes() {
+        return mGlobal.getSupportedHdrOutputTypes();
+    }
+
+    /**
      * When enabled the app requested mode is always selected regardless of user settings and
      * policies for low brightness, low battery, etc.
      *
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index f038c66..d9db177 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -979,6 +979,39 @@
     }
 
     /**
+     * Sets the {@link HdrConversionMode} for the device.
+     */
+    public void setHdrConversionMode(@NonNull HdrConversionMode hdrConversionMode) {
+        try {
+            mDm.setHdrConversionMode(hdrConversionMode);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the {@link HdrConversionMode} of the device.
+     */
+    public HdrConversionMode getHdrConversionMode() {
+        try {
+            return mDm.getHdrConversionMode();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the HDR output types supported by the device.
+     */
+    public @HdrType int[] getSupportedHdrOutputTypes() {
+        try {
+            return mDm.getSupportedHdrOutputTypes();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * When enabled the app requested display resolution and refresh rate is always selected
      * in DisplayModeDirector regardless of user settings and policies for low brightness, low
      * battery etc.
diff --git a/core/java/android/hardware/display/HdrConversionMode.aidl b/core/java/android/hardware/display/HdrConversionMode.aidl
new file mode 100644
index 0000000..ac89dd6
--- /dev/null
+++ b/core/java/android/hardware/display/HdrConversionMode.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.display;
+
+parcelable HdrConversionMode;
\ No newline at end of file
diff --git a/core/java/android/hardware/display/HdrConversionMode.java b/core/java/android/hardware/display/HdrConversionMode.java
new file mode 100644
index 0000000..1accd17
--- /dev/null
+++ b/core/java/android/hardware/display/HdrConversionMode.java
@@ -0,0 +1,122 @@
+/*
+ * 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.display;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.Display;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Describes the HDR conversion mode for a device.
+ *
+ * This class is used when user changes the HDR conversion mode of the device via
+ * {@link DisplayManager#setHdrConversionMode(HdrConversionMode)}.
+ * <p>
+ * HDR conversion mode has a conversionMode and preferredHdrOutputType. </p><p>
+ * The conversionMode can be one of:
+ * HDR_CONVERSION_PASSSTHROUGH : HDR conversion is disabled. The output HDR type will change
+ * dynamically to match the content. In this mode, preferredHdrOutputType should not be set.
+ * HDR_CONVERSION_AUTO: The output HDR type is selected by the implementation. In this mode,
+ * preferredHdrOutputType should not be set.
+ * HDR_CONVERSION_FORCE : The implementation converts all content to this HDR type, when possible.
+ * In this mode, preferredHdrOutputType should be set.
+ * </p>
+ * @hide
+ */
+@TestApi
+public final class HdrConversionMode implements Parcelable {
+    /** HDR output conversion is disabled */
+    public static final int HDR_CONVERSION_PASSTHROUGH = 1;
+    /** HDR output conversion is managed by the device manufacturer's implementation. */
+    public static final int HDR_CONVERSION_SYSTEM = 2;
+    /**
+     * HDR output conversion is set by the user. The preferred output type must be
+     * set in this case.
+     */
+    public static final int HDR_CONVERSION_FORCE = 3;
+
+    /** @hide */
+    @IntDef(prefix = {"HDR_CONVERSION"}, value = {
+            HDR_CONVERSION_PASSTHROUGH,
+            HDR_CONVERSION_SYSTEM,
+            HDR_CONVERSION_FORCE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ConversionMode {}
+
+    public static final @NonNull
+            Parcelable.Creator<HdrConversionMode> CREATOR =
+            new Parcelable.Creator<>() {
+                @Override
+                public HdrConversionMode createFromParcel(Parcel source) {
+                    return new HdrConversionMode(source);
+                }
+
+                @Override
+                public HdrConversionMode[] newArray(int size) {
+                    return new HdrConversionMode[size];
+                }
+            };
+
+    private final @ConversionMode int mConversionMode;
+    private @Display.HdrCapabilities.HdrType int mPreferredHdrOutputType;
+
+    public HdrConversionMode(int conversionMode, int preferredHdrOutputType) {
+        if (conversionMode != HdrConversionMode.HDR_CONVERSION_FORCE
+                && preferredHdrOutputType != -1) {
+            throw new IllegalArgumentException("preferredHdrOutputType must not be set if"
+                    + " the conversion mode is not HDR_CONVERSION_FORCE");
+        }
+
+        mConversionMode = conversionMode;
+        mPreferredHdrOutputType = preferredHdrOutputType;
+    }
+
+    public HdrConversionMode(int conversionMode) {
+        mConversionMode = conversionMode;
+        mPreferredHdrOutputType = Display.HdrCapabilities.HDR_TYPE_INVALID;
+    }
+
+    private HdrConversionMode(Parcel source) {
+        this(source.readInt(), source.readInt());
+    }
+
+    public int getConversionMode() {
+        return mConversionMode;
+    }
+
+    public int getPreferredHdrOutputType() {
+        return mPreferredHdrOutputType;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mConversionMode);
+        dest.writeInt(mPreferredHdrOutputType);
+    }
+}
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 28bb35f..0a44f85 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -23,6 +23,7 @@
 import android.hardware.display.BrightnessInfo;
 import android.hardware.display.Curve;
 import android.hardware.graphics.common.DisplayDecorationSupport;
+import android.hardware.display.HdrConversionMode;
 import android.hardware.display.IDisplayManagerCallback;
 import android.hardware.display.IVirtualDisplayCallback;
 import android.hardware.display.VirtualDisplayConfig;
@@ -174,6 +175,14 @@
     Mode getUserPreferredDisplayMode(int displayId);
     Mode getSystemPreferredDisplayMode(int displayId);
 
+    // Sets the HDR conversion mode for a device.
+    // Requires MODIFY_HDR_CONVERSION_MODE permission.
+    @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+                + ".permission.MODIFY_HDR_CONVERSION_MODE)")
+    void setHdrConversionMode(in HdrConversionMode hdrConversionMode);
+    HdrConversionMode getHdrConversionMode();
+    int[] getSupportedHdrOutputTypes();
+
     // When enabled the app requested display resolution and refresh rate is always selected
     // in DisplayModeDirector regardless of user settings and policies for low brightness, low
     // battery etc.
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 689dce8..25863a6 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -2323,6 +2323,10 @@
          */
         public static final float INVALID_LUMINANCE = -1;
         /**
+         * Invalid HDR type value.
+         */
+        public static final int HDR_TYPE_INVALID = -1;
+        /**
          * Dolby Vision high dynamic range (HDR) display.
          */
         public static final int HDR_TYPE_DOLBY_VISION = 1;
@@ -2350,6 +2354,7 @@
 
         /** @hide */
         @IntDef(prefix = { "HDR_TYPE_" }, value = {
+                HDR_TYPE_INVALID,
                 HDR_TYPE_DOLBY_VISION,
                 HDR_TYPE_HDR10,
                 HDR_TYPE_HLG,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index bc40cef..640b84a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5244,6 +5244,12 @@
     <permission android:name="android.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE"
                 android:protectionLevel="signature" />
 
+    <!-- Allows an application to modify the HDR conversion mode.
+         @hide
+         @TestApi -->
+    <permission android:name="android.permission.MODIFY_HDR_CONVERSION_MODE"
+                android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to control VPN.
          <p>Not for use by third-party applications.</p>
          @hide -->
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index c641a85..43062d4 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -508,6 +508,9 @@
     <!-- Permission needed for CTS test - DefaultDisplayModeTest -->
     <uses-permission android:name="android.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE" />
 
+    <!-- Permission needed for CTS test - HdrConversionTest -->
+    <uses-permission android:name="android.permission.MODIFY_HDR_CONVERSION_MODE" />
+
     <!-- Permissions needed for manual testing telephony time zone detector behavior -->
     <uses-permission android:name="android.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE" />
 
diff --git a/services/core/java/com/android/server/display/DisplayControl.java b/services/core/java/com/android/server/display/DisplayControl.java
index 864510e..fa9a100 100644
--- a/services/core/java/com/android/server/display/DisplayControl.java
+++ b/services/core/java/com/android/server/display/DisplayControl.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.os.IBinder;
+import android.view.Display;
 
 import java.util.Objects;
 
@@ -32,6 +33,9 @@
     private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
     private static native long[] nativeGetPhysicalDisplayIds();
     private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId);
+    private static native void nativeSetHdrConversionMode(int conversionMode,
+            int preferredHdrOutputType, int[] autoHdrTypes, int autoHdrTypesLength);
+    private static native int[] nativeGetSupportedHdrOutputTypes();
 
     /**
      * Create a display in SurfaceFlinger.
@@ -79,4 +83,20 @@
     public static IBinder getPhysicalDisplayToken(long physicalDisplayId) {
         return nativeGetPhysicalDisplayToken(physicalDisplayId);
     }
+
+    /**
+     * @hide
+     */
+    public static void setHdrConversionMode(int conversionMode, int preferredHdrOutputType,
+            int[] autoHdrTypes) {
+        int length = autoHdrTypes != null ? autoHdrTypes.length : 0;
+        nativeSetHdrConversionMode(conversionMode, preferredHdrOutputType, autoHdrTypes, length);
+    }
+
+    /**
+     * @hide
+     */
+    public static @Display.HdrCapabilities.HdrType int[] getSupportedHdrOutputTypes() {
+        return nativeGetSupportedHdrOutputTypes();
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 06b99f8..9ac3c553 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -79,6 +79,7 @@
 import android.hardware.display.DisplayViewport;
 import android.hardware.display.DisplayedContentSample;
 import android.hardware.display.DisplayedContentSamplingAttributes;
+import android.hardware.display.HdrConversionMode;
 import android.hardware.display.IDisplayManager;
 import android.hardware.display.IDisplayManagerCallback;
 import android.hardware.display.IVirtualDisplayCallback;
@@ -233,11 +234,17 @@
     private InputManagerInternal mInputManagerInternal;
     private IMediaProjectionManager mProjectionService;
     private DeviceStateManagerInternal mDeviceStateManager;
+    @GuardedBy("mSyncRoot")
     private int[] mUserDisabledHdrTypes = {};
+    private int[] mSupportedHdrOutputType;
+    @GuardedBy("mSyncRoot")
     private boolean mAreUserDisabledHdrTypesAllowed = true;
 
     // Display mode chosen by user.
     private Display.Mode mUserPreferredMode;
+    // HDR conversion mode chosen by user
+    @GuardedBy("mSyncRoot")
+    private HdrConversionMode mHdrConversionMode;
 
     // The synchronization root for the display manager.
     // This lock guards most of the display manager's state.
@@ -880,8 +887,8 @@
     }
 
     private void clearUserDisabledHdrTypesLocked() {
-        mUserDisabledHdrTypes = new int[]{};
         synchronized (mSyncRoot) {
+            mUserDisabledHdrTypes = new int[]{};
             Settings.Global.putString(mContext.getContentResolver(),
                     Settings.Global.USER_DISABLED_HDR_FORMATS, "");
         }
@@ -1904,6 +1911,24 @@
         });
     }
 
+    @GuardedBy("mSyncRoot")
+    private int[] getEnabledAutoHdrTypesLocked() {
+        IntArray autoHdrOutputTypesArray = new IntArray();
+        for (int type : getSupportedHdrOutputTypesInternal()) {
+            boolean isDisabled = false;
+            for (int disabledType : mUserDisabledHdrTypes) {
+                if (type == disabledType) {
+                    isDisabled = true;
+                    break;
+                }
+            }
+            if (!isDisabled) {
+                autoHdrOutputTypesArray.add(type);
+            }
+        }
+        return autoHdrOutputTypesArray.toArray();
+    }
+
     Display.Mode getUserPreferredDisplayModeInternal(int displayId) {
         synchronized (mSyncRoot) {
             if (displayId == Display.INVALID_DISPLAY) {
@@ -1927,6 +1952,38 @@
         }
     }
 
+    // TODO (b/264979880) - Add unit test for HDR output control methods.
+    private void setHdrConversionModeInternal(HdrConversionMode hdrConversionMode) {
+        int[] autoHdrOutputTypes = null;
+        synchronized (mSyncRoot) {
+            mHdrConversionMode = hdrConversionMode;
+
+            // For auto mode, all supported HDR types are allowed except the ones specifically
+            // disabled by the user.
+            if (hdrConversionMode.getConversionMode() == HdrConversionMode.HDR_CONVERSION_SYSTEM) {
+                autoHdrOutputTypes = getEnabledAutoHdrTypesLocked();
+            }
+            DisplayControl.setHdrConversionMode(hdrConversionMode.getConversionMode(),
+                    hdrConversionMode.getPreferredHdrOutputType(), autoHdrOutputTypes);
+        }
+    }
+
+    private HdrConversionMode getHdrConversionModeInternal() {
+        synchronized (mSyncRoot) {
+            if (mHdrConversionMode != null) {
+                return mHdrConversionMode;
+            }
+        }
+        return new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_SYSTEM);
+    }
+
+    private @Display.HdrCapabilities.HdrType int[] getSupportedHdrOutputTypesInternal() {
+        if (mSupportedHdrOutputType == null) {
+            mSupportedHdrOutputType = DisplayControl.getSupportedHdrOutputTypes();
+        }
+        return mSupportedHdrOutputType;
+    }
+
     void setShouldAlwaysRespectAppRequestedModeInternal(boolean enabled) {
         mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled);
     }
@@ -3193,7 +3250,9 @@
 
         @Override // Binder call
         public int[] getUserDisabledHdrTypes() {
-            return mUserDisabledHdrTypes;
+            synchronized (mSyncRoot) {
+                return mUserDisabledHdrTypes;
+            }
         }
 
         @Override // Binder call
@@ -3612,6 +3671,40 @@
         }
 
         @Override // Binder call
+        public void setHdrConversionMode(HdrConversionMode hdrConversionMode) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.MODIFY_HDR_CONVERSION_MODE,
+                    "Permission required to set the HDR conversion mode.");
+            final long token = Binder.clearCallingIdentity();
+            try {
+                setHdrConversionModeInternal(hdrConversionMode);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
+        public HdrConversionMode getHdrConversionMode() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return getHdrConversionModeInternal();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Display.HdrCapabilities.HdrType
+        @Override // Binder call
+        public int[] getSupportedHdrOutputTypes() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return getSupportedHdrOutputTypesInternal();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
         public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
diff --git a/services/core/jni/com_android_server_display_DisplayControl.cpp b/services/core/jni/com_android_server_display_DisplayControl.cpp
index 1859333..ec42324 100644
--- a/services/core/jni/com_android_server_display_DisplayControl.cpp
+++ b/services/core/jni/com_android_server_display_DisplayControl.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <android/gui/IHdrConversionConstants.h>
 #include <android_util_Binder.h>
 #include <gui/SurfaceComposerClient.h>
 #include <jni.h>
@@ -55,6 +56,58 @@
     }
 }
 
+static void nativeSetHdrConversionMode(JNIEnv* env, jclass clazz, jint hdrConversionMode,
+                                       jint preferredHdrOutputType, jintArray autoHdrOutputTypes,
+                                       jint autoHdrOutputTypesLength) {
+    gui::HdrConversionStrategy hdrConversionStrategy;
+    switch (hdrConversionMode) {
+        case gui::IHdrConversionConstants::HdrConversionModePassthrough: {
+            hdrConversionStrategy.set<gui::HdrConversionStrategy::Tag::passthrough>(true);
+            break;
+        }
+        case gui::IHdrConversionConstants::HdrConversionModeAuto: {
+            jint* autoHdrOutputTypesArray = env->GetIntArrayElements(autoHdrOutputTypes, 0);
+            std::vector<int> autoHdrOutputTypesVector(autoHdrOutputTypesLength);
+            for (int i = 0; i < autoHdrOutputTypesLength; i++) {
+                autoHdrOutputTypesVector[i] = autoHdrOutputTypesArray[i];
+            }
+            hdrConversionStrategy.set<gui::HdrConversionStrategy::Tag::autoAllowedHdrTypes>(
+                    autoHdrOutputTypesVector);
+            break;
+        }
+        case gui::IHdrConversionConstants::HdrConversionModeForce: {
+            hdrConversionStrategy.set<gui::HdrConversionStrategy::Tag::forceHdrConversion>(
+                    preferredHdrOutputType);
+            break;
+        }
+    }
+
+    SurfaceComposerClient::setHdrConversionStrategy(hdrConversionStrategy);
+}
+
+static jintArray nativeGetSupportedHdrOutputTypes(JNIEnv* env, jclass clazz) {
+    std::vector<gui::HdrConversionCapability> hdrConversionCapabilities;
+    SurfaceComposerClient::getHdrConversionCapabilities(&hdrConversionCapabilities);
+
+    // Extract unique HDR output types.
+    std::set<int> hdrOutputTypes;
+    for (const auto& hdrConversionCapability : hdrConversionCapabilities) {
+        hdrOutputTypes.insert(hdrConversionCapability.outputType);
+    }
+    jintArray array = env->NewIntArray(hdrOutputTypes.size());
+    if (array == nullptr) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
+        return nullptr;
+    }
+    jint* arrayValues = env->GetIntArrayElements(array, 0);
+    int index = 0;
+    for (auto hdrOutputType : hdrOutputTypes) {
+        arrayValues[index++] = static_cast<jint>(hdrOutputType);
+    }
+    env->ReleaseIntArrayElements(array, arrayValues, 0);
+    return array;
+}
+
 static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
     const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
     ScopedLongArrayRW values(env, env->NewLongArray(displayIds.size()));
@@ -91,6 +144,10 @@
             (void*)nativeGetPhysicalDisplayIds },
     {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
             (void*)nativeGetPhysicalDisplayToken },
+    {"nativeSetHdrConversionMode", "(II[II)V",
+            (void*)nativeSetHdrConversionMode },
+    {"nativeGetSupportedHdrOutputTypes", "()[I",
+            (void*)nativeGetSupportedHdrOutputTypes },
         // clang-format on
 };