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