• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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