Support native and srgb for night display

Bug: 68159303
Test: make -j100

Merged-In: Iea4b38bd8c9037f50b7ffa6e3c4f12b0e536a8ce
Change-Id: Iea4b38bd8c9037f50b7ffa6e3c4f12b0e536a8ce
(cherry picked from commit cf74d28888d8016051402ad7a6335de5c2b27b2f)
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2d8ed7a..49e3037 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3047,6 +3047,12 @@
         private static final Validator DIM_SCREEN_VALIDATOR = sBooleanValidator;
 
         /**
+         * The display color mode.
+         * @hide
+         */
+        public static final String DISPLAY_COLOR_MODE = "display_color_mode";
+
+        /**
          * The amount of time in milliseconds before the device goes to sleep or begins
          * to dream after a period of inactivity.  This value is also known as the
          * user activity timeout period since the screen isn't necessarily turned off
diff --git a/core/java/com/android/internal/app/NightDisplayController.java b/core/java/com/android/internal/app/NightDisplayController.java
index 860c5c4..78a8b17 100644
--- a/core/java/com/android/internal/app/NightDisplayController.java
+++ b/core/java/com/android/internal/app/NightDisplayController.java
@@ -26,6 +26,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings.Secure;
+import android.provider.Settings.System;
 import android.util.Slog;
 
 import com.android.internal.R;
@@ -50,6 +51,10 @@
     @IntDef({ AUTO_MODE_DISABLED, AUTO_MODE_CUSTOM, AUTO_MODE_TWILIGHT })
     public @interface AutoMode {}
 
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ COLOR_MODE_NATURAL, COLOR_MODE_BOOSTED, COLOR_MODE_SATURATED })
+    public @interface ColorMode {}
+
     /**
      * Auto mode value to prevent Night display from being automatically activated. It can still
      * be activated manually via {@link #setActivated(boolean)}.
@@ -72,6 +77,25 @@
      */
     public static final int AUTO_MODE_TWILIGHT = 2;
 
+    /**
+     * Color mode with natural colors.
+     *
+     * @see #setColorMode(int)
+     */
+    public static final int COLOR_MODE_NATURAL = 0;
+    /**
+     * Color mode with boosted colors.
+     *
+     * @see #setColorMode(int)
+     */
+    public static final int COLOR_MODE_BOOSTED = 1;
+    /**
+     * Color mode with saturated colors.
+     *
+     * @see #setColorMode(int)
+     */
+    public static final int COLOR_MODE_SATURATED = 2;
+
     private final Context mContext;
     private final int mUserId;
 
@@ -117,7 +141,8 @@
     public boolean setActivated(boolean activated) {
         if (isActivated() != activated) {
             Secure.putLongForUser(mContext.getContentResolver(),
-                    Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, System.currentTimeMillis(),
+                    Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+                    java.lang.System.currentTimeMillis(),
                     mUserId);
         }
         return Secure.putIntForUser(mContext.getContentResolver(),
@@ -294,6 +319,31 @@
     }
 
     /**
+     * Get the current color mode.
+     */
+    public int getColorMode() {
+        final int colorMode = System.getIntForUser(mContext.getContentResolver(),
+            System.DISPLAY_COLOR_MODE, COLOR_MODE_BOOSTED, mUserId);
+        if (colorMode < COLOR_MODE_NATURAL || colorMode > COLOR_MODE_SATURATED) {
+            return COLOR_MODE_BOOSTED;
+        }
+        return colorMode;
+    }
+
+    /**
+     * Set the current color mode.
+     *
+     * @param colorMode the color mode
+     */
+    public void setColorMode(@ColorMode int colorMode) {
+        if (colorMode < COLOR_MODE_NATURAL || colorMode > COLOR_MODE_SATURATED) {
+            return;
+        }
+        System.putIntForUser(mContext.getContentResolver(), System.DISPLAY_COLOR_MODE, colorMode,
+                mUserId);
+    }
+
+    /**
      * Returns the minimum allowed color temperature (in Kelvin) to tint the display when activated.
      */
     public int getMinimumColorTemperature() {
@@ -339,6 +389,9 @@
                 case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
                     mCallback.onColorTemperatureChanged(getColorTemperature());
                     break;
+                case System.DISPLAY_COLOR_MODE:
+                    mCallback.onDisplayColorModeChanged(getColorMode());
+                    break;
             }
         }
     }
@@ -367,6 +420,8 @@
                         false /* notifyForDescendants */, mContentObserver, mUserId);
                 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
                         false /* notifyForDescendants */, mContentObserver, mUserId);
+                cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
+                        false /* notifyForDecendants */, mContentObserver, mUserId);
             }
         }
     }
@@ -513,5 +568,12 @@
          * @param colorTemperature the color temperature to tint the screen
          */
         default void onColorTemperatureChanged(int colorTemperature) {}
+
+        /**
+         * Callback invoked when the color mode changes.
+         *
+         * @param displayColorMode the color mode
+         */
+        default void onDisplayColorModeChanged(int displayColorMode) {}
     }
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d1b3fec..bffab8b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -844,6 +844,30 @@
     <!-- Maximum color temperature, in Kelvin, supported by Night display. -->
     <integer name="config_nightDisplayColorTemperatureMax">4082</integer>
 
