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