Add configurable mapping between color modes

There are two categories of color modes: the standard color modes, which
are defined and implemented in AOSP, and a reserved range of vendor color
modes, which are defined and implemented by the vendor. Currently, there
is no way to distinguish between two vendor's color modes that use the
same value within the reserved range. This will allow OEMs to specify a
hint as to which vendor's color mode definitions were used on the
original device. If the hint from the restored device matches the hint
on the new device, the color mode from the restored device can still be
considered valid for the new device if it is within the vendor reserved
range. If it does not match, the restored device's color mode value is
invalid on the new device, and must be replaced by the default on the
new device.

Bug: 167449433
Test: builds
Change-Id: Ie5ac0a2d783e1ea703e0342f5cdb41dca07d04d4
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index 90d312e..efeb89e 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -357,6 +357,18 @@
     }
 
     /**
+     * Returns whether the specified color mode is part of the standard set.
+     *
+     * @hide
+     */
+    public static boolean isStandardColorMode(int mode) {
+        return mode == ColorDisplayManager.COLOR_MODE_NATURAL
+                || mode == ColorDisplayManager.COLOR_MODE_BOOSTED
+                || mode == ColorDisplayManager.COLOR_MODE_SATURATED
+                || mode == ColorDisplayManager.COLOR_MODE_AUTOMATIC;
+    }
+
+    /**
      * Returns whether the device has a wide color gamut display.
      *
      * @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 27ba72b..f570295 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3984,6 +3984,14 @@
         public static final String DISPLAY_COLOR_MODE = "display_color_mode";
 
         /**
+         * Hint to decide whether restored vendor color modes are compatible with the new device. If
+         * unset or a match is not made, only the standard color modes will be restored.
+         * @hide
+         */
+        public static final String DISPLAY_COLOR_MODE_VENDOR_HINT =
+                "display_color_mode_vendor_hint";
+
+        /**
          * The user selected min refresh rate in frames per second.
          *
          * If this isn't set, 0 will be used.
@@ -4941,6 +4949,7 @@
             PRIVATE_SETTINGS.add(EGG_MODE);
             PRIVATE_SETTINGS.add(SHOW_BATTERY_PERCENT);
             PRIVATE_SETTINGS.add(DISPLAY_COLOR_MODE);
+            PRIVATE_SETTINGS.add(DISPLAY_COLOR_MODE_VENDOR_HINT);
         }
 
         /**
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7f6053e..d4bb00f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -898,6 +898,21 @@
         -->
     </integer-array>
 
+    <!-- Mapping for default ColorDisplayManager.COLOR_MODE_xxx to other color modes, if
+         if applicable. By default, all map to the same value. -->
+    <integer-array name="config_mappedColorModes">
+        <item>0</item> <!-- COLOR_MODE_NATURAL -->
+        <item>1</item> <!-- COLOR_MODE_BOOSTED -->
+        <item>2</item> <!-- COLOR_MODE_SATURATED -->
+        <item>3</item> <!-- COLOR_MODE_AUTOMATIC -->
+    </integer-array>
+
+    <!-- Hint to decide whether restored vendor color modes are compatible with the new device. If
+         unset or a match is not made, only the standard color modes will be restored. If set, it
+         should be a unique identifier for the kinds of vendor modes this device supports, such as a
+         manufacturer name. -->
+    <string name="config_vendorColorModesRestoreHint" translatable="false"></string>
+
     <!-- Color mode to use when accessibility transforms are enabled. This color mode must be
          supported by the device, but not necessarily appear in config_availableColorModes. The
          regularly selected color mode will be used if this value is negative. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e2271d1..ff4ecd9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3194,6 +3194,8 @@
   <java-symbol type="array" name="config_reduceBrightColorsCoefficients" />
   <java-symbol type="array" name="config_reduceBrightColorsCoefficientsNative" />
   <java-symbol type="array" name="config_availableColorModes" />
+  <java-symbol type="array" name="config_mappedColorModes" />
+  <java-symbol type="string" name="config_vendorColorModesRestoreHint" />
   <java-symbol type="integer" name="config_accessibilityColorMode" />
   <java-symbol type="array" name="config_displayCompositionColorModes" />
   <java-symbol type="array" name="config_displayCompositionColorSpaces" />
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index c4330e1..30fd12b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -81,6 +81,7 @@
         Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
         Settings.System.RING_VIBRATION_INTENSITY,
         Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+        Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE
         Settings.System.DISPLAY_COLOR_MODE,
         Settings.System.ALARM_ALERT,
         Settings.System.NOTIFICATION_LIGHT_PULSE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 86aa214..ebf811f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -16,6 +16,7 @@
 
 package android.provider.settings.validators;
 
+import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.DATE_FORMAT_VALIDATOR;
@@ -109,6 +110,7 @@
                         }
                     }
                 });
+        VALIDATORS.put(System.DISPLAY_COLOR_MODE_VENDOR_HINT, ANY_STRING_VALIDATOR);
         VALIDATORS.put(System.SCREEN_OFF_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(System.SCREEN_BRIGHTNESS_FOR_VR, new InclusiveIntegerRangeValidator(0, 255));
         VALIDATORS.put(System.SCREEN_BRIGHTNESS_MODE, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index bfd5b1cc..c577868 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.hardware.display.ColorDisplayManager;
 import android.icu.util.ULocale;
 import android.media.AudioManager;
 import android.media.RingtoneManager;
@@ -161,6 +162,27 @@
                     || Settings.System.ALARM_ALERT.equals(name)) {
                 setRingtone(name, value);
                 return;
+            } else if (Settings.System.DISPLAY_COLOR_MODE.equals(name)) {
+                int mode = Integer.parseInt(value);
+                String restoredVendorHint = Settings.System.getString(mContext.getContentResolver(),
+                        Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT);
+                final String deviceVendorHint = mContext.getResources().getString(
+                        com.android.internal.R.string.config_vendorColorModesRestoreHint);
+                boolean displayColorModeVendorModeHintsMatch =
+                        !TextUtils.isEmpty(deviceVendorHint)
+                                && deviceVendorHint.equals(restoredVendorHint);
+                // Replace vendor hint with new device's vendor hint.
+                contentValues.clear();
+                contentValues.put(Settings.NameValueTable.NAME,
+                        Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT);
+                contentValues.put(Settings.NameValueTable.VALUE, deviceVendorHint);
+                cr.insert(destination, contentValues);
+                // If vendor hints match, modes in the vendor range can be restored. Otherwise, only
+                // map standard modes.
+                if (!ColorDisplayManager.isStandardColorMode(mode)
+                        && !displayColorModeVendorModeHintsMatch) {
+                    return;
+                }
             }
 
             // Default case: write the restored value to settings
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 727944d..4706edc 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -917,13 +917,18 @@
         // This happens when a color mode is no longer available (e.g., after system update or B&R)
         // or the device does not support any color mode.
         if (!isColorModeAvailable(colorMode)) {
-            if (colorMode == COLOR_MODE_BOOSTED && isColorModeAvailable(COLOR_MODE_NATURAL)) {
+            final int[] mappedColorModes = getContext().getResources().getIntArray(
+                    R.array.config_mappedColorModes);
+            if (colorMode == COLOR_MODE_BOOSTED && mappedColorModes.length > COLOR_MODE_NATURAL
+                    && isColorModeAvailable(mappedColorModes[COLOR_MODE_NATURAL])) {
                 colorMode = COLOR_MODE_NATURAL;
             } else if (colorMode == COLOR_MODE_SATURATED
-                    && isColorModeAvailable(COLOR_MODE_AUTOMATIC)) {
+                    && mappedColorModes.length > COLOR_MODE_AUTOMATIC
+                    && isColorModeAvailable(mappedColorModes[COLOR_MODE_AUTOMATIC])) {
                 colorMode = COLOR_MODE_AUTOMATIC;
             } else if (colorMode == COLOR_MODE_AUTOMATIC
-                    && isColorModeAvailable(COLOR_MODE_SATURATED)) {
+                    && mappedColorModes.length > COLOR_MODE_SATURATED
+                    && isColorModeAvailable(mappedColorModes[COLOR_MODE_SATURATED])) {
                 colorMode = COLOR_MODE_SATURATED;
             } else {
                 colorMode = -1;