Merge "Add IColorDisplayManager"
diff --git a/Android.bp b/Android.bp
index b3a2ef8..c568d36 100644
--- a/Android.bp
+++ b/Android.bp
@@ -162,6 +162,7 @@
         "core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl",
         "core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl",
         "core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl",
+        "core/java/android/hardware/display/IColorDisplayManager.aidl",
         "core/java/android/hardware/display/IDisplayManager.aidl",
         "core/java/android/hardware/display/IDisplayManagerCallback.aidl",
         "core/java/android/hardware/display/IVirtualDisplayCallback.aidl",
diff --git a/api/system-current.txt b/api/system-current.txt
index a112bba..0215b2f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -57,6 +57,7 @@
     field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
     field public static final java.lang.String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
     field public static final java.lang.String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
+    field public static final java.lang.String CONTROL_DISPLAY_COLOR_TRANSFORMS = "android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS";
     field public static final java.lang.String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION";
     field public static final java.lang.String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE";
     field public static final java.lang.String CONTROL_KEYGUARD_SECURE_NOTIFICATIONS = "android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS";
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index dfe371c..0404e80d 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -59,6 +59,7 @@
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.camera2.CameraManager;
+import android.hardware.display.ColorDisplayManager;
 import android.hardware.display.DisplayManager;
 import android.hardware.face.FaceManager;
 import android.hardware.face.IFaceService;
@@ -386,6 +387,14 @@
                 return new DisplayManager(ctx.getOuterContext());
             }});
 
+        registerService(Context.COLOR_DISPLAY_SERVICE, ColorDisplayManager.class,
+                new CachedServiceFetcher<ColorDisplayManager>() {
+                    @Override
+                    public ColorDisplayManager createService(ContextImpl ctx) {
+                        return new ColorDisplayManager();
+                    }
+                });
+
         // InputMethodManager has its own cache strategy based on display id to support apps that
         // still assume InputMethodManager is a per-process singleton and it's safe to directly
         // access internal fields via reflection.  Hence directly use ServiceFetcher instead of
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 68aac64..fe11acb 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3126,6 +3126,7 @@
             //@hide: HDMI_CONTROL_SERVICE,
             INPUT_SERVICE,
             DISPLAY_SERVICE,
+            //@hide COLOR_DISPLAY_SERVICE,
             USER_SERVICE,
             RESTRICTIONS_SERVICE,
             APP_OPS_SERVICE,
@@ -4108,6 +4109,16 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.display.ColorDisplayManager} for controlling color transforms.
+     *
+     * @see #getSystemService(String)
+     * @see android.hardware.display.ColorDisplayManager
+     * @hide
+     */
+    public static final String COLOR_DISPLAY_SERVICE = "color_display";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.os.UserManager} for managing users on devices that support multiple users.
      *
      * @see #getSystemService(String)
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index 0a76c2b..a4c1332 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -16,20 +16,81 @@
 
 package android.hardware.display;
 
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
 import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
 
 import com.android.internal.R;
 
 /**
  * Manages the display's color transforms and modes.
+ *
  * @hide
  */
+@SystemService(Context.COLOR_DISPLAY_SERVICE)
 public final class ColorDisplayManager {
 
+    private final ColorDisplayManagerInternal mManager;
+
+    /**
+     * @hide
+     */
+    public ColorDisplayManager() {
+        mManager = ColorDisplayManagerInternal.getInstance();
+    }
+
+    /**
+     * Returns whether the device has a wide color gamut display.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+    public boolean isDeviceColorManaged() {
+        return mManager.isDeviceColorManaged();
+    }
+
     /**
      * Returns {@code true} if Night Display is supported by the device.
      */
     public static boolean isNightDisplayAvailable(Context context) {
         return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable);
     }
