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