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