+
+    private static class ColorDisplayManagerInternal {
+
+        private static ColorDisplayManagerInternal sInstance;
+
+        private final IColorDisplayManager mCdm;
+
+        private ColorDisplayManagerInternal(IColorDisplayManager colorDisplayManager) {
+            mCdm = colorDisplayManager;
+        }
+
+        public static ColorDisplayManagerInternal getInstance() {
+            synchronized (ColorDisplayManagerInternal.class) {
+                if (sInstance == null) {
+                    try {
+                        IBinder b = ServiceManager.getServiceOrThrow(Context.COLOR_DISPLAY_SERVICE);
+                        sInstance = new ColorDisplayManagerInternal(
+                                IColorDisplayManager.Stub.asInterface(b));
+                    } catch (ServiceNotFoundException e) {
+                        throw new IllegalStateException(e);
+                    }
+                }
+                return sInstance;
+            }
+        }
+
+        boolean isDeviceColorManaged() {
+            try {
+                return mCdm.isDeviceColorManaged();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
 }
diff --git a/core/java/android/hardware/display/IColorDisplayManager.aidl b/core/java/android/hardware/display/IColorDisplayManager.aidl
new file mode 100644
index 0000000..f786589
--- /dev/null
+++ b/core/java/android/hardware/display/IColorDisplayManager.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 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;
+
+/** @hide */
+interface IColorDisplayManager {
+    boolean isDeviceColorManaged();
+}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index df17b4c..3018614 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3387,6 +3387,13 @@
     <permission android:name="android.permission.CONTROL_DISPLAY_SATURATION"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows an application to control display color transformations.
+         <p>Not for use by third-party applications.</p>
+         @hide
+         @SystemApi -->
+    <permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Allows an application to collect usage infomation about brightness slider changes.
          <p>Not for use by third-party applications.</p>
          @hide
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 9e4ea32..f237344 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -281,6 +281,7 @@
         <permission name="android.permission.WRITE_APN_SETTINGS"/>
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.settings.intelligence">
@@ -408,6 +409,7 @@
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
         <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
+        <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.tv">
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 0b9b27f..7d53c2f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -223,6 +223,9 @@
 
     <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" />
 
+    <!-- Permission to change the display color -->
+    <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
+
     <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 0b6786c..521fa23 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display;
 
+import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.TypeEvaluator;
@@ -29,8 +31,10 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.ContentObserver;
+import android.hardware.display.IColorDisplayManager;
 import android.net.Uri;
 import android.opengl.Matrix;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
@@ -39,6 +43,7 @@
 import android.util.Slog;
 import android.view.animation.AnimationUtils;
 
+import com.android.internal.R;
 import com.android.internal.app.ColorDisplayController;
 import com.android.server.SystemService;
 import com.android.server.twilight.TwilightListener;
@@ -49,12 +54,8 @@
 import java.time.LocalTime;
 import java.time.ZoneId;
 
-import com.android.internal.R;
-
-import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
-
 /**
- * Tints the display at night.
+ * Controls the display's color transforms.
  */
 public final class ColorDisplayService extends SystemService
         implements ColorDisplayController.Callback {
@@ -101,7 +102,7 @@
 
     @Override
     public void onStart() {
-        // Nothing to publish.
+        publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
     }
 
     @Override
@@ -171,7 +172,7 @@
                     }
                 };
                 cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
-                        false /* notifyForDescendents */, mUserSetupObserver, mCurrentUser);
+                        false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
             } else if (mBootCompleted) {
                 setUp();
             }
@@ -405,8 +406,8 @@
     }
 
     /**
-     * Returns the first date time corresponding to the local time that occurs before the
-     * provided date time.
+     * Returns the first date time corresponding to the local time that occurs before the provided
+     * date time.
      *
      * @param compareTime the LocalDateTime to compare against
      * @return the prior LocalDateTime corresponding to this local time
@@ -420,8 +421,8 @@
     }
 
     /**
-     * Returns the first date time corresponding to this local time that occurs after the
-     * provided date time.
+     * Returns the first date time corresponding to this local time that occurs after the provided
+     * date time.
      *
      * @param compareTime the LocalDateTime to compare against
      * @return the next LocalDateTime corresponding to this local time
@@ -434,6 +435,11 @@
         return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
     }
 
+    private boolean isDeviceColorManagedInternal() {
+        final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+        return dtm.isDeviceColorManaged();
+    }
+
     private abstract class AutoMode implements ColorDisplayController.Callback {
         public abstract void onStart();
 
@@ -616,4 +622,16 @@
             return mResultMatrix;
         }
     }
+
+    private final class BinderService extends IColorDisplayManager.Stub {
+        @Override
+        public boolean isDeviceColorManaged() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return isDeviceColorManagedInternal();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index d6931e0..5ca1755 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -16,7 +16,6 @@
 
 package com.android.server.display;
 
-import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.opengl.Matrix;
 import android.os.IBinder;
@@ -27,8 +26,10 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.ColorDisplayController;
+
 import java.util.Arrays;
 
 /**
@@ -59,10 +60,6 @@
 
     private static final int SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX = 1015;
     private static final int SURFACE_FLINGER_TRANSACTION_DALTONIZER = 1014;
-
-    private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation";
-    private static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode";
-
     /**
      * SurfaceFlinger global saturation factor.
      */
