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