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