1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.display.color; 18 19 import static android.hardware.display.ColorDisplayManager.AUTO_MODE_CUSTOM_TIME; 20 import static android.hardware.display.ColorDisplayManager.AUTO_MODE_DISABLED; 21 import static android.hardware.display.ColorDisplayManager.AUTO_MODE_TWILIGHT; 22 import static android.hardware.display.ColorDisplayManager.COLOR_MODE_AUTOMATIC; 23 import static android.hardware.display.ColorDisplayManager.COLOR_MODE_BOOSTED; 24 import static android.hardware.display.ColorDisplayManager.COLOR_MODE_NATURAL; 25 import static android.hardware.display.ColorDisplayManager.COLOR_MODE_SATURATED; 26 import static android.hardware.display.ColorDisplayManager.VENDOR_COLOR_MODE_RANGE_MAX; 27 import static android.hardware.display.ColorDisplayManager.VENDOR_COLOR_MODE_RANGE_MIN; 28 29 import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY; 30 31 import android.Manifest; 32 import android.animation.Animator; 33 import android.animation.AnimatorListenerAdapter; 34 import android.animation.TypeEvaluator; 35 import android.animation.ValueAnimator; 36 import android.annotation.NonNull; 37 import android.annotation.Nullable; 38 import android.annotation.Size; 39 import android.annotation.UserIdInt; 40 import android.app.AlarmManager; 41 import android.content.BroadcastReceiver; 42 import android.content.ContentResolver; 43 import android.content.Context; 44 import android.content.Intent; 45 import android.content.IntentFilter; 46 import android.content.pm.PackageManager; 47 import android.content.pm.PackageManagerInternal; 48 import android.content.res.Resources; 49 import android.database.ContentObserver; 50 import android.hardware.display.ColorDisplayManager; 51 import android.hardware.display.ColorDisplayManager.AutoMode; 52 import android.hardware.display.ColorDisplayManager.ColorMode; 53 import android.hardware.display.IColorDisplayManager; 54 import android.hardware.display.Time; 55 import android.net.Uri; 56 import android.opengl.Matrix; 57 import android.os.Binder; 58 import android.os.Handler; 59 import android.os.Looper; 60 import android.os.Message; 61 import android.os.ParcelFileDescriptor; 62 import android.os.SystemProperties; 63 import android.os.UserHandle; 64 import android.provider.Settings.Secure; 65 import android.provider.Settings.System; 66 import android.util.MathUtils; 67 import android.util.Slog; 68 import android.util.SparseIntArray; 69 import android.view.Display; 70 import android.view.SurfaceControl; 71 import android.view.accessibility.AccessibilityManager; 72 import android.view.animation.AnimationUtils; 73 74 import com.android.internal.R; 75 import com.android.internal.annotations.VisibleForTesting; 76 import com.android.internal.util.DumpUtils; 77 import com.android.server.DisplayThread; 78 import com.android.server.LocalServices; 79 import com.android.server.SystemService; 80 import com.android.server.twilight.TwilightListener; 81 import com.android.server.twilight.TwilightManager; 82 import com.android.server.twilight.TwilightState; 83 84 import java.io.FileDescriptor; 85 import java.io.PrintWriter; 86 import java.lang.ref.WeakReference; 87 import java.time.DateTimeException; 88 import java.time.Instant; 89 import java.time.LocalDateTime; 90 import java.time.LocalTime; 91 import java.time.ZoneId; 92 import java.time.format.DateTimeParseException; 93 94 /** 95 * Controls the display's color transforms. 96 */ 97 public final class ColorDisplayService extends SystemService { 98 99 static final String TAG = "ColorDisplayService"; 100 101 /** 102 * The identity matrix, used if one of the given matrices is {@code null}. 103 */ 104 static final float[] MATRIX_IDENTITY = new float[16]; 105 106 static { Matrix.setIdentityM(MATRIX_IDENTITY, 0)107 Matrix.setIdentityM(MATRIX_IDENTITY, 0); 108 } 109 110 private static final int MSG_USER_CHANGED = 0; 111 private static final int MSG_SET_UP = 1; 112 private static final int MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE = 2; 113 private static final int MSG_APPLY_NIGHT_DISPLAY_ANIMATED = 3; 114 private static final int MSG_APPLY_GLOBAL_SATURATION = 4; 115 private static final int MSG_APPLY_DISPLAY_WHITE_BALANCE = 5; 116 private static final int MSG_APPLY_REDUCE_BRIGHT_COLORS = 6; 117 118 /** 119 * Return value if a setting has not been set. 120 */ 121 private static final int NOT_SET = -1; 122 123 /** 124 * Evaluator used to animate color matrix transitions. 125 */ 126 private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator(); 127 /** 128 * Matrix and offset used for converting color to grayscale. 129 */ 130 private static final float[] MATRIX_GRAYSCALE = new float[]{ 131 .2126f, .2126f, .2126f, 0f, 132 .7152f, .7152f, .7152f, 0f, 133 .0722f, .0722f, .0722f, 0f, 134 0f, 0f, 0f, 1f 135 }; 136 137 /** 138 * Matrix and offset used for luminance inversion. Represents a transform from RGB to YIQ color 139 * space, rotation around the Y axis by 180 degrees, transform back to RGB color space, and 140 * subtraction from 1. The last row represents a non-multiplied addition, see surfaceflinger's 141 * ProgramCache for full implementation details. 142 */ 143 private static final float[] MATRIX_INVERT_COLOR = new float[]{ 144 0.402f, -0.598f, -0.599f, 0f, 145 -1.174f, -0.174f, -1.175f, 0f, 146 -0.228f, -0.228f, 0.772f, 0f, 147 1f, 1f, 1f, 1f 148 }; 149 150 @VisibleForTesting 151 final DisplayWhiteBalanceTintController mDisplayWhiteBalanceTintController = 152 new DisplayWhiteBalanceTintController(); 153 private final NightDisplayTintController mNightDisplayTintController = 154 new NightDisplayTintController(); 155 private final TintController mGlobalSaturationTintController = 156 new GlobalSaturationTintController(); 157 private final ReduceBrightColorsTintController mReduceBrightColorsTintController = 158 new ReduceBrightColorsTintController(); 159 160 @VisibleForTesting 161 final Handler mHandler; 162 163 private final AppSaturationController mAppSaturationController = new AppSaturationController(); 164 165 private int mCurrentUser = UserHandle.USER_NULL; 166 private ContentObserver mUserSetupObserver; 167 private boolean mBootCompleted; 168 169 private ContentObserver mContentObserver; 170 171 private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener; 172 private ReduceBrightColorsListener mReduceBrightColorsListener; 173 174 private NightDisplayAutoMode mNightDisplayAutoMode; 175 176 /** 177 * Map of color modes -> display composition colorspace 178 */ 179 private SparseIntArray mColorModeCompositionColorSpaces = null; 180 ColorDisplayService(Context context)181 public ColorDisplayService(Context context) { 182 super(context); 183 mHandler = new TintHandler(DisplayThread.get().getLooper()); 184 } 185 186 @Override onStart()187 public void onStart() { 188 publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService()); 189 publishLocalService(ColorDisplayServiceInternal.class, new ColorDisplayServiceInternal()); 190 publishLocalService(DisplayTransformManager.class, new DisplayTransformManager()); 191 } 192 193 @Override onBootPhase(int phase)194 public void onBootPhase(int phase) { 195 if (phase >= PHASE_BOOT_COMPLETED) { 196 mBootCompleted = true; 197 198 // Register listeners now that boot is complete. 199 if (mCurrentUser != UserHandle.USER_NULL && mUserSetupObserver == null) { 200 mHandler.sendEmptyMessage(MSG_SET_UP); 201 } 202 } 203 } 204 205 @Override onUserStarting(@onNull TargetUser user)206 public void onUserStarting(@NonNull TargetUser user) { 207 if (mCurrentUser == UserHandle.USER_NULL) { 208 final Message message = mHandler.obtainMessage(MSG_USER_CHANGED); 209 message.arg1 = user.getUserIdentifier(); 210 mHandler.sendMessage(message); 211 } 212 } 213 214 @Override onUserSwitching(@ullable TargetUser from, @NonNull TargetUser to)215 public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { 216 final Message message = mHandler.obtainMessage(MSG_USER_CHANGED); 217 message.arg1 = to.getUserIdentifier(); 218 mHandler.sendMessage(message); 219 } 220 221 @Override onUserStopping(@onNull TargetUser user)222 public void onUserStopping(@NonNull TargetUser user) { 223 if (mCurrentUser == user.getUserIdentifier()) { 224 final Message message = mHandler.obtainMessage(MSG_USER_CHANGED); 225 message.arg1 = UserHandle.USER_NULL; 226 mHandler.sendMessage(message); 227 } 228 } 229 onUserChanged(int userHandle)230 @VisibleForTesting void onUserChanged(int userHandle) { 231 final ContentResolver cr = getContext().getContentResolver(); 232 233 if (mCurrentUser != UserHandle.USER_NULL) { 234 if (mUserSetupObserver != null) { 235 cr.unregisterContentObserver(mUserSetupObserver); 236 mUserSetupObserver = null; 237 } else if (mBootCompleted) { 238 tearDown(); 239 } 240 } 241 242 mCurrentUser = userHandle; 243 244 if (mCurrentUser != UserHandle.USER_NULL) { 245 if (!isUserSetupCompleted(cr, mCurrentUser)) { 246 mUserSetupObserver = new ContentObserver(mHandler) { 247 @Override 248 public void onChange(boolean selfChange, Uri uri) { 249 if (isUserSetupCompleted(cr, mCurrentUser)) { 250 cr.unregisterContentObserver(this); 251 mUserSetupObserver = null; 252 253 if (mBootCompleted) { 254 setUp(); 255 } 256 } 257 } 258 }; 259 cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE), 260 false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser); 261 } else if (mBootCompleted) { 262 setUp(); 263 } 264 } 265 } 266 isUserSetupCompleted(ContentResolver cr, int userHandle)267 private static boolean isUserSetupCompleted(ContentResolver cr, int userHandle) { 268 return Secure.getIntForUser(cr, Secure.USER_SETUP_COMPLETE, 0, userHandle) == 1; 269 } 270 setUpDisplayCompositionColorSpaces(Resources res)271 private void setUpDisplayCompositionColorSpaces(Resources res) { 272 mColorModeCompositionColorSpaces = null; 273 274 final int[] colorModes = res.getIntArray(R.array.config_displayCompositionColorModes); 275 if (colorModes == null) { 276 return; 277 } 278 279 final int[] compSpaces = res.getIntArray(R.array.config_displayCompositionColorSpaces); 280 if (compSpaces == null) { 281 return; 282 } 283 284 if (colorModes.length != compSpaces.length) { 285 Slog.e(TAG, "Number of composition color spaces doesn't match specified color modes"); 286 return; 287 } 288 289 mColorModeCompositionColorSpaces = new SparseIntArray(colorModes.length); 290 for (int i = 0; i < colorModes.length; i++) { 291 mColorModeCompositionColorSpaces.put(colorModes[i], compSpaces[i]); 292 } 293 } 294 setUp()295 private void setUp() { 296 Slog.d(TAG, "setUp: currentUser=" + mCurrentUser); 297 298 // Listen for external changes to any of the settings. 299 if (mContentObserver == null) { 300 mContentObserver = new ContentObserver(mHandler) { 301 @Override 302 public void onChange(boolean selfChange, Uri uri) { 303 super.onChange(selfChange, uri); 304 305 final String setting = uri == null ? null : uri.getLastPathSegment(); 306 if (setting != null) { 307 switch (setting) { 308 case Secure.NIGHT_DISPLAY_ACTIVATED: 309 final boolean activated = mNightDisplayTintController 310 .isActivatedSetting(); 311 if (mNightDisplayTintController.isActivatedStateNotSet() 312 || mNightDisplayTintController.isActivated() != activated) { 313 mNightDisplayTintController.setActivated(activated); 314 } 315 break; 316 case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE: 317 final int temperature = mNightDisplayTintController 318 .getColorTemperatureSetting(); 319 if (mNightDisplayTintController.getColorTemperature() 320 != temperature) { 321 mNightDisplayTintController 322 .onColorTemperatureChanged(temperature); 323 } 324 break; 325 case Secure.NIGHT_DISPLAY_AUTO_MODE: 326 onNightDisplayAutoModeChanged(getNightDisplayAutoModeInternal()); 327 break; 328 case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME: 329 onNightDisplayCustomStartTimeChanged( 330 getNightDisplayCustomStartTimeInternal().getLocalTime()); 331 break; 332 case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME: 333 onNightDisplayCustomEndTimeChanged( 334 getNightDisplayCustomEndTimeInternal().getLocalTime()); 335 break; 336 case System.DISPLAY_COLOR_MODE: 337 onDisplayColorModeChanged(getColorModeInternal()); 338 break; 339 case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED: 340 onAccessibilityInversionChanged(); 341 onAccessibilityActivated(); 342 break; 343 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED: 344 onAccessibilityDaltonizerChanged(); 345 onAccessibilityActivated(); 346 break; 347 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER: 348 onAccessibilityDaltonizerChanged(); 349 break; 350 case Secure.DISPLAY_WHITE_BALANCE_ENABLED: 351 updateDisplayWhiteBalanceStatus(); 352 break; 353 case Secure.REDUCE_BRIGHT_COLORS_ACTIVATED: 354 onReduceBrightColorsActivationChanged(/*userInitiated*/ true); 355 mHandler.sendEmptyMessage(MSG_APPLY_REDUCE_BRIGHT_COLORS); 356 break; 357 case Secure.REDUCE_BRIGHT_COLORS_LEVEL: 358 onReduceBrightColorsStrengthLevelChanged(); 359 mHandler.sendEmptyMessage(MSG_APPLY_REDUCE_BRIGHT_COLORS); 360 break; 361 } 362 } 363 } 364 }; 365 } 366 final ContentResolver cr = getContext().getContentResolver(); 367 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED), 368 false /* notifyForDescendants */, mContentObserver, mCurrentUser); 369 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE), 370 false /* notifyForDescendants */, mContentObserver, mCurrentUser); 371 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE), 372 false /* notifyForDescendants */, mContentObserver, mCurrentUser); 373 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME), 374 false /* notifyForDescendants */, mContentObserver, mCurrentUser); 375 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME), 376 false /* notifyForDescendants */, mContentObserver, mCurrentUser); 377 cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE), 378 false /* notifyForDescendants */, mContentObserver, mCurrentUser); 379 cr.registerContentObserver(Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED), 380 false /* notifyForDescendants */, mContentObserver, mCurrentUser); 381 cr.registerContentObserver( 382 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED), 383 false /* notifyForDescendants */, mContentObserver, mCurrentUser); 384 cr.registerContentObserver(Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER), 385 false /* notifyForDescendants */, mContentObserver, mCurrentUser); 386 cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED), 387 false /* notifyForDescendants */, mContentObserver, mCurrentUser); 388 cr.registerContentObserver(Secure.getUriFor(Secure.REDUCE_BRIGHT_COLORS_ACTIVATED), 389 false /* notifyForDescendants */, mContentObserver, mCurrentUser); 390 cr.registerContentObserver(Secure.getUriFor(Secure.REDUCE_BRIGHT_COLORS_LEVEL), 391 false /* notifyForDescendants */, mContentObserver, mCurrentUser); 392 393 // Apply the accessibility settings first, since they override most other settings. 394 onAccessibilityInversionChanged(); 395 onAccessibilityDaltonizerChanged(); 396 397 setUpDisplayCompositionColorSpaces(getContext().getResources()); 398 399 // Set the color mode, if valid, and immediately apply the updated tint matrix based on the 400 // existing activated state. This ensures consistency of tint across the color mode change. 401 onDisplayColorModeChanged(getColorModeInternal()); 402 403 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); 404 if (mNightDisplayTintController.isAvailable(getContext())) { 405 // Reset the activated state. 406 mNightDisplayTintController.setActivated(null); 407 408 // Prepare the night display color transformation matrix. 409 mNightDisplayTintController.setUp(getContext(), dtm.needsLinearColorMatrix()); 410 mNightDisplayTintController 411 .setMatrix(mNightDisplayTintController.getColorTemperatureSetting()); 412 413 // Initialize the current auto mode. 414 onNightDisplayAutoModeChanged(getNightDisplayAutoModeInternal()); 415 416 // Force the initialization of the current saved activation state. 417 if (mNightDisplayTintController.isActivatedStateNotSet()) { 418 mNightDisplayTintController 419 .setActivated(mNightDisplayTintController.isActivatedSetting()); 420 } 421 } 422 423 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) { 424 // Prepare the display white balance transform matrix. 425 mDisplayWhiteBalanceTintController.setUp(getContext(), true /* needsLinear */); 426 427 updateDisplayWhiteBalanceStatus(); 428 } 429 430 if (mReduceBrightColorsTintController.isAvailable(getContext())) { 431 mReduceBrightColorsTintController.setUp(getContext(), dtm.needsLinearColorMatrix()); 432 onReduceBrightColorsStrengthLevelChanged(); 433 final boolean reset = resetReduceBrightColors(); 434 if (!reset) { 435 onReduceBrightColorsActivationChanged(/*userInitiated*/ false); 436 mHandler.sendEmptyMessage(MSG_APPLY_REDUCE_BRIGHT_COLORS); 437 } 438 } 439 } 440 tearDown()441 private void tearDown() { 442 Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser); 443 444 if (mContentObserver != null) { 445 getContext().getContentResolver().unregisterContentObserver(mContentObserver); 446 } 447 448 if (mNightDisplayTintController.isAvailable(getContext())) { 449 if (mNightDisplayAutoMode != null) { 450 mNightDisplayAutoMode.onStop(); 451 mNightDisplayAutoMode = null; 452 } 453 mNightDisplayTintController.endAnimator(); 454 } 455 456 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) { 457 mDisplayWhiteBalanceTintController.endAnimator(); 458 } 459 460 if (mGlobalSaturationTintController.isAvailable(getContext())) { 461 mGlobalSaturationTintController.setActivated(null); 462 } 463 464 if (mReduceBrightColorsTintController.isAvailable(getContext())) { 465 mReduceBrightColorsTintController.setActivated(null); 466 } 467 } 468 resetReduceBrightColors()469 private boolean resetReduceBrightColors() { 470 if (mCurrentUser == UserHandle.USER_NULL) { 471 return false; 472 } 473 474 final boolean isSettingActivated = Secure.getIntForUser(getContext().getContentResolver(), 475 Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0, mCurrentUser) == 1; 476 final boolean shouldResetOnReboot = Secure.getIntForUser(getContext().getContentResolver(), 477 Secure.REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS, 0, mCurrentUser) == 0; 478 if (isSettingActivated && mReduceBrightColorsTintController.isActivatedStateNotSet() 479 && shouldResetOnReboot) { 480 return Secure.putIntForUser(getContext().getContentResolver(), 481 Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0, mCurrentUser); 482 } 483 return false; 484 } 485 onNightDisplayAutoModeChanged(int autoMode)486 private void onNightDisplayAutoModeChanged(int autoMode) { 487 Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode); 488 489 if (mNightDisplayAutoMode != null) { 490 mNightDisplayAutoMode.onStop(); 491 mNightDisplayAutoMode = null; 492 } 493 494 if (autoMode == AUTO_MODE_CUSTOM_TIME) { 495 mNightDisplayAutoMode = new CustomNightDisplayAutoMode(); 496 } else if (autoMode == AUTO_MODE_TWILIGHT) { 497 mNightDisplayAutoMode = new TwilightNightDisplayAutoMode(); 498 } 499 500 if (mNightDisplayAutoMode != null) { 501 mNightDisplayAutoMode.onStart(); 502 } 503 } 504 onNightDisplayCustomStartTimeChanged(LocalTime startTime)505 private void onNightDisplayCustomStartTimeChanged(LocalTime startTime) { 506 Slog.d(TAG, "onNightDisplayCustomStartTimeChanged: startTime=" + startTime); 507 508 if (mNightDisplayAutoMode != null) { 509 mNightDisplayAutoMode.onCustomStartTimeChanged(startTime); 510 } 511 } 512 onNightDisplayCustomEndTimeChanged(LocalTime endTime)513 private void onNightDisplayCustomEndTimeChanged(LocalTime endTime) { 514 Slog.d(TAG, "onNightDisplayCustomEndTimeChanged: endTime=" + endTime); 515 516 if (mNightDisplayAutoMode != null) { 517 mNightDisplayAutoMode.onCustomEndTimeChanged(endTime); 518 } 519 } 520 getCompositionColorSpace(int mode)521 private int getCompositionColorSpace(int mode) { 522 if (mColorModeCompositionColorSpaces == null) { 523 return Display.COLOR_MODE_INVALID; 524 } 525 526 return mColorModeCompositionColorSpaces.get(mode, Display.COLOR_MODE_INVALID); 527 } 528 onDisplayColorModeChanged(int mode)529 private void onDisplayColorModeChanged(int mode) { 530 if (mode == NOT_SET) { 531 return; 532 } 533 534 mNightDisplayTintController.cancelAnimator(); 535 mDisplayWhiteBalanceTintController.cancelAnimator(); 536 537 if (mNightDisplayTintController.isAvailable(getContext())) { 538 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); 539 mNightDisplayTintController.setUp(getContext(), dtm.needsLinearColorMatrix(mode)); 540 mNightDisplayTintController 541 .setMatrix(mNightDisplayTintController.getColorTemperatureSetting()); 542 } 543 544 // dtm.setColorMode() needs to be called before 545 // updateDisplayWhiteBalanceStatus(), this is because the latter calls 546 // DisplayTransformManager.needsLinearColorMatrix(), therefore it is dependent 547 // on the state of DisplayTransformManager. 548 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); 549 dtm.setColorMode(mode, mNightDisplayTintController.getMatrix(), 550 getCompositionColorSpace(mode)); 551 552 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) { 553 updateDisplayWhiteBalanceStatus(); 554 } 555 } 556 onAccessibilityActivated()557 private void onAccessibilityActivated() { 558 onDisplayColorModeChanged(getColorModeInternal()); 559 } 560 isAccessiblityDaltonizerEnabled()561 private boolean isAccessiblityDaltonizerEnabled() { 562 return Secure.getIntForUser(getContext().getContentResolver(), 563 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, mCurrentUser) != 0; 564 } 565 isAccessiblityInversionEnabled()566 private boolean isAccessiblityInversionEnabled() { 567 return Secure.getIntForUser(getContext().getContentResolver(), 568 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) != 0; 569 } 570 isAccessibilityEnabled()571 private boolean isAccessibilityEnabled() { 572 return isAccessiblityDaltonizerEnabled() || isAccessiblityInversionEnabled(); 573 } 574 575 /** 576 * Apply the accessibility daltonizer transform based on the settings value. 577 */ onAccessibilityDaltonizerChanged()578 private void onAccessibilityDaltonizerChanged() { 579 if (mCurrentUser == UserHandle.USER_NULL) { 580 return; 581 } 582 final int daltonizerMode = isAccessiblityDaltonizerEnabled() 583 ? Secure.getIntForUser(getContext().getContentResolver(), 584 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, 585 AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser) 586 : AccessibilityManager.DALTONIZER_DISABLED; 587 588 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); 589 if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) { 590 // Monochromacy isn't supported by the native Daltonizer implementation; use grayscale. 591 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, 592 MATRIX_GRAYSCALE); 593 dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED); 594 } else { 595 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, null); 596 dtm.setDaltonizerMode(daltonizerMode); 597 } 598 } 599 600 /** 601 * Apply the accessibility inversion transform based on the settings value. 602 */ onAccessibilityInversionChanged()603 private void onAccessibilityInversionChanged() { 604 if (mCurrentUser == UserHandle.USER_NULL) { 605 return; 606 } 607 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); 608 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR, 609 isAccessiblityInversionEnabled() ? MATRIX_INVERT_COLOR : null); 610 } 611 onReduceBrightColorsActivationChanged(boolean userInitiated)612 private void onReduceBrightColorsActivationChanged(boolean userInitiated) { 613 if (mCurrentUser == UserHandle.USER_NULL) { 614 return; 615 } 616 final boolean activated = Secure.getIntForUser(getContext().getContentResolver(), 617 Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0, mCurrentUser) == 1; 618 mReduceBrightColorsTintController.setActivated(activated); 619 if (mReduceBrightColorsListener != null) { 620 mReduceBrightColorsListener.onReduceBrightColorsActivationChanged(activated, 621 userInitiated); 622 } 623 } 624 onReduceBrightColorsStrengthLevelChanged()625 private void onReduceBrightColorsStrengthLevelChanged() { 626 if (mCurrentUser == UserHandle.USER_NULL) { 627 return; 628 } 629 int strength = Secure.getIntForUser(getContext().getContentResolver(), 630 Secure.REDUCE_BRIGHT_COLORS_LEVEL, NOT_SET, mCurrentUser); 631 if (strength == NOT_SET) { 632 strength = getContext().getResources().getInteger( 633 R.integer.config_reduceBrightColorsStrengthDefault); 634 } 635 mReduceBrightColorsTintController.setMatrix(strength); 636 if (mReduceBrightColorsListener != null) { 637 mReduceBrightColorsListener.onReduceBrightColorsStrengthChanged(strength); 638 } 639 } 640 641 /** 642 * Applies current color temperature matrix, or removes it if deactivated. 643 * 644 * @param immediate {@code true} skips transition animation 645 */ applyTint(TintController tintController, boolean immediate)646 private void applyTint(TintController tintController, boolean immediate) { 647 tintController.cancelAnimator(); 648 649 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); 650 final float[] from = dtm.getColorMatrix(tintController.getLevel()); 651 final float[] to = tintController.getMatrix(); 652 653 if (immediate) { 654 dtm.setColorMatrix(tintController.getLevel(), to); 655 } else { 656 TintValueAnimator valueAnimator = TintValueAnimator.ofMatrix(COLOR_MATRIX_EVALUATOR, 657 from == null ? MATRIX_IDENTITY : from, to); 658 tintController.setAnimator(valueAnimator); 659 valueAnimator.setDuration(tintController.getTransitionDurationMilliseconds()); 660 valueAnimator.setInterpolator(AnimationUtils.loadInterpolator( 661 getContext(), android.R.interpolator.fast_out_slow_in)); 662 valueAnimator.addUpdateListener((ValueAnimator animator) -> { 663 final float[] value = (float[]) animator.getAnimatedValue(); 664 dtm.setColorMatrix(tintController.getLevel(), value); 665 ((TintValueAnimator) animator).updateMinMaxComponents(); 666 }); 667 valueAnimator.addListener(new AnimatorListenerAdapter() { 668 669 private boolean mIsCancelled; 670 671 @Override 672 public void onAnimationCancel(Animator animator) { 673 mIsCancelled = true; 674 } 675 676 @Override 677 public void onAnimationEnd(Animator animator) { 678 TintValueAnimator t = (TintValueAnimator) animator; 679 Slog.d(TAG, tintController.getClass().getSimpleName() 680 + " Animation cancelled: " + mIsCancelled 681 + " to matrix: " + TintController.matrixToString(to, 16) 682 + " min matrix coefficients: " 683 + TintController.matrixToString(t.getMin(), 16) 684 + " max matrix coefficients: " 685 + TintController.matrixToString(t.getMax(), 16)); 686 if (!mIsCancelled) { 687 // Ensure final color matrix is set at the end of the animation. If the 688 // animation is cancelled then don't set the final color matrix so the new 689 // animator can pick up from where this one left off. 690 dtm.setColorMatrix(tintController.getLevel(), to); 691 } 692 tintController.setAnimator(null); 693 } 694 }); 695 valueAnimator.start(); 696 } 697 } 698 699 /** 700 * Returns the first date time corresponding to the local time that occurs before the provided 701 * date time. 702 * 703 * @param compareTime the LocalDateTime to compare against 704 * @return the prior LocalDateTime corresponding to this local time 705 */ 706 @VisibleForTesting getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime)707 static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) { 708 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(), 709 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute()); 710 711 // Check if the local time has passed, if so return the same time yesterday. 712 return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt; 713 } 714 715 /** 716 * Returns the first date time corresponding to this local time that occurs after the provided 717 * date time. 718 * 719 * @param compareTime the LocalDateTime to compare against 720 * @return the next LocalDateTime corresponding to this local time 721 */ 722 @VisibleForTesting getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime)723 static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) { 724 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(), 725 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute()); 726 727 // Check if the local time has passed, if so return the same time tomorrow. 728 return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt; 729 } 730 731 @VisibleForTesting updateDisplayWhiteBalanceStatus()732 void updateDisplayWhiteBalanceStatus() { 733 boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated(); 734 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); 735 mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() 736 && !mNightDisplayTintController.isActivated() 737 && !isAccessibilityEnabled() 738 && dtm.needsLinearColorMatrix() 739 && mDisplayWhiteBalanceTintController.isAllowed()); 740 boolean activated = mDisplayWhiteBalanceTintController.isActivated(); 741 742 if (mDisplayWhiteBalanceListener != null && oldActivated != activated) { 743 mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(activated); 744 } 745 746 // If disabled, clear the tint. If enabled, do nothing more here and let the next 747 // temperature update set the correct tint. 748 if (!activated) { 749 mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE); 750 } 751 } 752 setDisplayWhiteBalanceSettingEnabled(boolean enabled)753 private boolean setDisplayWhiteBalanceSettingEnabled(boolean enabled) { 754 if (mCurrentUser == UserHandle.USER_NULL) { 755 return false; 756 } 757 return Secure.putIntForUser(getContext().getContentResolver(), 758 Secure.DISPLAY_WHITE_BALANCE_ENABLED, 759 enabled ? 1 : 0, mCurrentUser); 760 } 761 isDisplayWhiteBalanceSettingEnabled()762 private boolean isDisplayWhiteBalanceSettingEnabled() { 763 if (mCurrentUser == UserHandle.USER_NULL) { 764 return false; 765 } 766 return Secure.getIntForUser(getContext().getContentResolver(), 767 Secure.DISPLAY_WHITE_BALANCE_ENABLED, 768 getContext().getResources() 769 .getBoolean(R.bool.config_displayWhiteBalanceEnabledDefault) ? 1 770 : 0, 771 mCurrentUser) == 1; 772 } 773 setReduceBrightColorsActivatedInternal(boolean activated)774 private boolean setReduceBrightColorsActivatedInternal(boolean activated) { 775 if (mCurrentUser == UserHandle.USER_NULL) { 776 return false; 777 } 778 return Secure.putIntForUser(getContext().getContentResolver(), 779 Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, activated ? 1 : 0, mCurrentUser); 780 } 781 setReduceBrightColorsStrengthInternal(int strength)782 private boolean setReduceBrightColorsStrengthInternal(int strength) { 783 if (mCurrentUser == UserHandle.USER_NULL) { 784 return false; 785 } 786 return Secure.putIntForUser(getContext().getContentResolver(), 787 Secure.REDUCE_BRIGHT_COLORS_LEVEL, strength, mCurrentUser); 788 } 789 isDeviceColorManagedInternal()790 private boolean isDeviceColorManagedInternal() { 791 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); 792 return dtm.isDeviceColorManaged(); 793 } 794 getTransformCapabilitiesInternal()795 private int getTransformCapabilitiesInternal() { 796 int availabilityFlags = ColorDisplayManager.CAPABILITY_NONE; 797 if (SurfaceControl.getProtectedContentSupport()) { 798 availabilityFlags |= ColorDisplayManager.CAPABILITY_PROTECTED_CONTENT; 799 } 800 final Resources res = getContext().getResources(); 801 if (res.getBoolean(R.bool.config_setColorTransformAccelerated)) { 802 availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_GLOBAL; 803 } 804 if (res.getBoolean(R.bool.config_setColorTransformAcceleratedPerLayer)) { 805 availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_PER_APP; 806 } 807 return availabilityFlags; 808 } 809 setNightDisplayAutoModeInternal(@utoMode int autoMode)810 private boolean setNightDisplayAutoModeInternal(@AutoMode int autoMode) { 811 if (getNightDisplayAutoModeInternal() != autoMode) { 812 Secure.putStringForUser(getContext().getContentResolver(), 813 Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, 814 null, 815 mCurrentUser); 816 } 817 return Secure.putIntForUser(getContext().getContentResolver(), 818 Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mCurrentUser); 819 } 820 getNightDisplayAutoModeInternal()821 private int getNightDisplayAutoModeInternal() { 822 int autoMode = getNightDisplayAutoModeRawInternal(); 823 if (autoMode == NOT_SET) { 824 autoMode = getContext().getResources().getInteger( 825 R.integer.config_defaultNightDisplayAutoMode); 826 } 827 if (autoMode != AUTO_MODE_DISABLED 828 && autoMode != AUTO_MODE_CUSTOM_TIME 829 && autoMode != AUTO_MODE_TWILIGHT) { 830 Slog.e(TAG, "Invalid autoMode: " + autoMode); 831 autoMode = AUTO_MODE_DISABLED; 832 } 833 return autoMode; 834 } 835 getNightDisplayAutoModeRawInternal()836 private int getNightDisplayAutoModeRawInternal() { 837 if (mCurrentUser == UserHandle.USER_NULL) { 838 return NOT_SET; 839 } 840 return Secure 841 .getIntForUser(getContext().getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE, 842 NOT_SET, mCurrentUser); 843 } 844 getNightDisplayCustomStartTimeInternal()845 private Time getNightDisplayCustomStartTimeInternal() { 846 int startTimeValue = Secure.getIntForUser(getContext().getContentResolver(), 847 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, NOT_SET, mCurrentUser); 848 if (startTimeValue == NOT_SET) { 849 startTimeValue = getContext().getResources().getInteger( 850 R.integer.config_defaultNightDisplayCustomStartTime); 851 } 852 return new Time(LocalTime.ofSecondOfDay(startTimeValue / 1000)); 853 } 854 setNightDisplayCustomStartTimeInternal(Time startTime)855 private boolean setNightDisplayCustomStartTimeInternal(Time startTime) { 856 return Secure.putIntForUser(getContext().getContentResolver(), 857 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, 858 startTime.getLocalTime().toSecondOfDay() * 1000, 859 mCurrentUser); 860 } 861 getNightDisplayCustomEndTimeInternal()862 private Time getNightDisplayCustomEndTimeInternal() { 863 int endTimeValue = Secure.getIntForUser(getContext().getContentResolver(), 864 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, NOT_SET, mCurrentUser); 865 if (endTimeValue == NOT_SET) { 866 endTimeValue = getContext().getResources().getInteger( 867 R.integer.config_defaultNightDisplayCustomEndTime); 868 } 869 return new Time(LocalTime.ofSecondOfDay(endTimeValue / 1000)); 870 } 871 setNightDisplayCustomEndTimeInternal(Time endTime)872 private boolean setNightDisplayCustomEndTimeInternal(Time endTime) { 873 return Secure.putIntForUser(getContext().getContentResolver(), 874 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.getLocalTime().toSecondOfDay() * 1000, 875 mCurrentUser); 876 } 877 878 /** 879 * Returns the last time the night display transform activation state was changed, or {@link 880 * LocalDateTime#MIN} if night display has never been activated. 881 */ getNightDisplayLastActivatedTimeSetting()882 private LocalDateTime getNightDisplayLastActivatedTimeSetting() { 883 final ContentResolver cr = getContext().getContentResolver(); 884 final String lastActivatedTime = Secure.getStringForUser( 885 cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId()); 886 if (lastActivatedTime != null) { 887 try { 888 return LocalDateTime.parse(lastActivatedTime); 889 } catch (DateTimeParseException ignored) { 890 } 891 // Uses the old epoch time. 892 try { 893 return LocalDateTime.ofInstant( 894 Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)), 895 ZoneId.systemDefault()); 896 } catch (DateTimeException | NumberFormatException ignored) { 897 } 898 } 899 return LocalDateTime.MIN; 900 } 901 setSaturationLevelInternal(int saturationLevel)902 void setSaturationLevelInternal(int saturationLevel) { 903 final Message message = mHandler.obtainMessage(MSG_APPLY_GLOBAL_SATURATION); 904 message.arg1 = saturationLevel; 905 mHandler.sendMessage(message); 906 } 907 setAppSaturationLevelInternal(String callingPackageName, String affectedPackageName, int saturationLevel)908 boolean setAppSaturationLevelInternal(String callingPackageName, 909 String affectedPackageName, int saturationLevel) { 910 return mAppSaturationController 911 .setSaturationLevel(callingPackageName, affectedPackageName, mCurrentUser, 912 saturationLevel); 913 } 914 setColorModeInternal(@olorMode int colorMode)915 private void setColorModeInternal(@ColorMode int colorMode) { 916 if (!isColorModeAvailable(colorMode)) { 917 throw new IllegalArgumentException("Invalid colorMode: " + colorMode); 918 } 919 System.putIntForUser(getContext().getContentResolver(), System.DISPLAY_COLOR_MODE, 920 colorMode, 921 mCurrentUser); 922 } 923 getColorModeInternal()924 private @ColorMode int getColorModeInternal() { 925 final ContentResolver cr = getContext().getContentResolver(); 926 if (isAccessibilityEnabled()) { 927 // There are restrictions on the available color modes combined with a11y transforms. 928 final int a11yColorMode = getContext().getResources().getInteger( 929 R.integer.config_accessibilityColorMode); 930 if (a11yColorMode >= 0) { 931 return a11yColorMode; 932 } 933 } 934 935 int colorMode = System.getIntForUser(cr, System.DISPLAY_COLOR_MODE, -1, mCurrentUser); 936 if (colorMode == -1) { 937 // There might be a system property controlling color mode that we need to respect; if 938 // not, this will set a suitable default. 939 colorMode = getCurrentColorModeFromSystemProperties(); 940 } 941 942 // This happens when a color mode is no longer available (e.g., after system update or B&R) 943 // or the device does not support any color mode. 944 if (!isColorModeAvailable(colorMode)) { 945 final int[] mappedColorModes = getContext().getResources().getIntArray( 946 R.array.config_mappedColorModes); 947 if (colorMode != -1 && mappedColorModes.length > colorMode 948 && isColorModeAvailable(mappedColorModes[colorMode])) { 949 colorMode = mappedColorModes[colorMode]; 950 } else { 951 final int[] availableColorModes = getContext().getResources().getIntArray( 952 R.array.config_availableColorModes); 953 if (availableColorModes.length > 0) { 954 colorMode = availableColorModes[0]; 955 } else { 956 colorMode = NOT_SET; 957 } 958 } 959 } 960 961 return colorMode; 962 } 963 964 /** 965 * Get the current color mode from system properties, or return -1 if invalid. 966 * 967 * See {@link DisplayTransformManager} 968 */ getCurrentColorModeFromSystemProperties()969 private @ColorMode int getCurrentColorModeFromSystemProperties() { 970 final int displayColorSetting = SystemProperties.getInt("persist.sys.sf.native_mode", 0); 971 if (displayColorSetting == 0) { 972 return "1.0".equals(SystemProperties.get("persist.sys.sf.color_saturation")) 973 ? COLOR_MODE_NATURAL : COLOR_MODE_BOOSTED; 974 } else if (displayColorSetting == 1) { 975 return COLOR_MODE_SATURATED; 976 } else if (displayColorSetting == 2) { 977 return COLOR_MODE_AUTOMATIC; 978 } else if (displayColorSetting >= VENDOR_COLOR_MODE_RANGE_MIN 979 && displayColorSetting <= VENDOR_COLOR_MODE_RANGE_MAX) { 980 return displayColorSetting; 981 } else { 982 return -1; 983 } 984 } 985 isColorModeAvailable(@olorMode int colorMode)986 private boolean isColorModeAvailable(@ColorMode int colorMode) { 987 final int[] availableColorModes = getContext().getResources().getIntArray( 988 R.array.config_availableColorModes); 989 if (availableColorModes != null) { 990 for (int mode : availableColorModes) { 991 if (mode == colorMode) { 992 return true; 993 } 994 } 995 } 996 return false; 997 } 998 dumpInternal(PrintWriter pw)999 private void dumpInternal(PrintWriter pw) { 1000 pw.println("COLOR DISPLAY MANAGER dumpsys (color_display)"); 1001 1002 pw.println("Night display:"); 1003 if (mNightDisplayTintController.isAvailable(getContext())) { 1004 pw.println(" Activated: " + mNightDisplayTintController.isActivated()); 1005 pw.println(" Color temp: " + mNightDisplayTintController.getColorTemperature()); 1006 } else { 1007 pw.println(" Not available"); 1008 } 1009 1010 pw.println("Global saturation:"); 1011 if (mGlobalSaturationTintController.isAvailable(getContext())) { 1012 pw.println(" Activated: " + mGlobalSaturationTintController.isActivated()); 1013 } else { 1014 pw.println(" Not available"); 1015 } 1016 1017 mAppSaturationController.dump(pw); 1018 1019 pw.println("Display white balance:"); 1020 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) { 1021 pw.println(" Activated: " + mDisplayWhiteBalanceTintController.isActivated()); 1022 mDisplayWhiteBalanceTintController.dump(pw); 1023 } else { 1024 pw.println(" Not available"); 1025 } 1026 1027 pw.println("Reduce bright colors:"); 1028 if (mReduceBrightColorsTintController.isAvailable(getContext())) { 1029 pw.println(" Activated: " + mReduceBrightColorsTintController.isActivated()); 1030 mReduceBrightColorsTintController.dump(pw); 1031 } else { 1032 pw.println(" Not available"); 1033 } 1034 1035 pw.println("Color mode: " + getColorModeInternal()); 1036 } 1037 1038 private abstract class NightDisplayAutoMode { 1039 onActivated(boolean activated)1040 public abstract void onActivated(boolean activated); 1041 onStart()1042 public abstract void onStart(); 1043 onStop()1044 public abstract void onStop(); 1045 onCustomStartTimeChanged(LocalTime startTime)1046 public void onCustomStartTimeChanged(LocalTime startTime) { 1047 } 1048 onCustomEndTimeChanged(LocalTime endTime)1049 public void onCustomEndTimeChanged(LocalTime endTime) { 1050 } 1051 } 1052 1053 private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements 1054 AlarmManager.OnAlarmListener { 1055 1056 private final AlarmManager mAlarmManager; 1057 private final BroadcastReceiver mTimeChangedReceiver; 1058 1059 private LocalTime mStartTime; 1060 private LocalTime mEndTime; 1061 1062 private LocalDateTime mLastActivatedTime; 1063 CustomNightDisplayAutoMode()1064 CustomNightDisplayAutoMode() { 1065 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); 1066 mTimeChangedReceiver = new BroadcastReceiver() { 1067 @Override 1068 public void onReceive(Context context, Intent intent) { 1069 updateActivated(); 1070 } 1071 }; 1072 } 1073 updateActivated()1074 private void updateActivated() { 1075 final LocalDateTime now = LocalDateTime.now(); 1076 final LocalDateTime start = getDateTimeBefore(mStartTime, now); 1077 final LocalDateTime end = getDateTimeAfter(mEndTime, start); 1078 boolean activate = now.isBefore(end); 1079 1080 if (mLastActivatedTime != null) { 1081 // Maintain the existing activated state if within the current period. 1082 if (mLastActivatedTime.isBefore(now) 1083 && mLastActivatedTime.isAfter(start) 1084 && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) { 1085 activate = mNightDisplayTintController.isActivatedSetting(); 1086 } 1087 } 1088 1089 if (mNightDisplayTintController.isActivatedStateNotSet() 1090 || (mNightDisplayTintController.isActivated() != activate)) { 1091 mNightDisplayTintController.setActivated(activate, activate ? start : end); 1092 } 1093 1094 updateNextAlarm(mNightDisplayTintController.isActivated(), now); 1095 } 1096 updateNextAlarm(@ullable Boolean activated, @NonNull LocalDateTime now)1097 private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) { 1098 if (activated != null) { 1099 final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now) 1100 : getDateTimeAfter(mStartTime, now); 1101 final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); 1102 mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null); 1103 } 1104 } 1105 1106 @Override onStart()1107 public void onStart() { 1108 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED); 1109 intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 1110 getContext().registerReceiver(mTimeChangedReceiver, intentFilter); 1111 1112 mStartTime = getNightDisplayCustomStartTimeInternal().getLocalTime(); 1113 mEndTime = getNightDisplayCustomEndTimeInternal().getLocalTime(); 1114 1115 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting(); 1116 1117 // Force an update to initialize state. 1118 updateActivated(); 1119 } 1120 1121 @Override onStop()1122 public void onStop() { 1123 getContext().unregisterReceiver(mTimeChangedReceiver); 1124 1125 mAlarmManager.cancel(this); 1126 mLastActivatedTime = null; 1127 } 1128 1129 @Override onActivated(boolean activated)1130 public void onActivated(boolean activated) { 1131 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting(); 1132 updateNextAlarm(activated, LocalDateTime.now()); 1133 } 1134 1135 @Override onCustomStartTimeChanged(LocalTime startTime)1136 public void onCustomStartTimeChanged(LocalTime startTime) { 1137 mStartTime = startTime; 1138 mLastActivatedTime = null; 1139 updateActivated(); 1140 } 1141 1142 @Override onCustomEndTimeChanged(LocalTime endTime)1143 public void onCustomEndTimeChanged(LocalTime endTime) { 1144 mEndTime = endTime; 1145 mLastActivatedTime = null; 1146 updateActivated(); 1147 } 1148 1149 @Override onAlarm()1150 public void onAlarm() { 1151 Slog.d(TAG, "onAlarm"); 1152 updateActivated(); 1153 } 1154 } 1155 1156 private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements 1157 TwilightListener { 1158 1159 private final TwilightManager mTwilightManager; 1160 private LocalDateTime mLastActivatedTime; 1161 TwilightNightDisplayAutoMode()1162 TwilightNightDisplayAutoMode() { 1163 mTwilightManager = getLocalService(TwilightManager.class); 1164 } 1165 updateActivated(TwilightState state)1166 private void updateActivated(TwilightState state) { 1167 if (state == null) { 1168 // If there isn't a valid TwilightState then just keep the current activated 1169 // state. 1170 return; 1171 } 1172 1173 boolean activate = state.isNight(); 1174 if (mLastActivatedTime != null) { 1175 final LocalDateTime now = LocalDateTime.now(); 1176 final LocalDateTime sunrise = state.sunrise(); 1177 final LocalDateTime sunset = state.sunset(); 1178 // Maintain the existing activated state if within the current period. 1179 if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise) 1180 ^ mLastActivatedTime.isBefore(sunset))) { 1181 activate = mNightDisplayTintController.isActivatedSetting(); 1182 } 1183 } 1184 1185 if (mNightDisplayTintController.isActivatedStateNotSet() || ( 1186 mNightDisplayTintController.isActivated() != activate)) { 1187 mNightDisplayTintController.setActivated(activate); 1188 } 1189 } 1190 1191 @Override onActivated(boolean activated)1192 public void onActivated(boolean activated) { 1193 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting(); 1194 } 1195 1196 @Override onStart()1197 public void onStart() { 1198 mTwilightManager.registerListener(this, mHandler); 1199 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting(); 1200 1201 // Force an update to initialize state. 1202 updateActivated(mTwilightManager.getLastTwilightState()); 1203 } 1204 1205 @Override onStop()1206 public void onStop() { 1207 mTwilightManager.unregisterListener(this); 1208 mLastActivatedTime = null; 1209 } 1210 1211 @Override onTwilightStateChanged(@ullable TwilightState state)1212 public void onTwilightStateChanged(@Nullable TwilightState state) { 1213 Slog.d(TAG, "onTwilightStateChanged: isNight=" 1214 + (state == null ? null : state.isNight())); 1215 updateActivated(state); 1216 } 1217 } 1218 1219 /** 1220 * Only animates matrices and saves min and max coefficients for logging. 1221 */ 1222 static class TintValueAnimator extends ValueAnimator { 1223 private float[] min; 1224 private float[] max; 1225 ofMatrix(ColorMatrixEvaluator evaluator, Object... values)1226 public static TintValueAnimator ofMatrix(ColorMatrixEvaluator evaluator, 1227 Object... values) { 1228 TintValueAnimator anim = new TintValueAnimator(); 1229 anim.setObjectValues(values); 1230 anim.setEvaluator(evaluator); 1231 if (values == null || values.length == 0) { 1232 return null; 1233 } 1234 float[] m = (float[]) values[0]; 1235 anim.min = new float[m.length]; 1236 anim.max = new float[m.length]; 1237 for (int i = 0; i < m.length; ++i) { 1238 anim.min[i] = Float.MAX_VALUE; 1239 anim.max[i] = Float.MIN_VALUE; 1240 } 1241 return anim; 1242 } 1243 updateMinMaxComponents()1244 public void updateMinMaxComponents() { 1245 float[] value = (float[]) getAnimatedValue(); 1246 if (value == null) { 1247 return; 1248 } 1249 for (int i = 0; i < value.length; ++i) { 1250 min[i] = Math.min(min[i], value[i]); 1251 max[i] = Math.max(max[i], value[i]); 1252 } 1253 } 1254 getMin()1255 public float[] getMin() { 1256 return min; 1257 } 1258 getMax()1259 public float[] getMax() { 1260 return max; 1261 } 1262 } 1263 1264 /** 1265 * Interpolates between two 4x4 color transform matrices (in column-major order). 1266 */ 1267 private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> { 1268 1269 /** 1270 * Result matrix returned by {@link #evaluate(float, float[], float[])}. 1271 */ 1272 private final float[] mResultMatrix = new float[16]; 1273 1274 @Override evaluate(float fraction, float[] startValue, float[] endValue)1275 public float[] evaluate(float fraction, float[] startValue, float[] endValue) { 1276 for (int i = 0; i < mResultMatrix.length; i++) { 1277 mResultMatrix[i] = MathUtils.lerp(startValue[i], endValue[i], fraction); 1278 } 1279 return mResultMatrix; 1280 } 1281 } 1282 1283 private final class NightDisplayTintController extends TintController { 1284 1285 private final float[] mMatrix = new float[16]; 1286 private final float[] mColorTempCoefficients = new float[9]; 1287 1288 private Boolean mIsAvailable; 1289 private Integer mColorTemp; 1290 1291 /** 1292 * Set coefficients based on whether the color matrix is linear or not. 1293 */ 1294 @Override setUp(Context context, boolean needsLinear)1295 public void setUp(Context context, boolean needsLinear) { 1296 final String[] coefficients = context.getResources().getStringArray(needsLinear 1297 ? R.array.config_nightDisplayColorTemperatureCoefficients 1298 : R.array.config_nightDisplayColorTemperatureCoefficientsNative); 1299 for (int i = 0; i < 9 && i < coefficients.length; i++) { 1300 mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]); 1301 } 1302 } 1303 1304 @Override setMatrix(int cct)1305 public void setMatrix(int cct) { 1306 if (mMatrix.length != 16) { 1307 Slog.d(TAG, "The display transformation matrix must be 4x4"); 1308 return; 1309 } 1310 1311 Matrix.setIdentityM(mMatrix, 0); 1312 1313 final float squareTemperature = cct * cct; 1314 final float red = squareTemperature * mColorTempCoefficients[0] 1315 + cct * mColorTempCoefficients[1] + mColorTempCoefficients[2]; 1316 final float green = squareTemperature * mColorTempCoefficients[3] 1317 + cct * mColorTempCoefficients[4] + mColorTempCoefficients[5]; 1318 final float blue = squareTemperature * mColorTempCoefficients[6] 1319 + cct * mColorTempCoefficients[7] + mColorTempCoefficients[8]; 1320 mMatrix[0] = red; 1321 mMatrix[5] = green; 1322 mMatrix[10] = blue; 1323 } 1324 1325 @Override getMatrix()1326 public float[] getMatrix() { 1327 return isActivated() ? mMatrix : MATRIX_IDENTITY; 1328 } 1329 1330 @Override setActivated(Boolean activated)1331 public void setActivated(Boolean activated) { 1332 setActivated(activated, LocalDateTime.now()); 1333 } 1334 1335 /** 1336 * Use directly when it is important that the last activation time be exact (for example, an 1337 * automatic change). Otherwise use {@link #setActivated(Boolean)}. 1338 */ setActivated(Boolean activated, @NonNull LocalDateTime lastActivationTime)1339 public void setActivated(Boolean activated, @NonNull LocalDateTime lastActivationTime) { 1340 if (activated == null) { 1341 super.setActivated(null); 1342 return; 1343 } 1344 1345 boolean activationStateChanged = activated != isActivated(); 1346 1347 if (!isActivatedStateNotSet() && activationStateChanged) { 1348 // This is a true state change, so set this as the last activation time. 1349 Secure.putStringForUser(getContext().getContentResolver(), 1350 Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, 1351 lastActivationTime.toString(), 1352 mCurrentUser); 1353 } 1354 1355 if (isActivatedStateNotSet() || activationStateChanged) { 1356 super.setActivated(activated); 1357 if (isActivatedSetting() != activated) { 1358 Secure.putIntForUser(getContext().getContentResolver(), 1359 Secure.NIGHT_DISPLAY_ACTIVATED, 1360 activated ? 1 : 0, mCurrentUser); 1361 } 1362 onActivated(activated); 1363 } 1364 } 1365 1366 @Override getLevel()1367 public int getLevel() { 1368 return LEVEL_COLOR_MATRIX_NIGHT_DISPLAY; 1369 } 1370 1371 @Override isAvailable(Context context)1372 public boolean isAvailable(Context context) { 1373 if (mIsAvailable == null) { 1374 mIsAvailable = ColorDisplayManager.isNightDisplayAvailable(context); 1375 } 1376 return mIsAvailable; 1377 } 1378 onActivated(boolean activated)1379 private void onActivated(boolean activated) { 1380 Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display"); 1381 if (mNightDisplayAutoMode != null) { 1382 mNightDisplayAutoMode.onActivated(activated); 1383 } 1384 1385 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) { 1386 updateDisplayWhiteBalanceStatus(); 1387 } 1388 1389 mHandler.sendEmptyMessage(MSG_APPLY_NIGHT_DISPLAY_ANIMATED); 1390 } 1391 getColorTemperature()1392 int getColorTemperature() { 1393 return mColorTemp != null ? clampNightDisplayColorTemperature(mColorTemp) 1394 : getColorTemperatureSetting(); 1395 } 1396 setColorTemperature(int temperature)1397 boolean setColorTemperature(int temperature) { 1398 mColorTemp = temperature; 1399 final boolean success = Secure.putIntForUser(getContext().getContentResolver(), 1400 Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, temperature, mCurrentUser); 1401 onColorTemperatureChanged(temperature); 1402 return success; 1403 } 1404 onColorTemperatureChanged(int temperature)1405 void onColorTemperatureChanged(int temperature) { 1406 setMatrix(temperature); 1407 mHandler.sendEmptyMessage(MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE); 1408 } 1409 isActivatedSetting()1410 boolean isActivatedSetting() { 1411 if (mCurrentUser == UserHandle.USER_NULL) { 1412 return false; 1413 } 1414 return Secure.getIntForUser(getContext().getContentResolver(), 1415 Secure.NIGHT_DISPLAY_ACTIVATED, 0, mCurrentUser) == 1; 1416 } 1417 getColorTemperatureSetting()1418 int getColorTemperatureSetting() { 1419 if (mCurrentUser == UserHandle.USER_NULL) { 1420 return NOT_SET; 1421 } 1422 return clampNightDisplayColorTemperature(Secure.getIntForUser( 1423 getContext().getContentResolver(), Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 1424 NOT_SET, 1425 mCurrentUser)); 1426 } 1427 clampNightDisplayColorTemperature(int colorTemperature)1428 private int clampNightDisplayColorTemperature(int colorTemperature) { 1429 if (colorTemperature == NOT_SET) { 1430 colorTemperature = getContext().getResources().getInteger( 1431 R.integer.config_nightDisplayColorTemperatureDefault); 1432 } 1433 final int minimumTemperature = ColorDisplayManager 1434 .getMinimumColorTemperature(getContext()); 1435 final int maximumTemperature = ColorDisplayManager 1436 .getMaximumColorTemperature(getContext()); 1437 if (colorTemperature < minimumTemperature) { 1438 colorTemperature = minimumTemperature; 1439 } else if (colorTemperature > maximumTemperature) { 1440 colorTemperature = maximumTemperature; 1441 } 1442 1443 return colorTemperature; 1444 } 1445 } 1446 1447 /** 1448 * Local service that allows color transforms to be enabled from other system services. 1449 */ 1450 public class ColorDisplayServiceInternal { 1451 1452 /** Sets whether DWB should be allowed in the current state. */ setDisplayWhiteBalanceAllowed(boolean allowed)1453 public void setDisplayWhiteBalanceAllowed(boolean allowed) { 1454 mDisplayWhiteBalanceTintController.setAllowed(allowed); 1455 updateDisplayWhiteBalanceStatus(); 1456 } 1457 1458 /** 1459 * Set the current CCT value for the display white balance transform, and if the transform 1460 * is enabled, apply it. 1461 * 1462 * @param cct the color temperature in Kelvin. 1463 */ setDisplayWhiteBalanceColorTemperature(int cct)1464 public boolean setDisplayWhiteBalanceColorTemperature(int cct) { 1465 // Update the transform matrix even if it can't be applied. 1466 mDisplayWhiteBalanceTintController.setMatrix(cct); 1467 1468 if (mDisplayWhiteBalanceTintController.isActivated()) { 1469 mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE); 1470 return true; 1471 } 1472 return false; 1473 } 1474 1475 /** Get the luminance of the current chromatic adaptation matrix. */ getDisplayWhiteBalanceLuminance()1476 public float getDisplayWhiteBalanceLuminance() { 1477 return mDisplayWhiteBalanceTintController.getLuminance(); 1478 } 1479 1480 /** 1481 * Reset the CCT value for the display white balance transform to its default value. 1482 */ resetDisplayWhiteBalanceColorTemperature()1483 public boolean resetDisplayWhiteBalanceColorTemperature() { 1484 int temperatureDefault = getContext().getResources() 1485 .getInteger(R.integer.config_displayWhiteBalanceColorTemperatureDefault); 1486 Slog.d(TAG, "resetDisplayWhiteBalanceColorTemperature: " + temperatureDefault); 1487 return setDisplayWhiteBalanceColorTemperature(temperatureDefault); 1488 } 1489 1490 /** 1491 * Sets the listener and returns whether display white balance is currently enabled. 1492 */ setDisplayWhiteBalanceListener(DisplayWhiteBalanceListener listener)1493 public boolean setDisplayWhiteBalanceListener(DisplayWhiteBalanceListener listener) { 1494 mDisplayWhiteBalanceListener = listener; 1495 return mDisplayWhiteBalanceTintController.isActivated(); 1496 } 1497 1498 /** 1499 * Returns whether Display white balance is currently enabled. 1500 */ isDisplayWhiteBalanceEnabled()1501 public boolean isDisplayWhiteBalanceEnabled() { 1502 return isDisplayWhiteBalanceSettingEnabled(); 1503 } 1504 1505 /** 1506 * Sets the listener and returns whether reduce bright colors is currently enabled. 1507 */ setReduceBrightColorsListener(ReduceBrightColorsListener listener)1508 public boolean setReduceBrightColorsListener(ReduceBrightColorsListener listener) { 1509 mReduceBrightColorsListener = listener; 1510 return mReduceBrightColorsTintController.isActivated(); 1511 } 1512 1513 /** 1514 * Returns whether reduce bright colors is currently active. 1515 */ isReduceBrightColorsActivated()1516 public boolean isReduceBrightColorsActivated() { 1517 return mReduceBrightColorsTintController.isActivated(); 1518 } 1519 getReduceBrightColorsStrength()1520 public int getReduceBrightColorsStrength() { 1521 return mReduceBrightColorsTintController.getStrength(); 1522 } 1523 1524 /** 1525 * Gets the computed brightness, in nits, when the reduce bright colors feature is applied 1526 * at the current strength. 1527 * 1528 * @hide 1529 */ getReduceBrightColorsAdjustedBrightnessNits(float nits)1530 public float getReduceBrightColorsAdjustedBrightnessNits(float nits) { 1531 return mReduceBrightColorsTintController.getAdjustedBrightness(nits); 1532 } 1533 1534 /** 1535 * Adds a {@link WeakReference<ColorTransformController>} for a newly started activity, and 1536 * invokes {@link ColorTransformController#applyAppSaturation(float[], float[])} if needed. 1537 */ attachColorTransformController(String packageName, @UserIdInt int userId, WeakReference<ColorTransformController> controller)1538 public boolean attachColorTransformController(String packageName, @UserIdInt int userId, 1539 WeakReference<ColorTransformController> controller) { 1540 return mAppSaturationController 1541 .addColorTransformController(packageName, userId, controller); 1542 } 1543 } 1544 1545 /** 1546 * Listener for changes in display white balance status. 1547 */ 1548 public interface DisplayWhiteBalanceListener { 1549 1550 /** 1551 * Notify that the display white balance status has changed, either due to preemption by 1552 * another transform or the feature being turned off. 1553 */ onDisplayWhiteBalanceStatusChanged(boolean activated)1554 void onDisplayWhiteBalanceStatusChanged(boolean activated); 1555 } 1556 1557 /** 1558 * Listener for changes in reduce bright colors status. 1559 */ 1560 public interface ReduceBrightColorsListener { 1561 1562 /** 1563 * Notify that the reduce bright colors activation status has changed. 1564 */ onReduceBrightColorsActivationChanged(boolean activated, boolean userInitiated)1565 void onReduceBrightColorsActivationChanged(boolean activated, boolean userInitiated); 1566 1567 /** 1568 * Notify that the reduce bright colors strength has changed. 1569 */ onReduceBrightColorsStrengthChanged(int strength)1570 void onReduceBrightColorsStrengthChanged(int strength); 1571 } 1572 1573 private final class TintHandler extends Handler { 1574 TintHandler(Looper looper)1575 private TintHandler(Looper looper) { 1576 super(looper, null, true /* async */); 1577 } 1578 1579 @Override handleMessage(Message msg)1580 public void handleMessage(Message msg) { 1581 switch (msg.what) { 1582 case MSG_USER_CHANGED: 1583 onUserChanged(msg.arg1); 1584 break; 1585 case MSG_SET_UP: 1586 setUp(); 1587 break; 1588 case MSG_APPLY_GLOBAL_SATURATION: 1589 mGlobalSaturationTintController.setMatrix(msg.arg1); 1590 applyTint(mGlobalSaturationTintController, false); 1591 break; 1592 case MSG_APPLY_REDUCE_BRIGHT_COLORS: 1593 applyTint(mReduceBrightColorsTintController, true); 1594 break; 1595 case MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE: 1596 applyTint(mNightDisplayTintController, true); 1597 break; 1598 case MSG_APPLY_NIGHT_DISPLAY_ANIMATED: 1599 applyTint(mNightDisplayTintController, false); 1600 break; 1601 case MSG_APPLY_DISPLAY_WHITE_BALANCE: 1602 applyTint(mDisplayWhiteBalanceTintController, false); 1603 break; 1604 } 1605 } 1606 } 1607 1608 /** 1609 * Interface for applying transforms to a given AppWindow. 1610 */ 1611 public interface ColorTransformController { 1612 1613 /** 1614 * Apply the given saturation (grayscale) matrix to the associated AppWindow. 1615 */ applyAppSaturation(@ize9) float[] matrix, @Size(3) float[] translation)1616 void applyAppSaturation(@Size(9) float[] matrix, @Size(3) float[] translation); 1617 } 1618 1619 @VisibleForTesting 1620 final class BinderService extends IColorDisplayManager.Stub { 1621 1622 @Override setColorMode(int colorMode)1623 public void setColorMode(int colorMode) { 1624 getContext().enforceCallingOrSelfPermission( 1625 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS, 1626 "Permission required to set display color mode"); 1627 final long token = Binder.clearCallingIdentity(); 1628 try { 1629 setColorModeInternal(colorMode); 1630 } finally { 1631 Binder.restoreCallingIdentity(token); 1632 } 1633 } 1634 1635 @Override getColorMode()1636 public int getColorMode() { 1637 final long token = Binder.clearCallingIdentity(); 1638 try { 1639 return getColorModeInternal(); 1640 } finally { 1641 Binder.restoreCallingIdentity(token); 1642 } 1643 } 1644 1645 @Override isDeviceColorManaged()1646 public boolean isDeviceColorManaged() { 1647 final long token = Binder.clearCallingIdentity(); 1648 try { 1649 return isDeviceColorManagedInternal(); 1650 } finally { 1651 Binder.restoreCallingIdentity(token); 1652 } 1653 } 1654 1655 @Override setSaturationLevel(int level)1656 public boolean setSaturationLevel(int level) { 1657 final boolean hasTransformsPermission = getContext() 1658 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) 1659 == PackageManager.PERMISSION_GRANTED; 1660 final boolean hasLegacyPermission = getContext() 1661 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION) 1662 == PackageManager.PERMISSION_GRANTED; 1663 if (!hasTransformsPermission && !hasLegacyPermission) { 1664 throw new SecurityException("Permission required to set display saturation level"); 1665 } 1666 final long token = Binder.clearCallingIdentity(); 1667 try { 1668 setSaturationLevelInternal(level); 1669 } finally { 1670 Binder.restoreCallingIdentity(token); 1671 } 1672 return true; 1673 } 1674 1675 @Override isSaturationActivated()1676 public boolean isSaturationActivated() { 1677 getContext().enforceCallingPermission( 1678 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS, 1679 "Permission required to get display saturation level"); 1680 final long token = Binder.clearCallingIdentity(); 1681 try { 1682 return !mGlobalSaturationTintController.isActivatedStateNotSet() 1683 && mGlobalSaturationTintController.isActivated(); 1684 } finally { 1685 Binder.restoreCallingIdentity(token); 1686 } 1687 } 1688 1689 @Override setAppSaturationLevel(String packageName, int level)1690 public boolean setAppSaturationLevel(String packageName, int level) { 1691 getContext().enforceCallingPermission( 1692 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS, 1693 "Permission required to set display saturation level"); 1694 final String callingPackageName = LocalServices.getService(PackageManagerInternal.class) 1695 .getNameForUid(Binder.getCallingUid()); 1696 final long token = Binder.clearCallingIdentity(); 1697 try { 1698 return setAppSaturationLevelInternal(callingPackageName, packageName, level); 1699 } finally { 1700 Binder.restoreCallingIdentity(token); 1701 } 1702 } 1703 getTransformCapabilities()1704 public int getTransformCapabilities() { 1705 getContext().enforceCallingPermission( 1706 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS, 1707 "Permission required to query transform capabilities"); 1708 final long token = Binder.clearCallingIdentity(); 1709 try { 1710 return getTransformCapabilitiesInternal(); 1711 } finally { 1712 Binder.restoreCallingIdentity(token); 1713 } 1714 } 1715 1716 @Override setNightDisplayActivated(boolean activated)1717 public boolean setNightDisplayActivated(boolean activated) { 1718 getContext().enforceCallingOrSelfPermission( 1719 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS, 1720 "Permission required to set night display activated"); 1721 final long token = Binder.clearCallingIdentity(); 1722 try { 1723 mNightDisplayTintController.setActivated(activated); 1724 return true; 1725 } finally { 1726 Binder.restoreCallingIdentity(token); 1727 } 1728 } 1729 1730 @Override isNightDisplayActivated()1731 public boolean isNightDisplayActivated() { 1732 final long token = Binder.clearCallingIdentity(); 1733 try { 1734 return mNightDisplayTintController.isActivated(); 1735 } finally { 1736 Binder.restoreCallingIdentity(token); 1737 } 1738 } 1739 1740 @Override setNightDisplayColorTemperature(int temperature)1741 public boolean setNightDisplayColorTemperature(int temperature) { 1742 getContext().enforceCallingOrSelfPermission( 1743 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS, 1744 "Permission required to set night display temperature"); 1745 final long token = Binder.clearCallingIdentity(); 1746 try { 1747 return mNightDisplayTintController.setColorTemperature(temperature); 1748 } finally { 1749 Binder.restoreCallingIdentity(token); 1750 } 1751 } 1752 1753 @Override getNightDisplayColorTemperature()1754 public int getNightDisplayColorTemperature() { 1755 final long token = Binder.clearCallingIdentity(); 1756 try { 1757 return mNightDisplayTintController.getColorTemperature(); 1758 } finally { 1759 Binder.restoreCallingIdentity(token); 1760 } 1761 } 1762 1763 @Override setNightDisplayAutoMode(int autoMode)1764 public boolean setNightDisplayAutoMode(int autoMode) { 1765 getContext().enforceCallingOrSelfPermission( 1766 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS, 1767 "Permission required to set night display auto mode"); 1768 final long token = Binder.clearCallingIdentity(); 1769 try { 1770 return setNightDisplayAutoModeInternal(autoMode); 1771 } finally { 1772 Binder.restoreCallingIdentity(token); 1773 } 1774 } 1775 1776 @Override getNightDisplayAutoMode()1777 public int getNightDisplayAutoMode() { 1778 getContext().enforceCallingOrSelfPermission( 1779 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS, 1780 "Permission required to get night display auto mode"); 1781 final long token = Binder.clearCallingIdentity(); 1782 try { 1783 return getNightDisplayAutoModeInternal(); 1784 } finally { 1785 Binder.restoreCallingIdentity(token); 1786 } 1787 } 1788 1789 @Override getNightDisplayAutoModeRaw()1790 public int getNightDisplayAutoModeRaw() { 1791 final long token = Binder.clearCallingIdentity(); 1792 try { 1793 return getNightDisplayAutoModeRawInternal(); 1794 } finally { 1795 Binder.restoreCallingIdentity(token); 1796 } 1797 } 1798 1799 @Override setNightDisplayCustomStartTime(Time startTime)1800 public boolean setNightDisplayCustomStartTime(Time startTime) { 1801 getContext().enforceCallingOrSelfPermission( 1802 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS, 1803 "Permission required to set night display custom start time"); 1804 final long token = Binder.clearCallingIdentity(); 1805 try { 1806 return setNightDisplayCustomStartTimeInternal(startTime); 1807 } finally { 1808 Binder.restoreCallingIdentity(token); 1809 } 1810 } 1811 1812 @Override getNightDisplayCustomStartTime()1813 public Time getNightDisplayCustomStartTime() { 1814 final long token = Binder.clearCallingIdentity(); 1815 try { 1816 return getNightDisplayCustomStartTimeInternal(); 1817 } finally { 1818 Binder.restoreCallingIdentity(token); 1819 } 1820 } 1821 1822 @Override setNightDisplayCustomEndTime(Time endTime)1823 public boolean setNightDisplayCustomEndTime(Time endTime) { 1824 getContext().enforceCallingOrSelfPermission( 1825 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS, 1826 "Permission required to set night display custom end time"); 1827 final long token = Binder.clearCallingIdentity(); 1828 try { 1829 return setNightDisplayCustomEndTimeInternal(endTime); 1830 } finally { 1831 Binder.restoreCallingIdentity(token); 1832 } 1833 } 1834 1835 @Override getNightDisplayCustomEndTime()1836 public Time getNightDisplayCustomEndTime() { 1837 final long token = Binder.clearCallingIdentity(); 1838 try { 1839 return getNightDisplayCustomEndTimeInternal(); 1840 } finally { 1841 Binder.restoreCallingIdentity(token); 1842 } 1843 } 1844 1845 @Override setDisplayWhiteBalanceEnabled(boolean enabled)1846 public boolean setDisplayWhiteBalanceEnabled(boolean enabled) { 1847 getContext().enforceCallingOrSelfPermission( 1848 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS, 1849 "Permission required to set night display activated"); 1850 final long token = Binder.clearCallingIdentity(); 1851 try { 1852 return setDisplayWhiteBalanceSettingEnabled(enabled); 1853 } finally { 1854 Binder.restoreCallingIdentity(token); 1855 } 1856 } 1857 1858 @Override isDisplayWhiteBalanceEnabled()1859 public boolean isDisplayWhiteBalanceEnabled() { 1860 final long token = Binder.clearCallingIdentity(); 1861 try { 1862 return isDisplayWhiteBalanceSettingEnabled(); 1863 } finally { 1864 Binder.restoreCallingIdentity(token); 1865 } 1866 } 1867 1868 @Override isReduceBrightColorsActivated()1869 public boolean isReduceBrightColorsActivated() { 1870 final long token = Binder.clearCallingIdentity(); 1871 try { 1872 return mReduceBrightColorsTintController.isActivated(); 1873 } finally { 1874 Binder.restoreCallingIdentity(token); 1875 } 1876 } 1877 1878 @Override setReduceBrightColorsActivated(boolean activated)1879 public boolean setReduceBrightColorsActivated(boolean activated) { 1880 getContext().enforceCallingOrSelfPermission( 1881 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS, 1882 "Permission required to set reduce bright colors activation state"); 1883 final long token = Binder.clearCallingIdentity(); 1884 try { 1885 return setReduceBrightColorsActivatedInternal(activated); 1886 } finally { 1887 Binder.restoreCallingIdentity(token); 1888 } 1889 } 1890 1891 @Override getReduceBrightColorsStrength()1892 public int getReduceBrightColorsStrength() { 1893 final long token = Binder.clearCallingIdentity(); 1894 try { 1895 return mReduceBrightColorsTintController.getStrength(); 1896 } finally { 1897 Binder.restoreCallingIdentity(token); 1898 } 1899 } 1900 1901 @Override getReduceBrightColorsOffsetFactor()1902 public float getReduceBrightColorsOffsetFactor() { 1903 final long token = Binder.clearCallingIdentity(); 1904 try { 1905 return mReduceBrightColorsTintController.getOffsetFactor(); 1906 } finally { 1907 Binder.restoreCallingIdentity(token); 1908 } 1909 } 1910 1911 @Override setReduceBrightColorsStrength(int strength)1912 public boolean setReduceBrightColorsStrength(int strength) { 1913 getContext().enforceCallingOrSelfPermission( 1914 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS, 1915 "Permission required to set reduce bright colors strength"); 1916 final long token = Binder.clearCallingIdentity(); 1917 try { 1918 return setReduceBrightColorsStrengthInternal(strength); 1919 } finally { 1920 Binder.restoreCallingIdentity(token); 1921 } 1922 } 1923 1924 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)1925 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1926 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) { 1927 return; 1928 } 1929 1930 final long token = Binder.clearCallingIdentity(); 1931 try { 1932 dumpInternal(pw); 1933 } finally { 1934 Binder.restoreCallingIdentity(token); 1935 } 1936 } 1937 1938 @Override handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out, ParcelFileDescriptor err, String[] args)1939 public int handleShellCommand(ParcelFileDescriptor in, 1940 ParcelFileDescriptor out, ParcelFileDescriptor err, String[] args) { 1941 getContext().enforceCallingOrSelfPermission( 1942 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS, 1943 "Permission required to use ADB color transform commands"); 1944 final long token = Binder.clearCallingIdentity(); 1945 try { 1946 return new ColorDisplayShellCommand(ColorDisplayService.this) 1947 .exec(this, in.getFileDescriptor(), out.getFileDescriptor(), 1948 err.getFileDescriptor(), 1949 args); 1950 } finally { 1951 Binder.restoreCallingIdentity(token); 1952 } 1953 } 1954 } 1955 } 1956