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