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