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