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