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