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