+    <string-array name="config_nightDisplayColorTemperatureCoefficientsNative">
+        <!-- R a-coefficient --> <item>0.0</item>
+        <!-- R b-coefficient --> <item>0.0</item>
+        <!-- R y-intercept --> <item>1.0</item>
+        <!-- G a-coefficient --> <item>-0.00000000962353339</item>
+        <!-- G b-coefficient --> <item>0.000153045476</item>
+        <!-- G y-intercept --> <item>0.390782778</item>
+        <!-- B a-coefficient --> <item>-0.0000000189359041</item>
+        <!-- B b-coefficient --> <item>0.000302412211</item>
+        <!-- B y-intercept --> <item>-0.198650895</item>
+    </string-array>
+
+    <string-array name="config_nightDisplayColorTemperatureCoefficients">
+        <!-- R a-coefficient --> <item>0.0</item>
+        <!-- R b-coefficient --> <item>0.0</item>
+        <!-- R y-intercept --> <item>1.0</item>
+        <!-- G a-coefficient --> <item>-0.00000000962353339</item>
+        <!-- G b-coefficient --> <item>0.000153045476</item>
+        <!-- G y-intercept --> <item>0.390782778</item>
+        <!-- B a-coefficient --> <item>-0.0000000189359041</item>
+        <!-- B b-coefficient --> <item>0.000302412211</item>
+        <!-- B y-intercept --> <item>-0.198650895</item>
+    </string-array>
+
     <!-- Indicate whether to allow the device to suspend when the screen is off
          due to the proximity sensor.  This resource should only be set to true
          if the sensor HAL correctly handles the proximity sensor as a wake-up source.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 975737b..6209830 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2815,6 +2815,8 @@
   <java-symbol type="integer" name="config_nightDisplayColorTemperatureDefault" />
   <java-symbol type="integer" name="config_nightDisplayColorTemperatureMin" />
   <java-symbol type="integer" name="config_nightDisplayColorTemperatureMax" />
+  <java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficients" />
+  <java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficientsNative" />
 
   <!-- Default first user restrictions -->
   <java-symbol type="array" name="config_defaultFirstUserRestrictions" />
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index dbbb318..2aba871 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -16,15 +16,20 @@
 
 package com.android.server.display;
 
+import android.app.ActivityManager;
+import android.app.IActivityManager;
 import android.opengl.Matrix;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import com.android.internal.annotations.GuardedBy;
 
+import com.android.internal.app.NightDisplayController;
 import java.util.Arrays;
 
 /**
@@ -50,6 +55,17 @@
     private static final int SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX = 1015;
     private static final int SURFACE_FLINGER_TRANSACTION_DALTONIZER = 1014;
 
+    static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation";
+
+    static final String PERSISTENT_PROPERTY_NATIVE_MODE = "persist.sys.sf.native_mode";
+
+    private static final int SURFACE_FLINGER_TRANSACTION_SATURATION = 1022;
+    private static final int SURFACE_FLINGER_TRANSACTION_NATIVE_MODE = 1023;
+
+    static final float COLOR_SATURATION_NATURAL = 1.0f;
+
+    static final float COLOR_SATURATION_BOOSTED = 1.1f;
+
     /**
      * Map of level -> color transformation matrix.
      */
@@ -68,9 +84,18 @@
     @GuardedBy("mDaltonizerModeLock")
     private int mDaltonizerMode = -1;
 
+    private IBinder mSurfaceFlinger;
+
     /* package */ DisplayTransformManager() {
     }
 
+    public IBinder getSurfaceFlinger() {
+        if (mSurfaceFlinger == null) {
+            mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger");
+        }
+        return mSurfaceFlinger;
+    }
+
     /**
      * Returns a copy of the color transform matrix set for a given level.
      */
@@ -201,4 +226,71 @@
             }
         }
     }