@@ -71,6 +68,10 @@
      * SurfaceFlinger display color (managed, unmanaged, etc.).
      */
     private static final int SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR = 1023;
+    private static final int SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR = 1030;
+
+    private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation";
+    private static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode";
 
     private static final float COLOR_SATURATION_NATURAL = 1.0f;
     private static final float COLOR_SATURATION_BOOSTED = 1.1f;
@@ -269,6 +270,29 @@
     }
 
     /**
+     * Returns whether the screen is wide color gamut via SurfaceFlinger's
+     * {@link #SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR}.
+     */
+    public boolean isDeviceColorManaged() {
+        final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER);
+        if (flinger != null) {
+            final Parcel data = Parcel.obtain();
+            final Parcel reply = Parcel.obtain();
+            data.writeInterfaceToken("android.ui.ISurfaceComposer");
+            try {
+                flinger.transact(SURFACE_FLINGER_TRANSACTION_QUERY_WIDE_COLOR, data, reply, 0);
+                return reply.readBoolean();
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Failed to query wide color support", ex);
+            } finally {
+                data.recycle();
+                reply.recycle();
+            }
+        }
+        return false;
+    }
+
+    /**
      * Propagates the provided saturation to the SurfaceFlinger.
      */
     private void applySaturation(float saturation) {
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 746c453..cf4d3a8 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -66,6 +66,7 @@
     <uses-permission android:name="android.permission.SUSPEND_APPS"/>
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
     <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
+    <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
 
     <!-- Uses API introduced in O (26) -->
     <uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
index 53711a6..e0ecd3e 100644
--- a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
@@ -35,6 +35,7 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.R;
 import com.android.internal.app.ColorDisplayController;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.LocalServices;
@@ -911,7 +912,11 @@
         startService();
         assertAccessibilityTransformActivated(true /* activated */ );
         assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
-        assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+        if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) {
+            assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+        } else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) {
+            assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
+        }
     }
 
     @Test
@@ -926,7 +931,11 @@
         startService();
         assertAccessibilityTransformActivated(true /* activated */ );
         assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
-        assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+        if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) {
+            assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+        } else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) {
+            assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
+        }
     }
 
     @Test
@@ -942,7 +951,11 @@
         startService();
         assertAccessibilityTransformActivated(true /* activated */ );
         assertUserColorMode(ColorDisplayController.COLOR_MODE_NATURAL);
-        assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+        if (isColorModeValid(ColorDisplayController.COLOR_MODE_SATURATED)) {
+            assertActiveColorMode(ColorDisplayController.COLOR_MODE_SATURATED);
+        } else if (isColorModeValid(ColorDisplayController.COLOR_MODE_AUTOMATIC)) {
+            assertActiveColorMode(ColorDisplayController.COLOR_MODE_AUTOMATIC);
+        }
     }
 
     @Test
@@ -1030,6 +1043,24 @@
     }
 
     /**
+     * Returns whether the color mode is valid on the device the tests are running on.
+     *
+     * @param mode the mode to check
+     */
+    private boolean isColorModeValid(int mode) {
+        final int[] availableColorModes = mContext.getResources().getIntArray(
+            R.array.config_availableColorModes);
+        if (availableColorModes != null) {
+            for (int availableMode : availableColorModes) {
+                if (mode == availableMode) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
      * Convenience method to start {@link #mColorDisplayService}.
      */
     private void startService() {
@@ -1038,7 +1069,6 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                mColorDisplayService.onStart();
                 mColorDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
                 mColorDisplayService.onStartUser(mUserId);
             }