blob: 2dc2cf0d8e903ca0d9e37f9b141cedff69ef082a [file] [log] [blame]
Justin Klaassen911e8892016-06-21 18:24:24 -07001/*
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
Christine Franks0ada2772019-02-25 13:54:57 -080017package com.android.server.display.color;
Justin Klaassen911e8892016-06-21 18:24:24 -070018
Christine Franks83cc5412018-07-03 14:46:07 -070019import static android.hardware.display.ColorDisplayManager.AUTO_MODE_CUSTOM_TIME;
20import static android.hardware.display.ColorDisplayManager.AUTO_MODE_DISABLED;
21import static android.hardware.display.ColorDisplayManager.AUTO_MODE_TWILIGHT;
Christine Franksd154fe52019-01-04 17:17:45 -080022import static android.hardware.display.ColorDisplayManager.COLOR_MODE_AUTOMATIC;
23import static android.hardware.display.ColorDisplayManager.COLOR_MODE_BOOSTED;
24import static android.hardware.display.ColorDisplayManager.COLOR_MODE_NATURAL;
25import static android.hardware.display.ColorDisplayManager.COLOR_MODE_SATURATED;
Christine Franks531c0612019-05-16 14:29:14 -070026import static android.hardware.display.ColorDisplayManager.VENDOR_COLOR_MODE_RANGE_MAX;
27import static android.hardware.display.ColorDisplayManager.VENDOR_COLOR_MODE_RANGE_MIN;
Christine Franksa4ed3762019-01-24 15:37:04 -080028
Christine Franks0ada2772019-02-25 13:54:57 -080029import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
Christine Franks39b03112018-07-03 14:46:07 -070030
Christine Franks09c229e2018-12-14 10:37:40 -080031import android.Manifest;
Justin Klaassen639214e2016-07-14 21:00:06 -070032import android.animation.Animator;
33import android.animation.AnimatorListenerAdapter;
34import android.animation.TypeEvaluator;
35import android.animation.ValueAnimator;
Justin Klaassen4346f632016-08-08 15:01:47 -070036import android.annotation.NonNull;
Justin Klaassen908b86c2016-08-08 09:18:42 -070037import android.annotation.Nullable;
Christine Franksf3529b22019-01-03 13:20:17 -080038import android.annotation.Size;
Christine Franks55194dc2019-01-15 13:47:06 -080039import android.annotation.UserIdInt;
Justin Klaassen911e8892016-06-21 18:24:24 -070040import android.app.AlarmManager;
41import android.content.BroadcastReceiver;
42import android.content.ContentResolver;
43import android.content.Context;
44import android.content.Intent;
45import android.content.IntentFilter;
Christine Franks09c229e2018-12-14 10:37:40 -080046import android.content.pm.PackageManager;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080047import android.content.res.Resources;
Justin Klaassen2696d992016-07-11 21:26:46 -070048import android.database.ContentObserver;
Christine Franksf3529b22019-01-03 13:20:17 -080049import android.hardware.display.ColorDisplayManager;
Christine Franks83cc5412018-07-03 14:46:07 -070050import android.hardware.display.ColorDisplayManager.AutoMode;
Christine Franksd154fe52019-01-04 17:17:45 -080051import android.hardware.display.ColorDisplayManager.ColorMode;
Christine Franks39b03112018-07-03 14:46:07 -070052import android.hardware.display.IColorDisplayManager;
Christine Franks83cc5412018-07-03 14:46:07 -070053import android.hardware.display.Time;
Justin Klaassen2696d992016-07-11 21:26:46 -070054import android.net.Uri;
Justin Klaassen639214e2016-07-14 21:00:06 -070055import android.opengl.Matrix;
Christine Franks39b03112018-07-03 14:46:07 -070056import android.os.Binder;
Justin Klaassen911e8892016-06-21 18:24:24 -070057import android.os.Handler;
58import android.os.Looper;
Christine Franks09c229e2018-12-14 10:37:40 -080059import android.os.Message;
Christine Franksd154fe52019-01-04 17:17:45 -080060import android.os.SystemProperties;
Justin Klaassen911e8892016-06-21 18:24:24 -070061import android.os.UserHandle;
62import android.provider.Settings.Secure;
Christine Franks57fdde82018-07-03 14:46:07 -070063import android.provider.Settings.System;
Justin Klaassen639214e2016-07-14 21:00:06 -070064import android.util.MathUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070065import android.util.Slog;
Daniel Solomon41ee35f2019-07-11 16:29:33 -070066import android.util.SparseIntArray;
67import android.view.Display;
Christine Franks55194dc2019-01-15 13:47:06 -080068import android.view.SurfaceControl;
Christine Franks9114f462019-01-04 11:27:30 -080069import android.view.accessibility.AccessibilityManager;
Justin Klaassen639214e2016-07-14 21:00:06 -070070import android.view.animation.AnimationUtils;
Daniel Solomona4ab5672019-01-22 19:35:55 -080071
Christine Franks39b03112018-07-03 14:46:07 -070072import com.android.internal.R;
Christine Franks57fdde82018-07-03 14:46:07 -070073import com.android.internal.annotations.VisibleForTesting;
Christine Franksf3529b22019-01-03 13:20:17 -080074import com.android.internal.util.DumpUtils;
Christine Franks57fdde82018-07-03 14:46:07 -070075import com.android.server.DisplayThread;
Justin Klaassen911e8892016-06-21 18:24:24 -070076import com.android.server.SystemService;
77import com.android.server.twilight.TwilightListener;
78import com.android.server.twilight.TwilightManager;
79import com.android.server.twilight.TwilightState;
Christine Franksa4ed3762019-01-24 15:37:04 -080080
Christine Franksf3529b22019-01-03 13:20:17 -080081import java.io.FileDescriptor;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080082import java.io.PrintWriter;
Christine Franksf3529b22019-01-03 13:20:17 -080083import java.lang.ref.WeakReference;
Christine Franks57fdde82018-07-03 14:46:07 -070084import java.time.DateTimeException;
85import java.time.Instant;
Christine Franks03213462017-08-25 13:57:26 -070086import java.time.LocalDateTime;
87import java.time.LocalTime;
88import java.time.ZoneId;
Christine Franks57fdde82018-07-03 14:46:07 -070089import java.time.format.DateTimeParseException;
Christine Franks8ad71492017-10-24 19:04:22 -070090
Justin Klaassen911e8892016-06-21 18:24:24 -070091/**
Christine Franks39b03112018-07-03 14:46:07 -070092 * Controls the display's color transforms.
Justin Klaassen911e8892016-06-21 18:24:24 -070093 */
Christine Franks57fdde82018-07-03 14:46:07 -070094public final class ColorDisplayService extends SystemService {
Justin Klaassen911e8892016-06-21 18:24:24 -070095
Christine Franks7119e992019-03-14 17:28:21 -070096 static final String TAG = "ColorDisplayService";
Christine Franks7b83b4282017-01-18 14:55:00 -080097
98 /**
Justin Klaassen639214e2016-07-14 21:00:06 -070099 * The identity matrix, used if one of the given matrices is {@code null}.
100 */
Christine Franks6caba0f2019-03-07 17:48:25 -0800101 static final float[] MATRIX_IDENTITY = new float[16];
Christine Franks57fdde82018-07-03 14:46:07 -0700102
Justin Klaassen639214e2016-07-14 21:00:06 -0700103 static {
104 Matrix.setIdentityM(MATRIX_IDENTITY, 0);
105 }
106
Christine Franks7119e992019-03-14 17:28:21 -0700107 /**
108 * The transition time, in milliseconds, for Night Display to turn on/off.
109 */
110 private static final long TRANSITION_DURATION = 3000L;
111
Christine Franks27912a32019-04-02 10:43:10 -0700112 private static final int MSG_USER_CHANGED = 0;
113 private static final int MSG_SET_UP = 1;
114 private static final int MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE = 2;
115 private static final int MSG_APPLY_NIGHT_DISPLAY_ANIMATED = 3;
116 private static final int MSG_APPLY_GLOBAL_SATURATION = 4;
117 private static final int MSG_APPLY_DISPLAY_WHITE_BALANCE = 5;
Christine Franks09c229e2018-12-14 10:37:40 -0800118
Justin Klaassen639214e2016-07-14 21:00:06 -0700119 /**
Christine Franks83cc5412018-07-03 14:46:07 -0700120 * Return value if a setting has not been set.
121 */
122 private static final int NOT_SET = -1;
123
124 /**
Justin Klaassen639214e2016-07-14 21:00:06 -0700125 * Evaluator used to animate color matrix transitions.
126 */
127 private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
128
Christine Franks83cc5412018-07-03 14:46:07 -0700129 private final NightDisplayTintController mNightDisplayTintController =
130 new NightDisplayTintController();
Christine Franks245ffd42018-11-16 13:45:14 -0800131
Long Ling1d3f1892019-02-06 12:34:02 -0800132 @VisibleForTesting
133 final DisplayWhiteBalanceTintController mDisplayWhiteBalanceTintController =
134 new DisplayWhiteBalanceTintController();
Christine Franks245ffd42018-11-16 13:45:14 -0800135
Christine Franks7119e992019-03-14 17:28:21 -0700136 private final TintController mGlobalSaturationTintController =
137 new GlobalSaturationTintController();
Christine Franks09c229e2018-12-14 10:37:40 -0800138
Christine Franks9114f462019-01-04 11:27:30 -0800139 /**
140 * Matrix and offset used for converting color to grayscale.
141 */
142 private static final float[] MATRIX_GRAYSCALE = new float[]{
143 .2126f, .2126f, .2126f, 0f,
144 .7152f, .7152f, .7152f, 0f,
145 .0722f, .0722f, .0722f, 0f,
146 0f, 0f, 0f, 1f
147 };
148
149 /**
150 * Matrix and offset used for luminance inversion. Represents a transform from RGB to YIQ color
151 * space, rotation around the Y axis by 180 degrees, transform back to RGB color space, and
152 * subtraction from 1. The last row represents a non-multiplied addition, see surfaceflinger's
153 * ProgramCache for full implementation details.
154 */
Christine Franksd154fe52019-01-04 17:17:45 -0800155 private static final float[] MATRIX_INVERT_COLOR = new float[]{
Christine Franks9114f462019-01-04 11:27:30 -0800156 0.402f, -0.598f, -0.599f, 0f,
157 -1.174f, -0.174f, -1.175f, 0f,
158 -0.228f, -0.228f, 0.772f, 0f,
159 1f, 1f, 1f, 1f
160 };
161
Justin Klaassen2696d992016-07-11 21:26:46 -0700162 private final Handler mHandler;
163
Christine Franksf3529b22019-01-03 13:20:17 -0800164 private final AppSaturationController mAppSaturationController = new AppSaturationController();
165
Justin Klaassen911e8892016-06-21 18:24:24 -0700166 private int mCurrentUser = UserHandle.USER_NULL;
Justin Klaassen2696d992016-07-11 21:26:46 -0700167 private ContentObserver mUserSetupObserver;
Justin Klaassen911e8892016-06-21 18:24:24 -0700168 private boolean mBootCompleted;
169
Christine Franks57fdde82018-07-03 14:46:07 -0700170 private ContentObserver mContentObserver;
Christine Franks57fdde82018-07-03 14:46:07 -0700171
Christine Franks245ffd42018-11-16 13:45:14 -0800172 private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
173
Christine Franks57fdde82018-07-03 14:46:07 -0700174 private NightDisplayAutoMode mNightDisplayAutoMode;
Justin Klaassen911e8892016-06-21 18:24:24 -0700175
Daniel Solomon41ee35f2019-07-11 16:29:33 -0700176 /**
177 * Map of color modes -> display composition colorspace
178 */
179 private SparseIntArray mColorModeCompositionColorSpaces = null;
180
Christine Franks5397f032017-11-01 18:35:16 -0700181 public ColorDisplayService(Context context) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700182 super(context);
Christine Franks83cc5412018-07-03 14:46:07 -0700183 mHandler = new TintHandler(DisplayThread.get().getLooper());
Justin Klaassen911e8892016-06-21 18:24:24 -0700184 }
185
186 @Override
187 public void onStart() {
Christine Franks39b03112018-07-03 14:46:07 -0700188 publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
Christine Franks245ffd42018-11-16 13:45:14 -0800189 publishLocalService(ColorDisplayServiceInternal.class, new ColorDisplayServiceInternal());
Christine Franks0ada2772019-02-25 13:54:57 -0800190 publishLocalService(DisplayTransformManager.class, new DisplayTransformManager());
Justin Klaassen911e8892016-06-21 18:24:24 -0700191 }
192
193 @Override
Justin Klaassen2696d992016-07-11 21:26:46 -0700194 public void onBootPhase(int phase) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800195 if (phase >= PHASE_BOOT_COMPLETED) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700196 mBootCompleted = true;
197
198 // Register listeners now that boot is complete.
199 if (mCurrentUser != UserHandle.USER_NULL && mUserSetupObserver == null) {
Christine Franks27912a32019-04-02 10:43:10 -0700200 mHandler.sendEmptyMessage(MSG_SET_UP);
Justin Klaassen2696d992016-07-11 21:26:46 -0700201 }
202 }
203 }
204
205 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700206 public void onStartUser(int userHandle) {
207 super.onStartUser(userHandle);
208
Justin Klaassen911e8892016-06-21 18:24:24 -0700209 if (mCurrentUser == UserHandle.USER_NULL) {
Christine Franks27912a32019-04-02 10:43:10 -0700210 final Message message = mHandler.obtainMessage(MSG_USER_CHANGED);
211 message.arg1 = userHandle;
212 mHandler.sendMessage(message);
Justin Klaassen911e8892016-06-21 18:24:24 -0700213 }
214 }
215
216 @Override
217 public void onSwitchUser(int userHandle) {
218 super.onSwitchUser(userHandle);
219
Christine Franks27912a32019-04-02 10:43:10 -0700220 final Message message = mHandler.obtainMessage(MSG_USER_CHANGED);
221 message.arg1 = userHandle;
222 mHandler.sendMessage(message);
Justin Klaassen911e8892016-06-21 18:24:24 -0700223 }
224
225 @Override
226 public void onStopUser(int userHandle) {
227 super.onStopUser(userHandle);
228
Justin Klaassen911e8892016-06-21 18:24:24 -0700229 if (mCurrentUser == userHandle) {
Christine Franks27912a32019-04-02 10:43:10 -0700230 final Message message = mHandler.obtainMessage(MSG_USER_CHANGED);
231 message.arg1 = UserHandle.USER_NULL;
232 mHandler.sendMessage(message);
Justin Klaassen911e8892016-06-21 18:24:24 -0700233 }
234 }
235
Christine Franks771f5fa2019-06-12 13:31:50 -0700236 @VisibleForTesting void onUserChanged(int userHandle) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700237 final ContentResolver cr = getContext().getContentResolver();
Justin Klaassen911e8892016-06-21 18:24:24 -0700238
Justin Klaassen2696d992016-07-11 21:26:46 -0700239 if (mCurrentUser != UserHandle.USER_NULL) {
240 if (mUserSetupObserver != null) {
241 cr.unregisterContentObserver(mUserSetupObserver);
242 mUserSetupObserver = null;
243 } else if (mBootCompleted) {
244 tearDown();
245 }
246 }
247
248 mCurrentUser = userHandle;
249
250 if (mCurrentUser != UserHandle.USER_NULL) {
251 if (!isUserSetupCompleted(cr, mCurrentUser)) {
252 mUserSetupObserver = new ContentObserver(mHandler) {
253 @Override
254 public void onChange(boolean selfChange, Uri uri) {
255 if (isUserSetupCompleted(cr, mCurrentUser)) {
256 cr.unregisterContentObserver(this);
257 mUserSetupObserver = null;
258
259 if (mBootCompleted) {
260 setUp();
261 }
262 }
263 }
264 };
265 cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
Christine Franks39b03112018-07-03 14:46:07 -0700266 false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
Justin Klaassen2696d992016-07-11 21:26:46 -0700267 } else if (mBootCompleted) {
268 setUp();
Justin Klaassen911e8892016-06-21 18:24:24 -0700269 }
270 }
271 }
272
Justin Klaassen2696d992016-07-11 21:26:46 -0700273 private static boolean isUserSetupCompleted(ContentResolver cr, int userHandle) {
274 return Secure.getIntForUser(cr, Secure.USER_SETUP_COMPLETE, 0, userHandle) == 1;
275 }
276
Daniel Solomon41ee35f2019-07-11 16:29:33 -0700277 private void setUpDisplayCompositionColorSpaces(Resources res) {
278 mColorModeCompositionColorSpaces = null;
279
280 final int[] colorModes = res.getIntArray(R.array.config_displayCompositionColorModes);
281 if (colorModes == null) {
282 return;
283 }
284
285 final int[] compSpaces = res.getIntArray(R.array.config_displayCompositionColorSpaces);
286 if (compSpaces == null) {
287 return;
288 }
289
290 if (colorModes.length != compSpaces.length) {
291 Slog.e(TAG, "Number of composition color spaces doesn't match specified color modes");
292 return;
293 }
294
295 mColorModeCompositionColorSpaces = new SparseIntArray(colorModes.length);
296 for (int i = 0; i < colorModes.length; i++) {
297 mColorModeCompositionColorSpaces.put(colorModes[i], compSpaces[i]);
298 }
299 }
300
Justin Klaassen2696d992016-07-11 21:26:46 -0700301 private void setUp() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700302 Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
303
Christine Franks57fdde82018-07-03 14:46:07 -0700304 // Listen for external changes to any of the settings.
305 if (mContentObserver == null) {
Christine Franks27912a32019-04-02 10:43:10 -0700306 mContentObserver = new ContentObserver(mHandler) {
Christine Franks57fdde82018-07-03 14:46:07 -0700307 @Override
308 public void onChange(boolean selfChange, Uri uri) {
309 super.onChange(selfChange, uri);
310
311 final String setting = uri == null ? null : uri.getLastPathSegment();
312 if (setting != null) {
313 switch (setting) {
314 case Secure.NIGHT_DISPLAY_ACTIVATED:
Christine Franks78a4dd42019-02-08 11:09:30 -0800315 final boolean activated = mNightDisplayTintController
316 .isActivatedSetting();
Christine Franks83cc5412018-07-03 14:46:07 -0700317 if (mNightDisplayTintController.isActivatedStateNotSet()
318 || mNightDisplayTintController.isActivated() != activated) {
Christine Franks78a4dd42019-02-08 11:09:30 -0800319 mNightDisplayTintController.setActivated(activated);
Christine Franks83cc5412018-07-03 14:46:07 -0700320 }
Christine Franks57fdde82018-07-03 14:46:07 -0700321 break;
322 case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
Christine Franks78a4dd42019-02-08 11:09:30 -0800323 final int temperature = mNightDisplayTintController
324 .getColorTemperatureSetting();
Christine Franks83cc5412018-07-03 14:46:07 -0700325 if (mNightDisplayTintController.getColorTemperature()
326 != temperature) {
327 mNightDisplayTintController
328 .onColorTemperatureChanged(temperature);
329 }
Christine Franks57fdde82018-07-03 14:46:07 -0700330 break;
331 case Secure.NIGHT_DISPLAY_AUTO_MODE:
Christine Franks83cc5412018-07-03 14:46:07 -0700332 onNightDisplayAutoModeChanged(getNightDisplayAutoModeInternal());
Christine Franks57fdde82018-07-03 14:46:07 -0700333 break;
334 case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
335 onNightDisplayCustomStartTimeChanged(
Christine Franks83cc5412018-07-03 14:46:07 -0700336 getNightDisplayCustomStartTimeInternal().getLocalTime());
Christine Franks57fdde82018-07-03 14:46:07 -0700337 break;
338 case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
339 onNightDisplayCustomEndTimeChanged(
Christine Franks83cc5412018-07-03 14:46:07 -0700340 getNightDisplayCustomEndTimeInternal().getLocalTime());
Christine Franks57fdde82018-07-03 14:46:07 -0700341 break;
342 case System.DISPLAY_COLOR_MODE:
Christine Franks71e003e2019-01-24 14:40:20 -0800343 onDisplayColorModeChanged(getColorModeInternal());
Christine Franks57fdde82018-07-03 14:46:07 -0700344 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700345 case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
Christine Franks9114f462019-01-04 11:27:30 -0800346 onAccessibilityInversionChanged();
347 onAccessibilityActivated();
348 break;
349 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
350 onAccessibilityDaltonizerChanged();
351 onAccessibilityActivated();
352 break;
353 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER:
354 onAccessibilityDaltonizerChanged();
Christine Franks57fdde82018-07-03 14:46:07 -0700355 break;
Christine Franks245ffd42018-11-16 13:45:14 -0800356 case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800357 updateDisplayWhiteBalanceStatus();
Christine Franks245ffd42018-11-16 13:45:14 -0800358 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700359 }
360 }
361 }
362 };
363 }
364 final ContentResolver cr = getContext().getContentResolver();
365 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
366 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
367 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
368 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
369 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
370 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
371 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
372 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
373 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
374 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
375 cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
376 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
377 cr.registerContentObserver(
378 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
379 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
380 cr.registerContentObserver(
381 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
382 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks9114f462019-01-04 11:27:30 -0800383 cr.registerContentObserver(
384 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER),
385 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks245ffd42018-11-16 13:45:14 -0800386 cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED),
387 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Justin Klaassen911e8892016-06-21 18:24:24 -0700388
Christine Franks11e63152019-03-14 11:16:06 -0700389 // Apply the accessibility settings first, since they override most other settings.
390 onAccessibilityInversionChanged();
391 onAccessibilityDaltonizerChanged();
392
Daniel Solomon41ee35f2019-07-11 16:29:33 -0700393 setUpDisplayCompositionColorSpaces(getContext().getResources());
394
Christine Frankscf388c22018-05-15 15:48:10 -0700395 // Set the color mode, if valid, and immediately apply the updated tint matrix based on the
396 // existing activated state. This ensures consistency of tint across the color mode change.
Christine Franks71e003e2019-01-24 14:40:20 -0800397 onDisplayColorModeChanged(getColorModeInternal());
Christine Frankscf388c22018-05-15 15:48:10 -0700398
Christine Franksa4ed3762019-01-24 15:37:04 -0800399 if (mNightDisplayTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800400 // Reset the activated state.
401 mNightDisplayTintController.setActivated(null);
Christine Frankscf388c22018-05-15 15:48:10 -0700402
Christine Franks245ffd42018-11-16 13:45:14 -0800403 // Prepare the night display color transformation matrix.
404 mNightDisplayTintController
405 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
Christine Franks78a4dd42019-02-08 11:09:30 -0800406 mNightDisplayTintController
407 .setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
Christine Franks8ad71492017-10-24 19:04:22 -0700408
Christine Franks245ffd42018-11-16 13:45:14 -0800409 // Initialize the current auto mode.
Christine Franks83cc5412018-07-03 14:46:07 -0700410 onNightDisplayAutoModeChanged(getNightDisplayAutoModeInternal());
Christine Franks6418d0b2017-02-13 09:48:00 -0800411
Christine Franks83cc5412018-07-03 14:46:07 -0700412 // Force the initialization of the current saved activation state.
Christine Franks245ffd42018-11-16 13:45:14 -0800413 if (mNightDisplayTintController.isActivatedStateNotSet()) {
Christine Franks78a4dd42019-02-08 11:09:30 -0800414 mNightDisplayTintController
415 .setActivated(mNightDisplayTintController.isActivatedSetting());
Christine Franks245ffd42018-11-16 13:45:14 -0800416 }
417 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700418
Christine Franksa4ed3762019-01-24 15:37:04 -0800419 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800420 // Prepare the display white balance transform matrix.
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800421 mDisplayWhiteBalanceTintController.setUp(getContext(), true /* needsLinear */);
Christine Franks245ffd42018-11-16 13:45:14 -0800422
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800423 updateDisplayWhiteBalanceStatus();
Justin Klaassen911e8892016-06-21 18:24:24 -0700424 }
425 }
426
Justin Klaassen2696d992016-07-11 21:26:46 -0700427 private void tearDown() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700428 Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
429
Keun young Parka2162f52019-05-29 11:33:11 -0700430 if (mContentObserver != null) {
431 getContext().getContentResolver().unregisterContentObserver(mContentObserver);
432 }
Christine Franks57fdde82018-07-03 14:46:07 -0700433
Christine Franksa4ed3762019-01-24 15:37:04 -0800434 if (mNightDisplayTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800435 if (mNightDisplayAutoMode != null) {
436 mNightDisplayAutoMode.onStop();
437 mNightDisplayAutoMode = null;
438 }
439 mNightDisplayTintController.endAnimator();
Justin Klaassen911e8892016-06-21 18:24:24 -0700440 }
441
Christine Franksa4ed3762019-01-24 15:37:04 -0800442 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
Christine Franks245ffd42018-11-16 13:45:14 -0800443 mDisplayWhiteBalanceTintController.endAnimator();
Justin Klaassen639214e2016-07-14 21:00:06 -0700444 }
Christine Franks6d21d342019-02-07 15:09:03 -0800445
446 if (mGlobalSaturationTintController.isAvailable(getContext())) {
447 mGlobalSaturationTintController.setActivated(null);
448 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700449 }
450
Christine Franks57fdde82018-07-03 14:46:07 -0700451 private void onNightDisplayAutoModeChanged(int autoMode) {
452 Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700453
Christine Franks57fdde82018-07-03 14:46:07 -0700454 if (mNightDisplayAutoMode != null) {
455 mNightDisplayAutoMode.onStop();
456 mNightDisplayAutoMode = null;
Justin Klaassen911e8892016-06-21 18:24:24 -0700457 }
458
Christine Franks83cc5412018-07-03 14:46:07 -0700459 if (autoMode == AUTO_MODE_CUSTOM_TIME) {
Christine Franks57fdde82018-07-03 14:46:07 -0700460 mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
Christine Franks83cc5412018-07-03 14:46:07 -0700461 } else if (autoMode == AUTO_MODE_TWILIGHT) {
Christine Franks57fdde82018-07-03 14:46:07 -0700462 mNightDisplayAutoMode = new TwilightNightDisplayAutoMode();
Justin Klaassen911e8892016-06-21 18:24:24 -0700463 }
464
Christine Franks57fdde82018-07-03 14:46:07 -0700465 if (mNightDisplayAutoMode != null) {
466 mNightDisplayAutoMode.onStart();
Justin Klaassen911e8892016-06-21 18:24:24 -0700467 }
468 }
469
Christine Franks57fdde82018-07-03 14:46:07 -0700470 private void onNightDisplayCustomStartTimeChanged(LocalTime startTime) {
471 Slog.d(TAG, "onNightDisplayCustomStartTimeChanged: startTime=" + startTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700472
Christine Franks57fdde82018-07-03 14:46:07 -0700473 if (mNightDisplayAutoMode != null) {
474 mNightDisplayAutoMode.onCustomStartTimeChanged(startTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700475 }
476 }
477
Christine Franks57fdde82018-07-03 14:46:07 -0700478 private void onNightDisplayCustomEndTimeChanged(LocalTime endTime) {
479 Slog.d(TAG, "onNightDisplayCustomEndTimeChanged: endTime=" + endTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700480
Christine Franks57fdde82018-07-03 14:46:07 -0700481 if (mNightDisplayAutoMode != null) {
482 mNightDisplayAutoMode.onCustomEndTimeChanged(endTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700483 }
484 }
485
Daniel Solomon41ee35f2019-07-11 16:29:33 -0700486 private int getCompositionColorSpace(int mode) {
487 if (mColorModeCompositionColorSpaces == null) {
488 return Display.COLOR_MODE_INVALID;
489 }
490
491 return mColorModeCompositionColorSpaces.get(mode, Display.COLOR_MODE_INVALID);
492 }
493
Christine Franks57fdde82018-07-03 14:46:07 -0700494 private void onDisplayColorModeChanged(int mode) {
Christine Franks83cc5412018-07-03 14:46:07 -0700495 if (mode == NOT_SET) {
Christine Frankscf388c22018-05-15 15:48:10 -0700496 return;
497 }
498
Christine Franks245ffd42018-11-16 13:45:14 -0800499 mNightDisplayTintController.cancelAnimator();
500 mDisplayWhiteBalanceTintController.cancelAnimator();
501
Christine Franksa4ed3762019-01-24 15:37:04 -0800502 if (mNightDisplayTintController.isAvailable(getContext())) {
503 mNightDisplayTintController
504 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
Christine Franks78a4dd42019-02-08 11:09:30 -0800505 mNightDisplayTintController
506 .setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
Christine Franksa4ed3762019-01-24 15:37:04 -0800507 }
Christine Franks245ffd42018-11-16 13:45:14 -0800508
Anthony Han2c2e4a82019-06-07 11:13:35 -0700509 // dtm.setColorMode() needs to be called before
510 // updateDisplayWhiteBalanceStatus(), this is because the latter calls
511 // DisplayTransformManager.needsLinearColorMatrix(), therefore it is dependent
512 // on the state of DisplayTransformManager.
513 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Daniel Solomon41ee35f2019-07-11 16:29:33 -0700514 dtm.setColorMode(mode, mNightDisplayTintController.getMatrix(),
515 getCompositionColorSpace(mode));
Anthony Han2c2e4a82019-06-07 11:13:35 -0700516
Christine Franks18ac4e22019-04-05 18:30:50 -0700517 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
518 updateDisplayWhiteBalanceStatus();
519 }
Christine Franks8ad71492017-10-24 19:04:22 -0700520 }
521
Christine Franks9114f462019-01-04 11:27:30 -0800522 private void onAccessibilityActivated() {
Christine Franks71e003e2019-01-24 14:40:20 -0800523 onDisplayColorModeChanged(getColorModeInternal());
Daniel Solomon317a3572018-03-30 18:36:37 -0700524 }
525
Daniel Solomonff24ba92019-04-11 19:29:31 -0700526 private boolean isAccessiblityDaltonizerEnabled() {
527 return Secure.getIntForUser(getContext().getContentResolver(),
528 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, mCurrentUser) != 0;
529 }
530
531 private boolean isAccessiblityInversionEnabled() {
532 return Secure.getIntForUser(getContext().getContentResolver(),
533 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) != 0;
534 }
535
536 private boolean isAccessibilityEnabled() {
537 return isAccessiblityDaltonizerEnabled() || isAccessiblityInversionEnabled();
538 }
539
Christine Franks9114f462019-01-04 11:27:30 -0800540 /**
541 * Apply the accessibility daltonizer transform based on the settings value.
542 */
543 private void onAccessibilityDaltonizerChanged() {
Christine Franks27912a32019-04-02 10:43:10 -0700544 if (mCurrentUser == UserHandle.USER_NULL) {
545 return;
546 }
Daniel Solomonff24ba92019-04-11 19:29:31 -0700547 final int daltonizerMode = isAccessiblityDaltonizerEnabled()
548 ? Secure.getIntForUser(getContext().getContentResolver(),
549 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
550 AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser)
Christine Franks9114f462019-01-04 11:27:30 -0800551 : AccessibilityManager.DALTONIZER_DISABLED;
552
553 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
554 if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
555 // Monochromacy isn't supported by the native Daltonizer implementation; use grayscale.
556 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE,
557 MATRIX_GRAYSCALE);
558 dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
559 } else {
560 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, null);
561 dtm.setDaltonizerMode(daltonizerMode);
562 }
563 }
564
565 /**
566 * Apply the accessibility inversion transform based on the settings value.
567 */
568 private void onAccessibilityInversionChanged() {
Christine Franks27912a32019-04-02 10:43:10 -0700569 if (mCurrentUser == UserHandle.USER_NULL) {
570 return;
571 }
Christine Franks9114f462019-01-04 11:27:30 -0800572 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
573 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR,
Daniel Solomonff24ba92019-04-11 19:29:31 -0700574 isAccessiblityInversionEnabled() ? MATRIX_INVERT_COLOR : null);
Christine Franks9114f462019-01-04 11:27:30 -0800575 }
Christine Franks8ad71492017-10-24 19:04:22 -0700576
Christine Franks6418d0b2017-02-13 09:48:00 -0800577 /**
578 * Applies current color temperature matrix, or removes it if deactivated.
579 *
580 * @param immediate {@code true} skips transition animation
581 */
Christine Franks245ffd42018-11-16 13:45:14 -0800582 private void applyTint(TintController tintController, boolean immediate) {
583 tintController.cancelAnimator();
Christine Franks6418d0b2017-02-13 09:48:00 -0800584
Christine Franks6418d0b2017-02-13 09:48:00 -0800585 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800586 final float[] from = dtm.getColorMatrix(tintController.getLevel());
587 final float[] to = tintController.getMatrix();
Christine Franks6418d0b2017-02-13 09:48:00 -0800588
589 if (immediate) {
Christine Franks245ffd42018-11-16 13:45:14 -0800590 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800591 } else {
Anthony Han1bf7c562019-09-03 18:28:30 -0700592 TintValueAnimator valueAnimator = TintValueAnimator.ofMatrix(COLOR_MATRIX_EVALUATOR,
593 from == null ? MATRIX_IDENTITY : from, to);
594 tintController.setAnimator(valueAnimator);
595 valueAnimator.setDuration(TRANSITION_DURATION);
596 valueAnimator.setInterpolator(AnimationUtils.loadInterpolator(
Christine Franks6418d0b2017-02-13 09:48:00 -0800597 getContext(), android.R.interpolator.fast_out_slow_in));
Anthony Han1bf7c562019-09-03 18:28:30 -0700598 valueAnimator.addUpdateListener((ValueAnimator animator) -> {
Christine Franks245ffd42018-11-16 13:45:14 -0800599 final float[] value = (float[]) animator.getAnimatedValue();
600 dtm.setColorMatrix(tintController.getLevel(), value);
Anthony Han1bf7c562019-09-03 18:28:30 -0700601 ((TintValueAnimator) animator).updateMinMaxComponents();
Christine Franks6418d0b2017-02-13 09:48:00 -0800602 });
Anthony Han1bf7c562019-09-03 18:28:30 -0700603 valueAnimator.addListener(new AnimatorListenerAdapter() {
Christine Franks6418d0b2017-02-13 09:48:00 -0800604
605 private boolean mIsCancelled;
606
607 @Override
608 public void onAnimationCancel(Animator animator) {
609 mIsCancelled = true;
610 }
611
612 @Override
613 public void onAnimationEnd(Animator animator) {
Anthony Han1bf7c562019-09-03 18:28:30 -0700614 TintValueAnimator t = (TintValueAnimator) animator;
Anthony Han00a490b2019-08-26 14:00:28 -0700615 Slog.d(TAG, tintController.getClass().getSimpleName()
616 + " Animation cancelled: " + mIsCancelled
Anthony Han1bf7c562019-09-03 18:28:30 -0700617 + " to matrix: " + TintController.matrixToString(to, 16)
618 + " min matrix coefficients: "
619 + TintController.matrixToString(t.getMin(), 16)
620 + " max matrix coefficients: "
621 + TintController.matrixToString(t.getMax(), 16));
Christine Franks6418d0b2017-02-13 09:48:00 -0800622 if (!mIsCancelled) {
623 // Ensure final color matrix is set at the end of the animation. If the
624 // animation is cancelled then don't set the final color matrix so the new
625 // animator can pick up from where this one left off.
Christine Franks245ffd42018-11-16 13:45:14 -0800626 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800627 }
Christine Franks245ffd42018-11-16 13:45:14 -0800628 tintController.setAnimator(null);
Christine Franks6418d0b2017-02-13 09:48:00 -0800629 }
630 });
Anthony Han1bf7c562019-09-03 18:28:30 -0700631 valueAnimator.start();
Christine Franks6418d0b2017-02-13 09:48:00 -0800632 }
633 }
634
635 /**
Christine Franks39b03112018-07-03 14:46:07 -0700636 * Returns the first date time corresponding to the local time that occurs before the provided
637 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700638 *
639 * @param compareTime the LocalDateTime to compare against
640 * @return the prior LocalDateTime corresponding to this local time
641 */
Christine Franks57fdde82018-07-03 14:46:07 -0700642 @VisibleForTesting
643 static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700644 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
645 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
646
647 // Check if the local time has passed, if so return the same time yesterday.
648 return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
649 }
650
651 /**
Christine Franks39b03112018-07-03 14:46:07 -0700652 * Returns the first date time corresponding to this local time that occurs after the provided
653 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700654 *
655 * @param compareTime the LocalDateTime to compare against
656 * @return the next LocalDateTime corresponding to this local time
657 */
Christine Franks57fdde82018-07-03 14:46:07 -0700658 @VisibleForTesting
659 static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700660 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
661 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
662
663 // Check if the local time has passed, if so return the same time tomorrow.
664 return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
665 }
666
Long Ling1d3f1892019-02-06 12:34:02 -0800667 @VisibleForTesting
668 void updateDisplayWhiteBalanceStatus() {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800669 boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
Christine Franks0ada2772019-02-25 13:54:57 -0800670 mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled()
671 && !mNightDisplayTintController.isActivated()
Daniel Solomonff24ba92019-04-11 19:29:31 -0700672 && !isAccessibilityEnabled()
Christine Franks0ada2772019-02-25 13:54:57 -0800673 && DisplayTransformManager.needsLinearColorMatrix());
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800674 boolean activated = mDisplayWhiteBalanceTintController.isActivated();
675
676 if (mDisplayWhiteBalanceListener != null && oldActivated != activated) {
677 mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(activated);
Christine Franks245ffd42018-11-16 13:45:14 -0800678 }
Daniel Solomon86508f82019-01-18 19:01:02 -0800679
680 // If disabled, clear the tint. If enabled, do nothing more here and let the next
681 // temperature update set the correct tint.
682 if (!activated) {
Christine Franksc7fb9452019-02-04 08:45:33 -0800683 mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE);
Daniel Solomon86508f82019-01-18 19:01:02 -0800684 }
Christine Franks245ffd42018-11-16 13:45:14 -0800685 }
686
Christine Franks66783a82019-03-28 11:45:56 -0700687 private boolean setDisplayWhiteBalanceSettingEnabled(boolean enabled) {
688 if (mCurrentUser == UserHandle.USER_NULL) {
689 return false;
690 }
691 return Secure.putIntForUser(getContext().getContentResolver(),
692 Secure.DISPLAY_WHITE_BALANCE_ENABLED,
693 enabled ? 1 : 0, mCurrentUser);
694 }
695
Christine Franks245ffd42018-11-16 13:45:14 -0800696 private boolean isDisplayWhiteBalanceSettingEnabled() {
Christine Franks27912a32019-04-02 10:43:10 -0700697 if (mCurrentUser == UserHandle.USER_NULL) {
698 return false;
699 }
Christine Franks245ffd42018-11-16 13:45:14 -0800700 return Secure.getIntForUser(getContext().getContentResolver(),
Christine Franks18ac4e22019-04-05 18:30:50 -0700701 Secure.DISPLAY_WHITE_BALANCE_ENABLED,
702 getContext().getResources()
703 .getBoolean(R.bool.config_displayWhiteBalanceEnabledDefault) ? 1
704 : 0,
705 mCurrentUser) == 1;
Christine Franks245ffd42018-11-16 13:45:14 -0800706 }
707
Christine Franks39b03112018-07-03 14:46:07 -0700708 private boolean isDeviceColorManagedInternal() {
709 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
710 return dtm.isDeviceColorManaged();
711 }
712
Christine Franks55194dc2019-01-15 13:47:06 -0800713 private int getTransformCapabilitiesInternal() {
714 int availabilityFlags = ColorDisplayManager.CAPABILITY_NONE;
715 if (SurfaceControl.getProtectedContentSupport()) {
716 availabilityFlags |= ColorDisplayManager.CAPABILITY_PROTECTED_CONTENT;
717 }
718 final Resources res = getContext().getResources();
719 if (res.getBoolean(R.bool.config_setColorTransformAccelerated)) {
720 availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_GLOBAL;
721 }
722 if (res.getBoolean(R.bool.config_setColorTransformAcceleratedPerLayer)) {
723 availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_PER_APP;
724 }
725 return availabilityFlags;
726 }
727
Christine Franks83cc5412018-07-03 14:46:07 -0700728 private boolean setNightDisplayAutoModeInternal(@AutoMode int autoMode) {
729 if (getNightDisplayAutoModeInternal() != autoMode) {
730 Secure.putStringForUser(getContext().getContentResolver(),
731 Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
732 null,
733 mCurrentUser);
734 }
735 return Secure.putIntForUser(getContext().getContentResolver(),
736 Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mCurrentUser);
737 }
738
739 private int getNightDisplayAutoModeInternal() {
740 int autoMode = getNightDisplayAutoModeRawInternal();
741 if (autoMode == NOT_SET) {
742 autoMode = getContext().getResources().getInteger(
743 R.integer.config_defaultNightDisplayAutoMode);
744 }
745 if (autoMode != AUTO_MODE_DISABLED
746 && autoMode != AUTO_MODE_CUSTOM_TIME
747 && autoMode != AUTO_MODE_TWILIGHT) {
748 Slog.e(TAG, "Invalid autoMode: " + autoMode);
749 autoMode = AUTO_MODE_DISABLED;
750 }
751 return autoMode;
752 }
753
754 private int getNightDisplayAutoModeRawInternal() {
Christine Franks27912a32019-04-02 10:43:10 -0700755 if (mCurrentUser == UserHandle.USER_NULL) {
756 return NOT_SET;
757 }
Christine Franks83cc5412018-07-03 14:46:07 -0700758 return Secure
759 .getIntForUser(getContext().getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE,
760 NOT_SET, mCurrentUser);
761 }
762
763 private Time getNightDisplayCustomStartTimeInternal() {
764 int startTimeValue = Secure.getIntForUser(getContext().getContentResolver(),
765 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, NOT_SET, mCurrentUser);
766 if (startTimeValue == NOT_SET) {
767 startTimeValue = getContext().getResources().getInteger(
768 R.integer.config_defaultNightDisplayCustomStartTime);
769 }
770 return new Time(LocalTime.ofSecondOfDay(startTimeValue / 1000));
771 }
772
773 private boolean setNightDisplayCustomStartTimeInternal(Time startTime) {
774 return Secure.putIntForUser(getContext().getContentResolver(),
775 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME,
776 startTime.getLocalTime().toSecondOfDay() * 1000,
777 mCurrentUser);
778 }
779
780 private Time getNightDisplayCustomEndTimeInternal() {
781 int endTimeValue = Secure.getIntForUser(getContext().getContentResolver(),
782 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, NOT_SET, mCurrentUser);
783 if (endTimeValue == NOT_SET) {
784 endTimeValue = getContext().getResources().getInteger(
785 R.integer.config_defaultNightDisplayCustomEndTime);
786 }
787 return new Time(LocalTime.ofSecondOfDay(endTimeValue / 1000));
788 }
789
790 private boolean setNightDisplayCustomEndTimeInternal(Time endTime) {
791 return Secure.putIntForUser(getContext().getContentResolver(),
792 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.getLocalTime().toSecondOfDay() * 1000,
793 mCurrentUser);
794 }
795
Christine Franks57fdde82018-07-03 14:46:07 -0700796 /**
797 * Returns the last time the night display transform activation state was changed, or {@link
798 * LocalDateTime#MIN} if night display has never been activated.
799 */
Christine Franks245ffd42018-11-16 13:45:14 -0800800 private LocalDateTime getNightDisplayLastActivatedTimeSetting() {
Christine Franks57fdde82018-07-03 14:46:07 -0700801 final ContentResolver cr = getContext().getContentResolver();
802 final String lastActivatedTime = Secure.getStringForUser(
803 cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
804 if (lastActivatedTime != null) {
805 try {
806 return LocalDateTime.parse(lastActivatedTime);
807 } catch (DateTimeParseException ignored) {
808 }
809 // Uses the old epoch time.
810 try {
811 return LocalDateTime.ofInstant(
812 Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
813 ZoneId.systemDefault());
814 } catch (DateTimeException | NumberFormatException ignored) {
815 }
816 }
817 return LocalDateTime.MIN;
818 }
819
Christine Franksf3529b22019-01-03 13:20:17 -0800820 private boolean setAppSaturationLevelInternal(String packageName, int saturationLevel) {
821 return mAppSaturationController
822 .setSaturationLevel(packageName, mCurrentUser, saturationLevel);
823 }
824
Christine Franksd154fe52019-01-04 17:17:45 -0800825 private void setColorModeInternal(@ColorMode int colorMode) {
826 if (!isColorModeAvailable(colorMode)) {
827 throw new IllegalArgumentException("Invalid colorMode: " + colorMode);
828 }
829 System.putIntForUser(getContext().getContentResolver(), System.DISPLAY_COLOR_MODE,
830 colorMode,
831 mCurrentUser);
832 }
833
Christine Franks71e003e2019-01-24 14:40:20 -0800834 private @ColorMode int getColorModeInternal() {
Christine Franksd154fe52019-01-04 17:17:45 -0800835 final ContentResolver cr = getContext().getContentResolver();
Daniel Solomonff24ba92019-04-11 19:29:31 -0700836 if (isAccessibilityEnabled()) {
Christine Franksd154fe52019-01-04 17:17:45 -0800837 // There are restrictions on the available color modes combined with a11y transforms.
Christine Franksb8d03a52019-05-08 15:08:21 -0700838 final int a11yColorMode = getContext().getResources().getInteger(
839 R.integer.config_accessibilityColorMode);
840 if (a11yColorMode >= 0) {
841 return a11yColorMode;
Christine Franksd154fe52019-01-04 17:17:45 -0800842 }
843 }
844
845 int colorMode = System.getIntForUser(cr, System.DISPLAY_COLOR_MODE, -1, mCurrentUser);
846 if (colorMode == -1) {
847 // There might be a system property controlling color mode that we need to respect; if
848 // not, this will set a suitable default.
849 colorMode = getCurrentColorModeFromSystemProperties();
850 }
851
852 // This happens when a color mode is no longer available (e.g., after system update or B&R)
853 // or the device does not support any color mode.
854 if (!isColorModeAvailable(colorMode)) {
855 if (colorMode == COLOR_MODE_BOOSTED && isColorModeAvailable(COLOR_MODE_NATURAL)) {
856 colorMode = COLOR_MODE_NATURAL;
857 } else if (colorMode == COLOR_MODE_SATURATED
858 && isColorModeAvailable(COLOR_MODE_AUTOMATIC)) {
859 colorMode = COLOR_MODE_AUTOMATIC;
860 } else if (colorMode == COLOR_MODE_AUTOMATIC
861 && isColorModeAvailable(COLOR_MODE_SATURATED)) {
862 colorMode = COLOR_MODE_SATURATED;
863 } else {
864 colorMode = -1;
865 }
866 }
867
868 return colorMode;
869 }
870
871 /**
872 * Get the current color mode from system properties, or return -1 if invalid.
873 *
Christine Franks0ada2772019-02-25 13:54:57 -0800874 * See {@link DisplayTransformManager}
Christine Franksd154fe52019-01-04 17:17:45 -0800875 */
Christine Franks78a4dd42019-02-08 11:09:30 -0800876 private @ColorMode int getCurrentColorModeFromSystemProperties() {
Christine Franksd154fe52019-01-04 17:17:45 -0800877 final int displayColorSetting = SystemProperties.getInt("persist.sys.sf.native_mode", 0);
878 if (displayColorSetting == 0) {
879 return "1.0".equals(SystemProperties.get("persist.sys.sf.color_saturation"))
880 ? COLOR_MODE_NATURAL : COLOR_MODE_BOOSTED;
881 } else if (displayColorSetting == 1) {
882 return COLOR_MODE_SATURATED;
883 } else if (displayColorSetting == 2) {
884 return COLOR_MODE_AUTOMATIC;
Christine Franks531c0612019-05-16 14:29:14 -0700885 } else if (displayColorSetting >= VENDOR_COLOR_MODE_RANGE_MIN
886 && displayColorSetting <= VENDOR_COLOR_MODE_RANGE_MAX) {
887 return displayColorSetting;
Christine Franksd154fe52019-01-04 17:17:45 -0800888 } else {
889 return -1;
890 }
891 }
892
893 private boolean isColorModeAvailable(@ColorMode int colorMode) {
894 final int[] availableColorModes = getContext().getResources().getIntArray(
895 R.array.config_availableColorModes);
896 if (availableColorModes != null) {
897 for (int mode : availableColorModes) {
898 if (mode == colorMode) {
899 return true;
900 }
901 }
902 }
903 return false;
904 }
905
Christine Franksf3529b22019-01-03 13:20:17 -0800906 private void dumpInternal(PrintWriter pw) {
907 pw.println("COLOR DISPLAY MANAGER dumpsys (color_display)");
Christine Franksa4ed3762019-01-24 15:37:04 -0800908
Christine Franks0ada2772019-02-25 13:54:57 -0800909 pw.println("Night display:");
Christine Franksa4ed3762019-01-24 15:37:04 -0800910 if (mNightDisplayTintController.isAvailable(getContext())) {
Christine Franksf3529b22019-01-03 13:20:17 -0800911 pw.println(" Activated: " + mNightDisplayTintController.isActivated());
Christine Franksa4ed3762019-01-24 15:37:04 -0800912 pw.println(" Color temp: " + mNightDisplayTintController.getColorTemperature());
Christine Franksf3529b22019-01-03 13:20:17 -0800913 } else {
914 pw.println(" Not available");
915 }
Christine Franksa4ed3762019-01-24 15:37:04 -0800916
917 pw.println("Global saturation:");
918 if (mGlobalSaturationTintController.isAvailable(getContext())) {
919 pw.println(" Activated: " + mGlobalSaturationTintController.isActivated());
920 } else {
921 pw.println(" Not available");
922 }
923
Christine Franksf3529b22019-01-03 13:20:17 -0800924 mAppSaturationController.dump(pw);
Christine Franksa4ed3762019-01-24 15:37:04 -0800925
926 pw.println("Display white balance:");
927 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
928 pw.println(" Activated: " + mDisplayWhiteBalanceTintController.isActivated());
Long Ling1d3f1892019-02-06 12:34:02 -0800929 mDisplayWhiteBalanceTintController.dump(pw);
Christine Franksa4ed3762019-01-24 15:37:04 -0800930 } else {
931 pw.println(" Not available");
932 }
933
934 pw.println("Color mode: " + getColorModeInternal());
Christine Franksf3529b22019-01-03 13:20:17 -0800935 }
936
Christine Franks57fdde82018-07-03 14:46:07 -0700937 private abstract class NightDisplayAutoMode {
938
939 public abstract void onActivated(boolean activated);
940
Justin Klaassen911e8892016-06-21 18:24:24 -0700941 public abstract void onStart();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800942
Justin Klaassen911e8892016-06-21 18:24:24 -0700943 public abstract void onStop();
Christine Franks57fdde82018-07-03 14:46:07 -0700944
945 public void onCustomStartTimeChanged(LocalTime startTime) {
946 }
947
948 public void onCustomEndTimeChanged(LocalTime endTime) {
949 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700950 }
951
Christine Franks57fdde82018-07-03 14:46:07 -0700952 private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements
953 AlarmManager.OnAlarmListener {
Justin Klaassen911e8892016-06-21 18:24:24 -0700954
955 private final AlarmManager mAlarmManager;
956 private final BroadcastReceiver mTimeChangedReceiver;
957
Christine Franks03213462017-08-25 13:57:26 -0700958 private LocalTime mStartTime;
959 private LocalTime mEndTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700960
Christine Franks03213462017-08-25 13:57:26 -0700961 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700962
Christine Franks57fdde82018-07-03 14:46:07 -0700963 CustomNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -0700964 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
965 mTimeChangedReceiver = new BroadcastReceiver() {
966 @Override
967 public void onReceive(Context context, Intent intent) {
968 updateActivated();
969 }
970 };
971 }
972
973 private void updateActivated() {
Christine Franks03213462017-08-25 13:57:26 -0700974 final LocalDateTime now = LocalDateTime.now();
975 final LocalDateTime start = getDateTimeBefore(mStartTime, now);
976 final LocalDateTime end = getDateTimeAfter(mEndTime, start);
977 boolean activate = now.isBefore(end);
Justin Klaassen911e8892016-06-21 18:24:24 -0700978
Christine Frankse5bb03e2017-02-10 17:36:10 -0800979 if (mLastActivatedTime != null) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800980 // Maintain the existing activated state if within the current period.
Christine Franks0ada2772019-02-25 13:54:57 -0800981 if (mLastActivatedTime.isBefore(now)
982 && mLastActivatedTime.isAfter(start)
Christine Franks03213462017-08-25 13:57:26 -0700983 && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
Christine Franks78a4dd42019-02-08 11:09:30 -0800984 activate = mNightDisplayTintController.isActivatedSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -0700985 }
986 }
987
Christine Franks0ada2772019-02-25 13:54:57 -0800988 if (mNightDisplayTintController.isActivatedStateNotSet()
989 || (mNightDisplayTintController.isActivated() != activate)) {
Christine Franks8851ac82019-06-14 09:44:43 -0700990 mNightDisplayTintController.setActivated(activate, activate ? start : end);
Justin Klaassen911e8892016-06-21 18:24:24 -0700991 }
Christine Franks03213462017-08-25 13:57:26 -0700992
Christine Franks245ffd42018-11-16 13:45:14 -0800993 updateNextAlarm(mNightDisplayTintController.isActivated(), now);
Justin Klaassen911e8892016-06-21 18:24:24 -0700994 }
995
Christine Franks03213462017-08-25 13:57:26 -0700996 private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
Justin Klaassen4346f632016-08-08 15:01:47 -0700997 if (activated != null) {
Christine Franks03213462017-08-25 13:57:26 -0700998 final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
999 : getDateTimeAfter(mStartTime, now);
1000 final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
1001 mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
Justin Klaassen911e8892016-06-21 18:24:24 -07001002 }
1003 }
1004
1005 @Override
1006 public void onStart() {
1007 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
1008 intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
1009 getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
1010
Christine Franks83cc5412018-07-03 14:46:07 -07001011 mStartTime = getNightDisplayCustomStartTimeInternal().getLocalTime();
1012 mEndTime = getNightDisplayCustomEndTimeInternal().getLocalTime();
Justin Klaassen911e8892016-06-21 18:24:24 -07001013
Christine Franks57fdde82018-07-03 14:46:07 -07001014 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Frankse5bb03e2017-02-10 17:36:10 -08001015
Justin Klaassen911e8892016-06-21 18:24:24 -07001016 // Force an update to initialize state.
1017 updateActivated();
1018 }
1019
1020 @Override
1021 public void onStop() {
1022 getContext().unregisterReceiver(mTimeChangedReceiver);
1023
1024 mAlarmManager.cancel(this);
1025 mLastActivatedTime = null;
1026 }
1027
1028 @Override
1029 public void onActivated(boolean activated) {
Christine Franks57fdde82018-07-03 14:46:07 -07001030 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Franks03213462017-08-25 13:57:26 -07001031 updateNextAlarm(activated, LocalDateTime.now());
Justin Klaassen911e8892016-06-21 18:24:24 -07001032 }
1033
1034 @Override
Christine Franks03213462017-08-25 13:57:26 -07001035 public void onCustomStartTimeChanged(LocalTime startTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -07001036 mStartTime = startTime;
1037 mLastActivatedTime = null;
1038 updateActivated();
1039 }
1040
1041 @Override
Christine Franks03213462017-08-25 13:57:26 -07001042 public void onCustomEndTimeChanged(LocalTime endTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -07001043 mEndTime = endTime;
1044 mLastActivatedTime = null;
1045 updateActivated();
1046 }
1047
1048 @Override
1049 public void onAlarm() {
Justin Klaassenec8837a2016-08-23 12:04:42 -07001050 Slog.d(TAG, "onAlarm");
Justin Klaassen911e8892016-06-21 18:24:24 -07001051 updateActivated();
1052 }
1053 }
1054
Christine Franks57fdde82018-07-03 14:46:07 -07001055 private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements
1056 TwilightListener {
Justin Klaassen911e8892016-06-21 18:24:24 -07001057
1058 private final TwilightManager mTwilightManager;
Christine Franks57fdde82018-07-03 14:46:07 -07001059 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -07001060
Christine Franks57fdde82018-07-03 14:46:07 -07001061 TwilightNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -07001062 mTwilightManager = getLocalService(TwilightManager.class);
Justin Klaassen911e8892016-06-21 18:24:24 -07001063 }
1064
Justin Klaassen908b86c2016-08-08 09:18:42 -07001065 private void updateActivated(TwilightState state) {
Justin Klaassen3da4c442017-05-05 15:19:33 -07001066 if (state == null) {
1067 // If there isn't a valid TwilightState then just keep the current activated
1068 // state.
1069 return;
1070 }
1071
1072 boolean activate = state.isNight();
Christine Franks57fdde82018-07-03 14:46:07 -07001073 if (mLastActivatedTime != null) {
Christine Franks03213462017-08-25 13:57:26 -07001074 final LocalDateTime now = LocalDateTime.now();
1075 final LocalDateTime sunrise = state.sunrise();
1076 final LocalDateTime sunset = state.sunset();
Christine Frankse5bb03e2017-02-10 17:36:10 -08001077 // Maintain the existing activated state if within the current period.
Christine Franks57fdde82018-07-03 14:46:07 -07001078 if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
1079 ^ mLastActivatedTime.isBefore(sunset))) {
Christine Franks78a4dd42019-02-08 11:09:30 -08001080 activate = mNightDisplayTintController.isActivatedSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -07001081 }
1082 }
Justin Klaassen908b86c2016-08-08 09:18:42 -07001083
Christine Franks245ffd42018-11-16 13:45:14 -08001084 if (mNightDisplayTintController.isActivatedStateNotSet() || (
1085 mNightDisplayTintController.isActivated() != activate)) {
Christine Franks83cc5412018-07-03 14:46:07 -07001086 mNightDisplayTintController.setActivated(activate);
Justin Klaassen908b86c2016-08-08 09:18:42 -07001087 }
Justin Klaassen911e8892016-06-21 18:24:24 -07001088 }
1089
1090 @Override
Christine Franks57fdde82018-07-03 14:46:07 -07001091 public void onActivated(boolean activated) {
1092 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
1093 }
1094
1095 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -07001096 public void onStart() {
1097 mTwilightManager.registerListener(this, mHandler);
Christine Franks57fdde82018-07-03 14:46:07 -07001098 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -07001099
1100 // Force an update to initialize state.
Justin Klaassen908b86c2016-08-08 09:18:42 -07001101 updateActivated(mTwilightManager.getLastTwilightState());
Justin Klaassen911e8892016-06-21 18:24:24 -07001102 }
1103
1104 @Override
1105 public void onStop() {
1106 mTwilightManager.unregisterListener(this);
Christine Franks57fdde82018-07-03 14:46:07 -07001107 mLastActivatedTime = null;
Justin Klaassen908b86c2016-08-08 09:18:42 -07001108 }
1109
1110 @Override
1111 public void onTwilightStateChanged(@Nullable TwilightState state) {
Justin Klaassenec8837a2016-08-23 12:04:42 -07001112 Slog.d(TAG, "onTwilightStateChanged: isNight="
1113 + (state == null ? null : state.isNight()));
Justin Klaassen908b86c2016-08-08 09:18:42 -07001114 updateActivated(state);
Justin Klaassen911e8892016-06-21 18:24:24 -07001115 }
1116 }
Justin Klaassen639214e2016-07-14 21:00:06 -07001117
1118 /**
Anthony Han1bf7c562019-09-03 18:28:30 -07001119 * Only animates matrices and saves min and max coefficients for logging.
1120 */
1121 static class TintValueAnimator extends ValueAnimator {
1122 private float[] min;
1123 private float[] max;
1124
1125 public static TintValueAnimator ofMatrix(ColorMatrixEvaluator evaluator,
1126 Object... values) {
1127 TintValueAnimator anim = new TintValueAnimator();
1128 anim.setObjectValues(values);
1129 anim.setEvaluator(evaluator);
1130 if (values == null || values.length == 0) {
1131 return null;
1132 }
1133 float[] m = (float[]) values[0];
1134 anim.min = new float[m.length];
1135 anim.max = new float[m.length];
1136 for (int i = 0; i < m.length; ++i) {
1137 anim.min[i] = Float.MAX_VALUE;
1138 anim.max[i] = Float.MIN_VALUE;
1139 }
1140 return anim;
1141 }
1142
1143 public void updateMinMaxComponents() {
1144 float[] value = (float[]) getAnimatedValue();
1145 if (value == null) {
1146 return;
1147 }
1148 for (int i = 0; i < value.length; ++i) {
1149 min[i] = Math.min(min[i], value[i]);
1150 max[i] = Math.max(max[i], value[i]);
1151 }
1152 }
1153
1154 public float[] getMin() {
1155 return min;
1156 }
1157
1158 public float[] getMax() {
1159 return max;
1160 }
1161 }
1162
1163 /**
Justin Klaassen639214e2016-07-14 21:00:06 -07001164 * Interpolates between two 4x4 color transform matrices (in column-major order).
1165 */
1166 private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> {
1167
1168 /**
1169 * Result matrix returned by {@link #evaluate(float, float[], float[])}.
1170 */
1171 private final float[] mResultMatrix = new float[16];
1172
1173 @Override
1174 public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
1175 for (int i = 0; i < mResultMatrix.length; i++) {
1176 mResultMatrix[i] = MathUtils.lerp(startValue[i], endValue[i], fraction);
1177 }
1178 return mResultMatrix;
1179 }
1180 }
Christine Franks39b03112018-07-03 14:46:07 -07001181
Christine Franks83cc5412018-07-03 14:46:07 -07001182 private final class NightDisplayTintController extends TintController {
1183
Christine Franksa4ed3762019-01-24 15:37:04 -08001184 private final float[] mMatrix = new float[16];
Christine Franks83cc5412018-07-03 14:46:07 -07001185 private final float[] mColorTempCoefficients = new float[9];
Christine Franksa4ed3762019-01-24 15:37:04 -08001186
1187 private Boolean mIsAvailable;
Christine Franks83cc5412018-07-03 14:46:07 -07001188 private Integer mColorTemp;
1189
1190 /**
1191 * Set coefficients based on whether the color matrix is linear or not.
1192 */
1193 @Override
1194 public void setUp(Context context, boolean needsLinear) {
1195 final String[] coefficients = context.getResources().getStringArray(needsLinear
1196 ? R.array.config_nightDisplayColorTemperatureCoefficients
1197 : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
1198 for (int i = 0; i < 9 && i < coefficients.length; i++) {
1199 mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
1200 }
1201 }
1202
1203 @Override
1204 public void setMatrix(int cct) {
1205 if (mMatrix.length != 16) {
1206 Slog.d(TAG, "The display transformation matrix must be 4x4");
1207 return;
1208 }
1209
1210 Matrix.setIdentityM(mMatrix, 0);
1211
1212 final float squareTemperature = cct * cct;
1213 final float red = squareTemperature * mColorTempCoefficients[0]
1214 + cct * mColorTempCoefficients[1] + mColorTempCoefficients[2];
1215 final float green = squareTemperature * mColorTempCoefficients[3]
1216 + cct * mColorTempCoefficients[4] + mColorTempCoefficients[5];
1217 final float blue = squareTemperature * mColorTempCoefficients[6]
1218 + cct * mColorTempCoefficients[7] + mColorTempCoefficients[8];
1219 mMatrix[0] = red;
1220 mMatrix[5] = green;
1221 mMatrix[10] = blue;
1222 }
1223
1224 @Override
1225 public float[] getMatrix() {
1226 return isActivated() ? mMatrix : MATRIX_IDENTITY;
1227 }
1228
1229 @Override
1230 public void setActivated(Boolean activated) {
Christine Franks8851ac82019-06-14 09:44:43 -07001231 setActivated(activated, LocalDateTime.now());
1232 }
1233
1234 /**
1235 * Use directly when it is important that the last activation time be exact (for example, an
1236 * automatic change). Otherwise use {@link #setActivated(Boolean)}.
1237 */
1238 public void setActivated(Boolean activated, @NonNull LocalDateTime lastActivationTime) {
Christine Franks83cc5412018-07-03 14:46:07 -07001239 if (activated == null) {
1240 super.setActivated(null);
1241 return;
1242 }
1243
1244 boolean activationStateChanged = activated != isActivated();
1245
1246 if (!isActivatedStateNotSet() && activationStateChanged) {
1247 // This is a true state change, so set this as the last activation time.
1248 Secure.putStringForUser(getContext().getContentResolver(),
1249 Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
Christine Franks8851ac82019-06-14 09:44:43 -07001250 lastActivationTime.toString(),
Christine Franks83cc5412018-07-03 14:46:07 -07001251 mCurrentUser);
1252 }
1253
1254 if (isActivatedStateNotSet() || activationStateChanged) {
1255 super.setActivated(activated);
Christine Franks78a4dd42019-02-08 11:09:30 -08001256 if (isActivatedSetting() != activated) {
1257 Secure.putIntForUser(getContext().getContentResolver(),
1258 Secure.NIGHT_DISPLAY_ACTIVATED,
1259 activated ? 1 : 0, mCurrentUser);
1260 }
Christine Franks83cc5412018-07-03 14:46:07 -07001261 onActivated(activated);
1262 }
1263 }
1264
1265 @Override
1266 public int getLevel() {
1267 return LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
1268 }
1269
Christine Franksa4ed3762019-01-24 15:37:04 -08001270 @Override
1271 public boolean isAvailable(Context context) {
1272 if (mIsAvailable == null) {
1273 mIsAvailable = ColorDisplayManager.isNightDisplayAvailable(context);
1274 }
1275 return mIsAvailable;
1276 }
1277
Christine Franks78a4dd42019-02-08 11:09:30 -08001278 private void onActivated(boolean activated) {
Christine Franks83cc5412018-07-03 14:46:07 -07001279 Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
1280 if (mNightDisplayAutoMode != null) {
1281 mNightDisplayAutoMode.onActivated(activated);
1282 }
1283
Christine Franksa4ed3762019-01-24 15:37:04 -08001284 if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
Christine Franks83cc5412018-07-03 14:46:07 -07001285 updateDisplayWhiteBalanceStatus();
1286 }
1287
1288 mHandler.sendEmptyMessage(MSG_APPLY_NIGHT_DISPLAY_ANIMATED);
1289 }
1290
1291 int getColorTemperature() {
1292 return mColorTemp != null ? clampNightDisplayColorTemperature(mColorTemp)
Christine Franks78a4dd42019-02-08 11:09:30 -08001293 : getColorTemperatureSetting();
Christine Franks83cc5412018-07-03 14:46:07 -07001294 }
1295
1296 boolean setColorTemperature(int temperature) {
1297 mColorTemp = temperature;
1298 final boolean success = Secure.putIntForUser(getContext().getContentResolver(),
1299 Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, temperature, mCurrentUser);
1300 onColorTemperatureChanged(temperature);
1301 return success;
1302 }
1303
1304 void onColorTemperatureChanged(int temperature) {
1305 setMatrix(temperature);
1306 mHandler.sendEmptyMessage(MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE);
1307 }
Christine Franks78a4dd42019-02-08 11:09:30 -08001308
1309 boolean isActivatedSetting() {
Christine Franks44782612019-03-07 17:25:39 -08001310 if (mCurrentUser == UserHandle.USER_NULL) {
1311 return false;
1312 }
Christine Franks78a4dd42019-02-08 11:09:30 -08001313 return Secure.getIntForUser(getContext().getContentResolver(),
1314 Secure.NIGHT_DISPLAY_ACTIVATED, 0, mCurrentUser) == 1;
1315 }
1316
1317 int getColorTemperatureSetting() {
Christine Franks44782612019-03-07 17:25:39 -08001318 if (mCurrentUser == UserHandle.USER_NULL) {
1319 return NOT_SET;
1320 }
Christine Franks78a4dd42019-02-08 11:09:30 -08001321 return clampNightDisplayColorTemperature(Secure.getIntForUser(
1322 getContext().getContentResolver(), Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
1323 NOT_SET,
1324 mCurrentUser));
1325 }
1326
1327 private int clampNightDisplayColorTemperature(int colorTemperature) {
1328 if (colorTemperature == NOT_SET) {
1329 colorTemperature = getContext().getResources().getInteger(
1330 R.integer.config_nightDisplayColorTemperatureDefault);
1331 }
1332 final int minimumTemperature = ColorDisplayManager
1333 .getMinimumColorTemperature(getContext());
1334 final int maximumTemperature = ColorDisplayManager
1335 .getMaximumColorTemperature(getContext());
1336 if (colorTemperature < minimumTemperature) {
1337 colorTemperature = minimumTemperature;
1338 } else if (colorTemperature > maximumTemperature) {
1339 colorTemperature = maximumTemperature;
1340 }
1341
1342 return colorTemperature;
1343 }
Christine Franks83cc5412018-07-03 14:46:07 -07001344 }
1345
Christine Franks245ffd42018-11-16 13:45:14 -08001346 /**
1347 * Local service that allows color transforms to be enabled from other system services.
1348 */
1349 public final class ColorDisplayServiceInternal {
1350
1351 /**
1352 * Set the current CCT value for the display white balance transform, and if the transform
1353 * is enabled, apply it.
1354 *
1355 * @param cct the color temperature in Kelvin.
1356 */
1357 public boolean setDisplayWhiteBalanceColorTemperature(int cct) {
1358 // Update the transform matrix even if it can't be applied.
Christine Franks245ffd42018-11-16 13:45:14 -08001359 mDisplayWhiteBalanceTintController.setMatrix(cct);
1360
1361 if (mDisplayWhiteBalanceTintController.isActivated()) {
Christine Franksc7fb9452019-02-04 08:45:33 -08001362 mHandler.sendEmptyMessage(MSG_APPLY_DISPLAY_WHITE_BALANCE);
Christine Franks245ffd42018-11-16 13:45:14 -08001363 return true;
1364 }
1365 return false;
1366 }
1367
1368 /**
Daniel Solomon37816412019-04-10 15:17:41 -07001369 * Reset the CCT value for the display white balance transform to its default value.
1370 */
1371 public boolean resetDisplayWhiteBalanceColorTemperature() {
Anthony Han00a490b2019-08-26 14:00:28 -07001372 int temperatureDefault = getContext().getResources()
1373 .getInteger(R.integer.config_displayWhiteBalanceColorTemperatureDefault);
1374 Slog.d(TAG, "resetDisplayWhiteBalanceColorTemperature: " + temperatureDefault);
1375 return setDisplayWhiteBalanceColorTemperature(temperatureDefault);
Daniel Solomon37816412019-04-10 15:17:41 -07001376 }
1377
1378 /**
Christine Franks245ffd42018-11-16 13:45:14 -08001379 * Sets the listener and returns whether display white balance is currently enabled.
1380 */
1381 public boolean setDisplayWhiteBalanceListener(DisplayWhiteBalanceListener listener) {
1382 mDisplayWhiteBalanceListener = listener;
1383 return mDisplayWhiteBalanceTintController.isActivated();
1384 }
Daniel Solomon8b72c5b2018-11-25 11:07:13 -08001385
Christine Franksf3529b22019-01-03 13:20:17 -08001386 /**
Christine Franks66783a82019-03-28 11:45:56 -07001387 * Returns whether Display white balance is currently enabled.
1388 */
1389 public boolean isDisplayWhiteBalanceEnabled() {
1390 return isDisplayWhiteBalanceSettingEnabled();
1391 }
1392
1393 /**
Christine Franksf3529b22019-01-03 13:20:17 -08001394 * Adds a {@link WeakReference<ColorTransformController>} for a newly started activity, and
1395 * invokes {@link ColorTransformController#applyAppSaturation(float[], float[])} if needed.
1396 */
Christine Franks55194dc2019-01-15 13:47:06 -08001397 public boolean attachColorTransformController(String packageName, @UserIdInt int userId,
Christine Franksf3529b22019-01-03 13:20:17 -08001398 WeakReference<ColorTransformController> controller) {
1399 return mAppSaturationController
Christine Franks55194dc2019-01-15 13:47:06 -08001400 .addColorTransformController(packageName, userId, controller);
Christine Franksf3529b22019-01-03 13:20:17 -08001401 }
Christine Franks245ffd42018-11-16 13:45:14 -08001402 }
1403
1404 /**
1405 * Listener for changes in display white balance status.
1406 */
1407 public interface DisplayWhiteBalanceListener {
1408
1409 /**
1410 * Notify that the display white balance status has changed, either due to preemption by
1411 * another transform or the feature being turned off.
1412 */
Christine Franks66783a82019-03-28 11:45:56 -07001413 void onDisplayWhiteBalanceStatusChanged(boolean activated);
Christine Franks245ffd42018-11-16 13:45:14 -08001414 }
1415
Christine Franks09c229e2018-12-14 10:37:40 -08001416 private final class TintHandler extends Handler {
1417
Christine Franks83cc5412018-07-03 14:46:07 -07001418 private TintHandler(Looper looper) {
Christine Franks09c229e2018-12-14 10:37:40 -08001419 super(looper, null, true /* async */);
1420 }
1421
1422 @Override
1423 public void handleMessage(Message msg) {
1424 switch (msg.what) {
Christine Franks27912a32019-04-02 10:43:10 -07001425 case MSG_USER_CHANGED:
1426 onUserChanged(msg.arg1);
1427 break;
1428 case MSG_SET_UP:
1429 setUp();
1430 break;
Christine Franks09c229e2018-12-14 10:37:40 -08001431 case MSG_APPLY_GLOBAL_SATURATION:
1432 mGlobalSaturationTintController.setMatrix(msg.arg1);
1433 applyTint(mGlobalSaturationTintController, false);
1434 break;
Christine Franks83cc5412018-07-03 14:46:07 -07001435 case MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE:
1436 applyTint(mNightDisplayTintController, true);
1437 break;
1438 case MSG_APPLY_NIGHT_DISPLAY_ANIMATED:
1439 applyTint(mNightDisplayTintController, false);
1440 break;
Christine Franksc7fb9452019-02-04 08:45:33 -08001441 case MSG_APPLY_DISPLAY_WHITE_BALANCE:
1442 applyTint(mDisplayWhiteBalanceTintController, false);
1443 break;
Christine Franks09c229e2018-12-14 10:37:40 -08001444 }
1445 }
1446 }
1447
Christine Franksf3529b22019-01-03 13:20:17 -08001448 /**
1449 * Interface for applying transforms to a given AppWindow.
1450 */
1451 public interface ColorTransformController {
1452
Christine Franksd154fe52019-01-04 17:17:45 -08001453 /**
1454 * Apply the given saturation (grayscale) matrix to the associated AppWindow.
1455 */
Christine Franksf3529b22019-01-03 13:20:17 -08001456 void applyAppSaturation(@Size(9) float[] matrix, @Size(3) float[] translation);
1457 }
1458
Christine Franks83cc5412018-07-03 14:46:07 -07001459 @VisibleForTesting
1460 final class BinderService extends IColorDisplayManager.Stub {
Christine Franks57fdde82018-07-03 14:46:07 -07001461
Christine Franks39b03112018-07-03 14:46:07 -07001462 @Override
Christine Franksd154fe52019-01-04 17:17:45 -08001463 public void setColorMode(int colorMode) {
1464 getContext().enforceCallingOrSelfPermission(
1465 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1466 "Permission required to set display color mode");
1467 final long token = Binder.clearCallingIdentity();
1468 try {
1469 setColorModeInternal(colorMode);
1470 } finally {
1471 Binder.restoreCallingIdentity(token);
1472 }
1473 }
1474
1475 @Override
1476 public int getColorMode() {
1477 final long token = Binder.clearCallingIdentity();
1478 try {
1479 return getColorModeInternal();
1480 } finally {
1481 Binder.restoreCallingIdentity(token);
1482 }
1483 }
1484
1485 @Override
Christine Franks39b03112018-07-03 14:46:07 -07001486 public boolean isDeviceColorManaged() {
1487 final long token = Binder.clearCallingIdentity();
1488 try {
1489 return isDeviceColorManagedInternal();
1490 } finally {
1491 Binder.restoreCallingIdentity(token);
1492 }
1493 }
Christine Franks09c229e2018-12-14 10:37:40 -08001494
1495 @Override
1496 public boolean setSaturationLevel(int level) {
1497 final boolean hasTransformsPermission = getContext()
1498 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
1499 == PackageManager.PERMISSION_GRANTED;
1500 final boolean hasLegacyPermission = getContext()
1501 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
1502 == PackageManager.PERMISSION_GRANTED;
1503 if (!hasTransformsPermission && !hasLegacyPermission) {
1504 throw new SecurityException("Permission required to set display saturation level");
1505 }
1506 final long token = Binder.clearCallingIdentity();
1507 try {
1508 final Message message = mHandler.obtainMessage(MSG_APPLY_GLOBAL_SATURATION);
1509 message.arg1 = level;
1510 mHandler.sendMessage(message);
1511 } finally {
1512 Binder.restoreCallingIdentity(token);
1513 }
1514 return true;
1515 }
Christine Franksf3529b22019-01-03 13:20:17 -08001516
1517 @Override
Christine Franks6d21d342019-02-07 15:09:03 -08001518 public boolean isSaturationActivated() {
1519 getContext().enforceCallingPermission(
1520 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1521 "Permission required to get display saturation level");
1522 final long token = Binder.clearCallingIdentity();
1523 try {
1524 return !mGlobalSaturationTintController.isActivatedStateNotSet()
1525 && mGlobalSaturationTintController.isActivated();
1526 } finally {
1527 Binder.restoreCallingIdentity(token);
1528 }
1529 }
1530
1531 @Override
Christine Franksf3529b22019-01-03 13:20:17 -08001532 public boolean setAppSaturationLevel(String packageName, int level) {
1533 getContext().enforceCallingPermission(
1534 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1535 "Permission required to set display saturation level");
1536 final long token = Binder.clearCallingIdentity();
1537 try {
1538 return setAppSaturationLevelInternal(packageName, level);
1539 } finally {
1540 Binder.restoreCallingIdentity(token);
1541 }
1542 }
1543
Christine Franks55194dc2019-01-15 13:47:06 -08001544 public int getTransformCapabilities() {
1545 getContext().enforceCallingPermission(
1546 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1547 "Permission required to query transform capabilities");
1548 final long token = Binder.clearCallingIdentity();
1549 try {
1550 return getTransformCapabilitiesInternal();
1551 } finally {
1552 Binder.restoreCallingIdentity(token);
1553 }
1554 }
1555
Christine Franksf3529b22019-01-03 13:20:17 -08001556 @Override
Christine Franks83cc5412018-07-03 14:46:07 -07001557 public boolean setNightDisplayActivated(boolean activated) {
1558 getContext().enforceCallingOrSelfPermission(
1559 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1560 "Permission required to set night display activated");
1561 final long token = Binder.clearCallingIdentity();
1562 try {
1563 mNightDisplayTintController.setActivated(activated);
1564 return true;
1565 } finally {
1566 Binder.restoreCallingIdentity(token);
1567 }
1568 }
1569
1570 @Override
1571 public boolean isNightDisplayActivated() {
1572 final long token = Binder.clearCallingIdentity();
1573 try {
1574 return mNightDisplayTintController.isActivated();
1575 } finally {
1576 Binder.restoreCallingIdentity(token);
1577 }
1578 }
1579
1580 @Override
1581 public boolean setNightDisplayColorTemperature(int temperature) {
1582 getContext().enforceCallingOrSelfPermission(
1583 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1584 "Permission required to set night display temperature");
1585 final long token = Binder.clearCallingIdentity();
1586 try {
1587 return mNightDisplayTintController.setColorTemperature(temperature);
1588 } finally {
1589 Binder.restoreCallingIdentity(token);
1590 }
1591 }
1592
1593 @Override
1594 public int getNightDisplayColorTemperature() {
1595 final long token = Binder.clearCallingIdentity();
1596 try {
1597 return mNightDisplayTintController.getColorTemperature();
1598 } finally {
1599 Binder.restoreCallingIdentity(token);
1600 }
1601 }
1602
1603 @Override
1604 public boolean setNightDisplayAutoMode(int autoMode) {
1605 getContext().enforceCallingOrSelfPermission(
1606 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1607 "Permission required to set night display auto mode");
1608 final long token = Binder.clearCallingIdentity();
1609 try {
1610 return setNightDisplayAutoModeInternal(autoMode);
1611 } finally {
1612 Binder.restoreCallingIdentity(token);
1613 }
1614 }
1615
1616 @Override
1617 public int getNightDisplayAutoMode() {
1618 getContext().enforceCallingOrSelfPermission(
1619 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1620 "Permission required to get night display auto mode");
1621 final long token = Binder.clearCallingIdentity();
1622 try {
1623 return getNightDisplayAutoModeInternal();
1624 } finally {
1625 Binder.restoreCallingIdentity(token);
1626 }
1627 }
1628
1629 @Override
1630 public int getNightDisplayAutoModeRaw() {
1631 final long token = Binder.clearCallingIdentity();
1632 try {
1633 return getNightDisplayAutoModeRawInternal();
1634 } finally {
1635 Binder.restoreCallingIdentity(token);
1636 }
1637 }
1638
1639 @Override
1640 public boolean setNightDisplayCustomStartTime(Time startTime) {
1641 getContext().enforceCallingOrSelfPermission(
1642 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1643 "Permission required to set night display custom start time");
1644 final long token = Binder.clearCallingIdentity();
1645 try {
1646 return setNightDisplayCustomStartTimeInternal(startTime);
1647 } finally {
1648 Binder.restoreCallingIdentity(token);
1649 }
1650 }
1651
1652 @Override
1653 public Time getNightDisplayCustomStartTime() {
1654 final long token = Binder.clearCallingIdentity();
1655 try {
1656 return getNightDisplayCustomStartTimeInternal();
1657 } finally {
1658 Binder.restoreCallingIdentity(token);
1659 }
1660 }
1661
1662 @Override
1663 public boolean setNightDisplayCustomEndTime(Time endTime) {
1664 getContext().enforceCallingOrSelfPermission(
1665 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1666 "Permission required to set night display custom end time");
1667 final long token = Binder.clearCallingIdentity();
1668 try {
1669 return setNightDisplayCustomEndTimeInternal(endTime);
1670 } finally {
1671 Binder.restoreCallingIdentity(token);
1672 }
1673 }
1674
1675 @Override
1676 public Time getNightDisplayCustomEndTime() {
1677 final long token = Binder.clearCallingIdentity();
1678 try {
1679 return getNightDisplayCustomEndTimeInternal();
1680 } finally {
1681 Binder.restoreCallingIdentity(token);
1682 }
1683 }
1684
1685 @Override
Christine Franks66783a82019-03-28 11:45:56 -07001686 public boolean setDisplayWhiteBalanceEnabled(boolean enabled) {
1687 getContext().enforceCallingOrSelfPermission(
1688 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1689 "Permission required to set night display activated");
1690 final long token = Binder.clearCallingIdentity();
1691 try {
1692 return setDisplayWhiteBalanceSettingEnabled(enabled);
1693 } finally {
1694 Binder.restoreCallingIdentity(token);
1695 }
1696 }
1697
1698 @Override
1699 public boolean isDisplayWhiteBalanceEnabled() {
1700 final long token = Binder.clearCallingIdentity();
1701 try {
1702 return isDisplayWhiteBalanceSettingEnabled();
1703 } finally {
1704 Binder.restoreCallingIdentity(token);
1705 }
1706 }
1707
1708 @Override
Christine Franksf3529b22019-01-03 13:20:17 -08001709 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Christine Franksd154fe52019-01-04 17:17:45 -08001710 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
1711 return;
1712 }
Christine Franksf3529b22019-01-03 13:20:17 -08001713
1714 final long token = Binder.clearCallingIdentity();
1715 try {
1716 dumpInternal(pw);
1717 } finally {
1718 Binder.restoreCallingIdentity(token);
1719 }
1720 }
Christine Franks39b03112018-07-03 14:46:07 -07001721 }
Justin Klaassen911e8892016-06-21 18:24:24 -07001722}