+
+    public static boolean isNativeModeEnabled() {
+        return SystemProperties.getBoolean(PERSISTENT_PROPERTY_NATIVE_MODE, false);
+    }
+
+    public boolean setColorMode(int colorMode) {
+        if (colorMode == NightDisplayController.COLOR_MODE_NATURAL) {
+            applySaturation(COLOR_SATURATION_NATURAL);
+            setNativeMode(false);
+        } else if (colorMode == NightDisplayController.COLOR_MODE_BOOSTED) {
+            applySaturation(COLOR_SATURATION_BOOSTED);
+            setNativeMode(false);
+        } else if (colorMode == NightDisplayController.COLOR_MODE_SATURATED) {
+            applySaturation(COLOR_SATURATION_NATURAL);
+            setNativeMode(true);
+        }
+
+        updateConfiguration();
+
+        return true;
+    }
+
+    /**
+     * Propagates the provided saturation to the SurfaceFlinger.
+     */
+    private void applySaturation(float saturation) {
+        SystemProperties.set(PERSISTENT_PROPERTY_SATURATION, Float.toString(saturation));
+        if (getSurfaceFlinger() != null) {
+            final Parcel data = Parcel.obtain();
+            data.writeInterfaceToken("android.ui.ISurfaceComposer");
+            data.writeFloat(saturation);
+            try {
+                getSurfaceFlinger().transact(SURFACE_FLINGER_TRANSACTION_SATURATION, data, null, 0);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Failed to set saturation", ex);
+            } finally {
+                data.recycle();
+            }
+        }
+    }
+
+    /**
+     * Toggles native mode on/off in SurfaceFlinger.
+     */
+    private void setNativeMode(boolean enabled) {
+        SystemProperties.set(PERSISTENT_PROPERTY_NATIVE_MODE, enabled ? "1" : "0");
+        if (getSurfaceFlinger() != null) {
+            final Parcel data = Parcel.obtain();
+            data.writeInterfaceToken("android.ui.ISurfaceComposer");
+            data.writeInt(enabled ? 1 : 0);
+            try {
+                getSurfaceFlinger().transact(SURFACE_FLINGER_TRANSACTION_NATIVE_MODE, data, null, 0);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Failed to set native mode", ex);
+            } finally {
+                data.recycle();
+            }
+        }
+    }
+
+    void updateConfiguration() {
+        try {
+            ActivityManager.getService().updateConfiguration(null);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not update configuration", e);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/NightDisplayService.java b/services/core/java/com/android/server/display/NightDisplayService.java
index 026921d..eeee768 100644
--- a/services/core/java/com/android/server/display/NightDisplayService.java
+++ b/services/core/java/com/android/server/display/NightDisplayService.java
@@ -52,6 +52,8 @@
 import java.util.Calendar;
 import java.util.TimeZone;
 
+import com.android.internal.R;
+
 import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
 
 /**
@@ -110,19 +112,7 @@
 
     private float[] mMatrixNight = new float[16];
 
-    /**
-     *  The 3x3 color transformation matrix is formatted like so:
-     *  <table>
-     *      <tr><td>R: a coefficient</td><td>G: a coefficient</td><td>B: a coefficient</td></tr>
-     *      <tr><td>R: b coefficient</td><td>G: b coefficient</td><td>B: b coefficient</td></tr>
-     *      <tr><td>R: y-intercept</td><td>G: y-intercept</td><td>B: y-intercept</td></tr>
-     *  </table>
-     */
-    private static final float[] mColorTempCoefficients = new float[] {
-            0.0f, -0.000000014365268757f, -0.000000000910931179f,
-            0.0f, 0.000255092801250106f, 0.000207598323269139f,
-            1.0f, -0.064156942434907716f, -0.349361641294833436f
-    };
+    private final float[] mColorTempCoefficients = new float[9];
 
     private int mCurrentUser = UserHandle.USER_NULL;
     private ContentObserver mUserSetupObserver;
@@ -240,6 +230,8 @@
         mController = new NightDisplayController(getContext(), mCurrentUser);
         mController.setListener(this);
 
+        setCoefficientMatrix(getContext());
+
         // Prepare color transformation matrix.
         setMatrix(mController.getColorTemperature(), mMatrixNight);
 
@@ -335,6 +327,28 @@
         applyTint(true);
     }
 
+    @Override
+    public void onDisplayColorModeChanged(int colorMode) {
+        final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+        dtm.setColorMode(colorMode);
+
+        setCoefficientMatrix(getContext());
+        setMatrix(mController.getColorTemperature(), mMatrixNight);
+        if (mController.isActivated()) {
+            applyTint(true);
+        }
+    }
+
+    private void setCoefficientMatrix(Context context) {
+        final boolean isNative = DisplayTransformManager.isNativeModeEnabled();
+        final String[] coefficients = context.getResources().getStringArray(isNative ?
+            R.array.config_nightDisplayColorTemperatureCoefficientsNative
+            : R.array.config_nightDisplayColorTemperatureCoefficients);
+        for (int i = 0; i < 9 && i < coefficients.length; i++) {
+            mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
+        }
+    }
+
     /**
      * Applies current color temperature matrix, or removes it if deactivated.
      *
@@ -410,11 +424,11 @@
 
         final float squareTemperature = colorTemperature * colorTemperature;
         final float red = squareTemperature * mColorTempCoefficients[0]
-                + colorTemperature * mColorTempCoefficients[3] + mColorTempCoefficients[6];
-        final float green = squareTemperature * mColorTempCoefficients[1]
-                + colorTemperature * mColorTempCoefficients[4] + mColorTempCoefficients[7];
-        final float blue = squareTemperature * mColorTempCoefficients[2]
-                + colorTemperature * mColorTempCoefficients[5] + mColorTempCoefficients[8];
+                + colorTemperature * mColorTempCoefficients[1] + mColorTempCoefficients[2];
+        final float green = squareTemperature * mColorTempCoefficients[3]
+                + colorTemperature * mColorTempCoefficients[4] + mColorTempCoefficients[5];
+        final float blue = squareTemperature * mColorTempCoefficients[6]
+                + colorTemperature * mColorTempCoefficients[7] + mColorTempCoefficients[8];
         outTemp[0] = red;
         outTemp[5] = green;
         outTemp[10] = blue;