Add IColorDisplayManager

- Add IColorDisplayManager
- Add CONTROL_DISPLAY_COLOR_TRANSFORMS permission

Bug: 111215474
Test: atest FrameworksServicesTest:ColorDisplayServiceTest
Change-Id: Ia8182ccc80c1733f00c62b136e7950e2d2092d75
This commit is contained in:
Christine Franks 2018-07-03 14:46:07 -07:00
parent cc64dadb05
commit 39b0311db8
13 changed files with 210 additions and 20 deletions

View File

@ -162,6 +162,7 @@ java_defaults {
"core/java/android/hardware/biometrics/IBiometricService.aidl",
"core/java/android/hardware/biometrics/IBiometricServiceReceiver.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",

View File

@ -56,6 +56,7 @@ package android {
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";

View File

@ -59,6 +59,7 @@ import android.hardware.SystemSensorManager;
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;
@ -385,6 +386,14 @@ final class SystemServiceRegistry {
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

View File

@ -3120,6 +3120,7 @@ public abstract class Context {
//@hide: HDMI_CONTROL_SERVICE,
INPUT_SERVICE,
DISPLAY_SERVICE,
//@hide COLOR_DISPLAY_SERVICE,
USER_SERVICE,
RESTRICTIONS_SERVICE,
APP_OPS_SERVICE,
@ -4099,6 +4100,16 @@ public abstract class Context {
*/
public static final String DISPLAY_SERVICE = "display";
/**
* 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.

View File

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

View File

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

View File

@ -3321,6 +3321,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

View File

@ -281,6 +281,7 @@ applications that come with the platform
<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 @@ applications that come with the platform
<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">

View File

@ -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" />

View File

@ -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.Context;
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.MathUtils;
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.LocalDateTime;
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 @@ public final class ColorDisplayService extends SystemService
@Override
public void onStart() {
// Nothing to publish.
publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
}
@Override
@ -171,7 +172,7 @@ public final class ColorDisplayService extends SystemService
}
};
cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
false /* notifyForDescendents */, mUserSetupObserver, mCurrentUser);
false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
} else if (mBootCompleted) {
setUp();
}
@ -405,8 +406,8 @@ public final class ColorDisplayService extends SystemService
}
/**
* 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 @@ public final class ColorDisplayService extends SystemService
}
/**
* 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 @@ public final class ColorDisplayService extends SystemService
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 @@ public final class ColorDisplayService extends SystemService
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);
}
}
}
}

View File

@ -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.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.ColorDisplayController;
import java.util.Arrays;
/**
@ -59,10 +60,6 @@ public class DisplayTransformManager {
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 @@ public class DisplayTransformManager {
* 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;
@ -268,6 +269,29 @@ public class DisplayTransformManager {
return true;
}
/**
* 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.
*/

View File

@ -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"

View File

@ -35,6 +35,7 @@ import android.test.mock.MockContentResolver;
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 @@ public class ColorDisplayServiceTest {
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 @@ public class ColorDisplayServiceTest {
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 @@ public class ColorDisplayServiceTest {
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
@ -1029,6 +1042,24 @@ public class ColorDisplayServiceTest {
mColorDisplayController.setColorMode(mode);
}
/**
* 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}.
*/
@ -1038,7 +1069,6 @@ public class ColorDisplayServiceTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
mColorDisplayService.onStart();
mColorDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
mColorDisplayService.onStartUser(mUserId);
}