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);
}