blob: 58c88b36f09da525e3bd841b079f0fefe285938e [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
17package com.android.server.display;
18
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;
22
Christine Franks245ffd42018-11-16 13:45:14 -080023import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
Christine Franks39b03112018-07-03 14:46:07 -070024import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
Christine Franks09c229e2018-12-14 10:37:40 -080025import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_SATURATION;
Christine Franks39b03112018-07-03 14:46:07 -070026
Christine Franks09c229e2018-12-14 10:37:40 -080027import android.Manifest;
Justin Klaassen639214e2016-07-14 21:00:06 -070028import android.animation.Animator;
29import android.animation.AnimatorListenerAdapter;
30import android.animation.TypeEvaluator;
31import android.animation.ValueAnimator;
Justin Klaassen4346f632016-08-08 15:01:47 -070032import android.annotation.NonNull;
Justin Klaassen908b86c2016-08-08 09:18:42 -070033import android.annotation.Nullable;
Christine Franksf3529b22019-01-03 13:20:17 -080034import android.annotation.Size;
Christine Franks55194dc2019-01-15 13:47:06 -080035import android.annotation.UserIdInt;
Justin Klaassen911e8892016-06-21 18:24:24 -070036import android.app.AlarmManager;
37import android.content.BroadcastReceiver;
38import android.content.ContentResolver;
39import android.content.Context;
40import android.content.Intent;
41import android.content.IntentFilter;
Christine Franks09c229e2018-12-14 10:37:40 -080042import android.content.pm.PackageManager;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080043import android.content.res.Resources;
Justin Klaassen2696d992016-07-11 21:26:46 -070044import android.database.ContentObserver;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080045import android.graphics.ColorSpace;
Christine Franksf3529b22019-01-03 13:20:17 -080046import android.hardware.display.ColorDisplayManager;
Christine Franks83cc5412018-07-03 14:46:07 -070047import android.hardware.display.ColorDisplayManager.AutoMode;
Christine Franks39b03112018-07-03 14:46:07 -070048import android.hardware.display.IColorDisplayManager;
Christine Franks83cc5412018-07-03 14:46:07 -070049import android.hardware.display.Time;
Justin Klaassen2696d992016-07-11 21:26:46 -070050import android.net.Uri;
Justin Klaassen639214e2016-07-14 21:00:06 -070051import android.opengl.Matrix;
Christine Franks39b03112018-07-03 14:46:07 -070052import android.os.Binder;
Justin Klaassen911e8892016-06-21 18:24:24 -070053import android.os.Handler;
54import android.os.Looper;
Christine Franks09c229e2018-12-14 10:37:40 -080055import android.os.Message;
Justin Klaassen911e8892016-06-21 18:24:24 -070056import android.os.UserHandle;
57import android.provider.Settings.Secure;
Christine Franks57fdde82018-07-03 14:46:07 -070058import android.provider.Settings.System;
Justin Klaassen639214e2016-07-14 21:00:06 -070059import android.util.MathUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070060import android.util.Slog;
Christine Franks55194dc2019-01-15 13:47:06 -080061import android.view.SurfaceControl;
Christine Franks9114f462019-01-04 11:27:30 -080062import android.view.accessibility.AccessibilityManager;
Justin Klaassen639214e2016-07-14 21:00:06 -070063import android.view.animation.AnimationUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070064
Christine Franks39b03112018-07-03 14:46:07 -070065import com.android.internal.R;
Christine Franks57fdde82018-07-03 14:46:07 -070066import com.android.internal.annotations.VisibleForTesting;
Christine Franks5397f032017-11-01 18:35:16 -070067import com.android.internal.app.ColorDisplayController;
Christine Franksf3529b22019-01-03 13:20:17 -080068import com.android.internal.util.DumpUtils;
Christine Franks57fdde82018-07-03 14:46:07 -070069import com.android.server.DisplayThread;
Justin Klaassen911e8892016-06-21 18:24:24 -070070import com.android.server.SystemService;
71import com.android.server.twilight.TwilightListener;
72import com.android.server.twilight.TwilightManager;
73import com.android.server.twilight.TwilightState;
74
Christine Franksf3529b22019-01-03 13:20:17 -080075import java.io.FileDescriptor;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080076import java.io.PrintWriter;
Christine Franksf3529b22019-01-03 13:20:17 -080077import java.lang.ref.WeakReference;
Christine Franks57fdde82018-07-03 14:46:07 -070078import java.time.DateTimeException;
79import java.time.Instant;
Christine Franks03213462017-08-25 13:57:26 -070080import java.time.LocalDateTime;
81import java.time.LocalTime;
82import java.time.ZoneId;
Christine Franks57fdde82018-07-03 14:46:07 -070083import java.time.format.DateTimeParseException;
Christine Franks09c229e2018-12-14 10:37:40 -080084import java.util.Arrays;
Christine Franks8ad71492017-10-24 19:04:22 -070085
Justin Klaassen911e8892016-06-21 18:24:24 -070086/**
Christine Franks39b03112018-07-03 14:46:07 -070087 * Controls the display's color transforms.
Justin Klaassen911e8892016-06-21 18:24:24 -070088 */
Christine Franks57fdde82018-07-03 14:46:07 -070089public final class ColorDisplayService extends SystemService {
Justin Klaassen911e8892016-06-21 18:24:24 -070090
Christine Franks5397f032017-11-01 18:35:16 -070091 private static final String TAG = "ColorDisplayService";
Justin Klaassen911e8892016-06-21 18:24:24 -070092
93 /**
Christine Franks7b83b4282017-01-18 14:55:00 -080094 * The transition time, in milliseconds, for Night Display to turn on/off.
95 */
96 private static final long TRANSITION_DURATION = 3000L;
97
98 /**
Justin Klaassen639214e2016-07-14 21:00:06 -070099 * The identity matrix, used if one of the given matrices is {@code null}.
100 */
101 private 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 Franks09c229e2018-12-14 10:37:40 -0800107 private static final int MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE = 0;
108 private static final int MSG_APPLY_NIGHT_DISPLAY_ANIMATED = 1;
109 private static final int MSG_APPLY_GLOBAL_SATURATION = 2;
110
Justin Klaassen639214e2016-07-14 21:00:06 -0700111 /**
Christine Franks83cc5412018-07-03 14:46:07 -0700112 * Return value if a setting has not been set.
113 */
114 private static final int NOT_SET = -1;
115
116 /**
Justin Klaassen639214e2016-07-14 21:00:06 -0700117 * Evaluator used to animate color matrix transitions.
118 */
119 private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
120
Christine Franks83cc5412018-07-03 14:46:07 -0700121 private final NightDisplayTintController mNightDisplayTintController =
122 new NightDisplayTintController();
Christine Franks245ffd42018-11-16 13:45:14 -0800123
124 private final TintController mDisplayWhiteBalanceTintController = new TintController() {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800125 // Three chromaticity coordinates per color: X, Y, and Z
126 private final int NUM_VALUES_PER_PRIMARY = 3;
127 // Four colors: red, green, blue, and white
128 private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY;
Christine Franks245ffd42018-11-16 13:45:14 -0800129
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800130 private final Object mLock = new Object();
131 private int mTemperatureMin;
132 private int mTemperatureMax;
133 private int mTemperatureDefault;
134 private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
135 private ColorSpace.Rgb mDisplayColorSpaceRGB;
136 private float[] mChromaticAdaptationMatrix;
137 private int mCurrentColorTemperature;
138 private float[] mCurrentColorTemperatureXYZ;
139 private boolean mSetUp = false;
Christine Franks245ffd42018-11-16 13:45:14 -0800140 private float[] mMatrixDisplayWhiteBalance = new float[16];
141
142 @Override
143 public void setUp(Context context, boolean needsLinear) {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800144 mSetUp = false;
145
146 final Resources res = getContext().getResources();
147 final String[] displayPrimariesValues = res.getStringArray(
148 R.array.config_displayWhiteBalanceDisplayPrimaries);
149 final String[] nominalWhiteValues = res.getStringArray(
150 R.array.config_displayWhiteBalanceDisplayNominalWhite);
151
152 if (displayPrimariesValues.length != NUM_DISPLAY_PRIMARIES_VALS) {
153 Slog.e(TAG, "Unexpected display white balance primaries resource length " +
154 displayPrimariesValues.length);
155 return;
156 }
157
158 if (nominalWhiteValues.length != NUM_VALUES_PER_PRIMARY) {
159 Slog.e(TAG, "Unexpected display white balance nominal white resource length " +
160 nominalWhiteValues.length);
161 return;
162 }
163
164 float[] displayRedGreenBlueXYZ =
165 new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
166 float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
167 for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
168 displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
169 }
170 for (int i = 0; i < displayWhiteXYZ.length; i++) {
171 displayWhiteXYZ[i] = Float.parseFloat(
172 displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
173 }
174
175 final ColorSpace.Rgb displayColorSpaceRGB = new ColorSpace.Rgb(
176 "Display Color Space",
177 displayRedGreenBlueXYZ,
178 displayWhiteXYZ,
179 2.2f // gamma, unused for display white balance
180 );
181
182 float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
183 for (int i = 0; i < nominalWhiteValues.length; i++) {
184 displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
185 }
186
187 final int colorTemperatureMin = res.getInteger(
188 R.integer.config_displayWhiteBalanceColorTemperatureMin);
189 if (colorTemperatureMin <= 0) {
190 Slog.e(TAG, "display white balance minimum temperature must be greater than 0");
191 return;
192 }
193
194 final int colorTemperatureMax = res.getInteger(
195 R.integer.config_displayWhiteBalanceColorTemperatureMax);
196 if (colorTemperatureMax < colorTemperatureMin) {
197 Slog.e(TAG, "display white balance max temp must be greater or equal to min");
198 return;
199 }
200
201 final int colorTemperature = res.getInteger(
202 R.integer.config_displayWhiteBalanceColorTemperatureDefault);
203
204 synchronized (mLock) {
205 mDisplayColorSpaceRGB = displayColorSpaceRGB;
206 mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
207 mTemperatureMin = colorTemperatureMin;
208 mTemperatureMax = colorTemperatureMax;
209 mTemperatureDefault = colorTemperature;
210 mSetUp = true;
211 }
212
213 setMatrix(mTemperatureDefault);
Christine Franks245ffd42018-11-16 13:45:14 -0800214 }
215
216 @Override
217 public float[] getMatrix() {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800218 return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
Christine Franks245ffd42018-11-16 13:45:14 -0800219 }
220
221 @Override
222 public void setMatrix(int cct) {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800223 if (!mSetUp) {
224 Slog.w(TAG, "Can't set display white balance temperature: uninitialized");
225 return;
226 }
227
228 if (cct < mTemperatureMin) {
229 Slog.w(TAG, "Requested display color temperature is below allowed minimum");
230 cct = mTemperatureMin;
231 } else if (cct > mTemperatureMax) {
232 Slog.w(TAG, "Requested display color temperature is above allowed maximum");
233 cct = mTemperatureMax;
234 }
235
236 Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);
237
238 synchronized (mLock) {
239 mCurrentColorTemperature = cct;
240
241 // Adapt the display's nominal white point to match the requested CCT value
242 mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct);
243
244 mChromaticAdaptationMatrix =
245 ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
246 mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
247
248 // Convert the adaptation matrix to RGB space
249 float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
250 mDisplayColorSpaceRGB.getTransform());
251 result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);
252
253 // Normalize the transform matrix to peak white value in RGB space
254 final float adaptedMaxR = result[0] + result[3] + result[6];
255 final float adaptedMaxG = result[1] + result[4] + result[7];
256 final float adaptedMaxB = result[2] + result[5] + result[8];
257 final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
258 for (int i = 0; i < result.length; i++) {
259 result[i] /= denum;
260 }
261
262 Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
263 java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
264 java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
265 java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
266 }
Christine Franks245ffd42018-11-16 13:45:14 -0800267 }
268
269 @Override
270 public int getLevel() {
271 return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
272 }
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800273
274 /**
275 * Format a given matrix into a string.
276 *
277 * @param matrix the matrix to format
278 * @param cols number of columns in the matrix
279 */
280 private String matrixToString(float[] matrix, int cols) {
281 if (matrix == null || cols <= 0) {
282 Slog.e(TAG, "Invalid arguments when formatting matrix to string");
283 return "";
284 }
285
286 StringBuilder sb = new StringBuilder("");
287 for (int i = 0; i < matrix.length; i++) {
288 if (i % cols == 0) {
289 sb.append("\n ");
290 }
291 sb.append(String.format("%9.6f ", matrix[i]));
292 }
293 return sb.toString();
294 }
295
296 @Override
297 public void dump(PrintWriter pw) {
298 synchronized (mLock) {
299 pw.println("ColorDisplayService");
300 pw.println(" mSetUp = " + mSetUp);
301
302 if (!mSetUp) {
303 return;
304 }
305
306 pw.println(" isActivated = " + isActivated());
307 pw.println(" mTemperatureMin = " + mTemperatureMin);
308 pw.println(" mTemperatureMax = " + mTemperatureMax);
309 pw.println(" mTemperatureDefault = " + mTemperatureDefault);
310 pw.println(" mCurrentColorTemperature = " + mCurrentColorTemperature);
311 pw.println(" mCurrentColorTemperatureXYZ = " +
312 matrixToString(mCurrentColorTemperatureXYZ, 3));
313 pw.println(" mDisplayColorSpaceRGB RGB-to-XYZ = " +
314 matrixToString(mDisplayColorSpaceRGB.getTransform(), 3));
315 pw.println(" mChromaticAdaptationMatrix = " +
316 matrixToString(mChromaticAdaptationMatrix, 3));
317 pw.println(" mDisplayColorSpaceRGB XYZ-to-RGB = " +
318 matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3));
319 pw.println(" mMatrixDisplayWhiteBalance = " +
320 matrixToString(mMatrixDisplayWhiteBalance, 4));
321 }
322 }
Christine Franks245ffd42018-11-16 13:45:14 -0800323 };
324
Christine Franks09c229e2018-12-14 10:37:40 -0800325 private final TintController mGlobalSaturationTintController = new TintController() {
326
327 private float[] mMatrixGlobalSaturation = new float[16];
328
329 @Override
330 public void setUp(Context context, boolean needsLinear) {
331 }
332
333 @Override
334 public float[] getMatrix() {
335 return Arrays.copyOf(mMatrixGlobalSaturation, mMatrixGlobalSaturation.length);
336 }
337
338 @Override
339 public void setMatrix(int saturationLevel) {
340 if (saturationLevel < 0) {
341 saturationLevel = 0;
342 } else if (saturationLevel > 100) {
343 saturationLevel = 100;
344 }
345 Slog.d(TAG, "Setting saturation level: " + saturationLevel);
346
347 if (saturationLevel == 100) {
348 Matrix.setIdentityM(mMatrixGlobalSaturation, 0);
349 } else {
350 float saturation = saturationLevel * 0.1f;
351 float desaturation = 1.0f - saturation;
352 float[] luminance = {0.231f * desaturation, 0.715f * desaturation,
Christine Franks83cc5412018-07-03 14:46:07 -0700353 0.072f * desaturation};
Christine Franks09c229e2018-12-14 10:37:40 -0800354 mMatrixGlobalSaturation[0] = luminance[0] + saturation;
355 mMatrixGlobalSaturation[1] = luminance[0];
356 mMatrixGlobalSaturation[2] = luminance[0];
357 mMatrixGlobalSaturation[4] = luminance[1];
358 mMatrixGlobalSaturation[5] = luminance[1] + saturation;
359 mMatrixGlobalSaturation[6] = luminance[1];
360 mMatrixGlobalSaturation[8] = luminance[2];
361 mMatrixGlobalSaturation[9] = luminance[2];
362 mMatrixGlobalSaturation[10] = luminance[2] + saturation;
363 }
364 }
365
366 @Override
367 public int getLevel() {
368 return LEVEL_COLOR_MATRIX_SATURATION;
369 }
370 };
371
Christine Franks9114f462019-01-04 11:27:30 -0800372 /**
373 * Matrix and offset used for converting color to grayscale.
374 */
375 private static final float[] MATRIX_GRAYSCALE = new float[]{
376 .2126f, .2126f, .2126f, 0f,
377 .7152f, .7152f, .7152f, 0f,
378 .0722f, .0722f, .0722f, 0f,
379 0f, 0f, 0f, 1f
380 };
381
382 /**
383 * Matrix and offset used for luminance inversion. Represents a transform from RGB to YIQ color
384 * space, rotation around the Y axis by 180 degrees, transform back to RGB color space, and
385 * subtraction from 1. The last row represents a non-multiplied addition, see surfaceflinger's
386 * ProgramCache for full implementation details.
387 */
388 private static final float[] MATRIX_INVERT_COLOR = new float[] {
389 0.402f, -0.598f, -0.599f, 0f,
390 -1.174f, -0.174f, -1.175f, 0f,
391 -0.228f, -0.228f, 0.772f, 0f,
392 1f, 1f, 1f, 1f
393 };
394
Justin Klaassen2696d992016-07-11 21:26:46 -0700395 private final Handler mHandler;
396
Christine Franksf3529b22019-01-03 13:20:17 -0800397 private final AppSaturationController mAppSaturationController = new AppSaturationController();
398
Justin Klaassen911e8892016-06-21 18:24:24 -0700399 private int mCurrentUser = UserHandle.USER_NULL;
Justin Klaassen2696d992016-07-11 21:26:46 -0700400 private ContentObserver mUserSetupObserver;
Justin Klaassen911e8892016-06-21 18:24:24 -0700401 private boolean mBootCompleted;
402
Christine Franks57fdde82018-07-03 14:46:07 -0700403 private ColorDisplayController mNightDisplayController;
404 private ContentObserver mContentObserver;
Christine Franks57fdde82018-07-03 14:46:07 -0700405
Christine Franks245ffd42018-11-16 13:45:14 -0800406 private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
407
Christine Franks57fdde82018-07-03 14:46:07 -0700408 private NightDisplayAutoMode mNightDisplayAutoMode;
Justin Klaassen911e8892016-06-21 18:24:24 -0700409
Christine Franks5397f032017-11-01 18:35:16 -0700410 public ColorDisplayService(Context context) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700411 super(context);
Christine Franks83cc5412018-07-03 14:46:07 -0700412 mHandler = new TintHandler(DisplayThread.get().getLooper());
Justin Klaassen911e8892016-06-21 18:24:24 -0700413 }
414
415 @Override
416 public void onStart() {
Christine Franks39b03112018-07-03 14:46:07 -0700417 publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
Christine Franks245ffd42018-11-16 13:45:14 -0800418 publishLocalService(ColorDisplayServiceInternal.class, new ColorDisplayServiceInternal());
Justin Klaassen911e8892016-06-21 18:24:24 -0700419 }
420
421 @Override
Justin Klaassen2696d992016-07-11 21:26:46 -0700422 public void onBootPhase(int phase) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800423 if (phase >= PHASE_BOOT_COMPLETED) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700424 mBootCompleted = true;
425
426 // Register listeners now that boot is complete.
427 if (mCurrentUser != UserHandle.USER_NULL && mUserSetupObserver == null) {
428 setUp();
429 }
430 }
431 }
432
433 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700434 public void onStartUser(int userHandle) {
435 super.onStartUser(userHandle);
436
Justin Klaassen911e8892016-06-21 18:24:24 -0700437 if (mCurrentUser == UserHandle.USER_NULL) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700438 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700439 }
440 }
441
442 @Override
443 public void onSwitchUser(int userHandle) {
444 super.onSwitchUser(userHandle);
445
Justin Klaassen2696d992016-07-11 21:26:46 -0700446 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700447 }
448
449 @Override
450 public void onStopUser(int userHandle) {
451 super.onStopUser(userHandle);
452
Justin Klaassen911e8892016-06-21 18:24:24 -0700453 if (mCurrentUser == userHandle) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700454 onUserChanged(UserHandle.USER_NULL);
Justin Klaassen911e8892016-06-21 18:24:24 -0700455 }
456 }
457
Justin Klaassen2696d992016-07-11 21:26:46 -0700458 private void onUserChanged(int userHandle) {
459 final ContentResolver cr = getContext().getContentResolver();
Justin Klaassen911e8892016-06-21 18:24:24 -0700460
Justin Klaassen2696d992016-07-11 21:26:46 -0700461 if (mCurrentUser != UserHandle.USER_NULL) {
462 if (mUserSetupObserver != null) {
463 cr.unregisterContentObserver(mUserSetupObserver);
464 mUserSetupObserver = null;
465 } else if (mBootCompleted) {
466 tearDown();
467 }
468 }
469
470 mCurrentUser = userHandle;
471
472 if (mCurrentUser != UserHandle.USER_NULL) {
473 if (!isUserSetupCompleted(cr, mCurrentUser)) {
474 mUserSetupObserver = new ContentObserver(mHandler) {
475 @Override
476 public void onChange(boolean selfChange, Uri uri) {
477 if (isUserSetupCompleted(cr, mCurrentUser)) {
478 cr.unregisterContentObserver(this);
479 mUserSetupObserver = null;
480
481 if (mBootCompleted) {
482 setUp();
483 }
484 }
485 }
486 };
487 cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
Christine Franks39b03112018-07-03 14:46:07 -0700488 false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
Justin Klaassen2696d992016-07-11 21:26:46 -0700489 } else if (mBootCompleted) {
490 setUp();
Justin Klaassen911e8892016-06-21 18:24:24 -0700491 }
492 }
493 }
494
Justin Klaassen2696d992016-07-11 21:26:46 -0700495 private static boolean isUserSetupCompleted(ContentResolver cr, int userHandle) {
496 return Secure.getIntForUser(cr, Secure.USER_SETUP_COMPLETE, 0, userHandle) == 1;
497 }
498
499 private void setUp() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700500 Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
501
Christine Franks57fdde82018-07-03 14:46:07 -0700502 mNightDisplayController = new ColorDisplayController(getContext(), mCurrentUser);
503
504 // Listen for external changes to any of the settings.
505 if (mContentObserver == null) {
506 mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) {
507 @Override
508 public void onChange(boolean selfChange, Uri uri) {
509 super.onChange(selfChange, uri);
510
511 final String setting = uri == null ? null : uri.getLastPathSegment();
512 if (setting != null) {
513 switch (setting) {
514 case Secure.NIGHT_DISPLAY_ACTIVATED:
Christine Franks83cc5412018-07-03 14:46:07 -0700515 final boolean activated = isNightDisplayActivatedSetting();
516 if (mNightDisplayTintController.isActivatedStateNotSet()
517 || mNightDisplayTintController.isActivated() != activated) {
518 mNightDisplayTintController.onActivated(activated);
519 }
Christine Franks57fdde82018-07-03 14:46:07 -0700520 break;
521 case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
Christine Franks83cc5412018-07-03 14:46:07 -0700522 final int temperature = getNightDisplayColorTemperatureSetting();
523 if (mNightDisplayTintController.getColorTemperature()
524 != temperature) {
525 mNightDisplayTintController
526 .onColorTemperatureChanged(temperature);
527 }
Christine Franks57fdde82018-07-03 14:46:07 -0700528 break;
529 case Secure.NIGHT_DISPLAY_AUTO_MODE:
Christine Franks83cc5412018-07-03 14:46:07 -0700530 onNightDisplayAutoModeChanged(getNightDisplayAutoModeInternal());
Christine Franks57fdde82018-07-03 14:46:07 -0700531 break;
532 case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
533 onNightDisplayCustomStartTimeChanged(
Christine Franks83cc5412018-07-03 14:46:07 -0700534 getNightDisplayCustomStartTimeInternal().getLocalTime());
Christine Franks57fdde82018-07-03 14:46:07 -0700535 break;
536 case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
537 onNightDisplayCustomEndTimeChanged(
Christine Franks83cc5412018-07-03 14:46:07 -0700538 getNightDisplayCustomEndTimeInternal().getLocalTime());
Christine Franks57fdde82018-07-03 14:46:07 -0700539 break;
540 case System.DISPLAY_COLOR_MODE:
541 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
542 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700543 case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
Christine Franks9114f462019-01-04 11:27:30 -0800544 onAccessibilityInversionChanged();
545 onAccessibilityActivated();
546 break;
547 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
548 onAccessibilityDaltonizerChanged();
549 onAccessibilityActivated();
550 break;
551 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER:
552 onAccessibilityDaltonizerChanged();
Christine Franks57fdde82018-07-03 14:46:07 -0700553 break;
Christine Franks245ffd42018-11-16 13:45:14 -0800554 case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800555 updateDisplayWhiteBalanceStatus();
Christine Franks245ffd42018-11-16 13:45:14 -0800556 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700557 }
558 }
559 }
560 };
561 }
562 final ContentResolver cr = getContext().getContentResolver();
563 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
564 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
565 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
566 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
567 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
568 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
569 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
570 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
571 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
572 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
573 cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
574 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
575 cr.registerContentObserver(
576 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
577 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
578 cr.registerContentObserver(
579 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
580 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks9114f462019-01-04 11:27:30 -0800581 cr.registerContentObserver(
582 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER),
583 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks245ffd42018-11-16 13:45:14 -0800584 cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED),
585 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Justin Klaassen911e8892016-06-21 18:24:24 -0700586
Christine Frankscf388c22018-05-15 15:48:10 -0700587 // Set the color mode, if valid, and immediately apply the updated tint matrix based on the
588 // existing activated state. This ensures consistency of tint across the color mode change.
Christine Franks57fdde82018-07-03 14:46:07 -0700589 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
Christine Frankscf388c22018-05-15 15:48:10 -0700590
Christine Franks245ffd42018-11-16 13:45:14 -0800591 if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
592 // Reset the activated state.
593 mNightDisplayTintController.setActivated(null);
Christine Frankscf388c22018-05-15 15:48:10 -0700594
Christine Franks245ffd42018-11-16 13:45:14 -0800595 // Prepare the night display color transformation matrix.
596 mNightDisplayTintController
597 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
Christine Franks83cc5412018-07-03 14:46:07 -0700598 mNightDisplayTintController.setMatrix(getNightDisplayColorTemperatureSetting());
Christine Franks8ad71492017-10-24 19:04:22 -0700599
Christine Franks245ffd42018-11-16 13:45:14 -0800600 // Initialize the current auto mode.
Christine Franks83cc5412018-07-03 14:46:07 -0700601 onNightDisplayAutoModeChanged(getNightDisplayAutoModeInternal());
Christine Franks6418d0b2017-02-13 09:48:00 -0800602
Christine Franks83cc5412018-07-03 14:46:07 -0700603 // Force the initialization of the current saved activation state.
Christine Franks245ffd42018-11-16 13:45:14 -0800604 if (mNightDisplayTintController.isActivatedStateNotSet()) {
Christine Franks83cc5412018-07-03 14:46:07 -0700605 mNightDisplayTintController.onActivated(isNightDisplayActivatedSetting());
Christine Franks245ffd42018-11-16 13:45:14 -0800606 }
607 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700608
Christine Franks245ffd42018-11-16 13:45:14 -0800609 if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
610 // Prepare the display white balance transform matrix.
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800611 mDisplayWhiteBalanceTintController.setUp(getContext(), true /* needsLinear */);
Christine Franks245ffd42018-11-16 13:45:14 -0800612
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800613 updateDisplayWhiteBalanceStatus();
Justin Klaassen911e8892016-06-21 18:24:24 -0700614 }
615 }
616
Justin Klaassen2696d992016-07-11 21:26:46 -0700617 private void tearDown() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700618 Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
619
Christine Franks57fdde82018-07-03 14:46:07 -0700620 getContext().getContentResolver().unregisterContentObserver(mContentObserver);
621
622 if (mNightDisplayController != null) {
623 mNightDisplayController = null;
Justin Klaassen2696d992016-07-11 21:26:46 -0700624 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700625
Christine Franks245ffd42018-11-16 13:45:14 -0800626 if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
627 if (mNightDisplayAutoMode != null) {
628 mNightDisplayAutoMode.onStop();
629 mNightDisplayAutoMode = null;
630 }
631 mNightDisplayTintController.endAnimator();
Justin Klaassen911e8892016-06-21 18:24:24 -0700632 }
633
Christine Franks245ffd42018-11-16 13:45:14 -0800634 if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
635 mDisplayWhiteBalanceTintController.endAnimator();
Justin Klaassen639214e2016-07-14 21:00:06 -0700636 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700637 }
638
Christine Franks57fdde82018-07-03 14:46:07 -0700639 private void onNightDisplayAutoModeChanged(int autoMode) {
640 Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700641
Christine Franks57fdde82018-07-03 14:46:07 -0700642 if (mNightDisplayAutoMode != null) {
643 mNightDisplayAutoMode.onStop();
644 mNightDisplayAutoMode = null;
Justin Klaassen911e8892016-06-21 18:24:24 -0700645 }
646
Christine Franks83cc5412018-07-03 14:46:07 -0700647 if (autoMode == AUTO_MODE_CUSTOM_TIME) {
Christine Franks57fdde82018-07-03 14:46:07 -0700648 mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
Christine Franks83cc5412018-07-03 14:46:07 -0700649 } else if (autoMode == AUTO_MODE_TWILIGHT) {
Christine Franks57fdde82018-07-03 14:46:07 -0700650 mNightDisplayAutoMode = new TwilightNightDisplayAutoMode();
Justin Klaassen911e8892016-06-21 18:24:24 -0700651 }
652
Christine Franks57fdde82018-07-03 14:46:07 -0700653 if (mNightDisplayAutoMode != null) {
654 mNightDisplayAutoMode.onStart();
Justin Klaassen911e8892016-06-21 18:24:24 -0700655 }
656 }
657
Christine Franks57fdde82018-07-03 14:46:07 -0700658 private void onNightDisplayCustomStartTimeChanged(LocalTime startTime) {
659 Slog.d(TAG, "onNightDisplayCustomStartTimeChanged: startTime=" + startTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700660
Christine Franks57fdde82018-07-03 14:46:07 -0700661 if (mNightDisplayAutoMode != null) {
662 mNightDisplayAutoMode.onCustomStartTimeChanged(startTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700663 }
664 }
665
Christine Franks57fdde82018-07-03 14:46:07 -0700666 private void onNightDisplayCustomEndTimeChanged(LocalTime endTime) {
667 Slog.d(TAG, "onNightDisplayCustomEndTimeChanged: endTime=" + endTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700668
Christine Franks57fdde82018-07-03 14:46:07 -0700669 if (mNightDisplayAutoMode != null) {
670 mNightDisplayAutoMode.onCustomEndTimeChanged(endTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700671 }
672 }
673
Christine Franks57fdde82018-07-03 14:46:07 -0700674 private void onDisplayColorModeChanged(int mode) {
Christine Franks83cc5412018-07-03 14:46:07 -0700675 if (mode == NOT_SET) {
Christine Frankscf388c22018-05-15 15:48:10 -0700676 return;
677 }
678
Christine Franks245ffd42018-11-16 13:45:14 -0800679 mNightDisplayTintController.cancelAnimator();
680 mDisplayWhiteBalanceTintController.cancelAnimator();
681
682 mNightDisplayTintController
683 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
Christine Franks83cc5412018-07-03 14:46:07 -0700684 mNightDisplayTintController.setMatrix(getNightDisplayColorTemperatureSetting());
Christine Franks245ffd42018-11-16 13:45:14 -0800685
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800686 updateDisplayWhiteBalanceStatus();
Christine Franks8ad71492017-10-24 19:04:22 -0700687
Christine Franks218e6562017-11-27 10:20:14 -0800688 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800689 dtm.setColorMode(mode, mNightDisplayTintController.getMatrix());
Christine Franks8ad71492017-10-24 19:04:22 -0700690 }
691
Christine Franks9114f462019-01-04 11:27:30 -0800692 private void onAccessibilityActivated() {
Christine Franks57fdde82018-07-03 14:46:07 -0700693 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
Daniel Solomon317a3572018-03-30 18:36:37 -0700694 }
695
Christine Franks9114f462019-01-04 11:27:30 -0800696 /**
697 * Apply the accessibility daltonizer transform based on the settings value.
698 */
699 private void onAccessibilityDaltonizerChanged() {
700 final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(),
701 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, mCurrentUser) != 0;
702 final int daltonizerMode = enabled ? Secure.getIntForUser(getContext().getContentResolver(),
703 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
704 AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser)
705 : AccessibilityManager.DALTONIZER_DISABLED;
706
707 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
708 if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
709 // Monochromacy isn't supported by the native Daltonizer implementation; use grayscale.
710 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE,
711 MATRIX_GRAYSCALE);
712 dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
713 } else {
714 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, null);
715 dtm.setDaltonizerMode(daltonizerMode);
716 }
717 }
718
719 /**
720 * Apply the accessibility inversion transform based on the settings value.
721 */
722 private void onAccessibilityInversionChanged() {
723 final boolean enabled = Secure.getIntForUser(getContext().getContentResolver(),
724 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) != 0;
725 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
726 dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_INVERT_COLOR,
727 enabled ? MATRIX_INVERT_COLOR : null);
728 }
Christine Franks8ad71492017-10-24 19:04:22 -0700729
Christine Franks6418d0b2017-02-13 09:48:00 -0800730 /**
731 * Applies current color temperature matrix, or removes it if deactivated.
732 *
733 * @param immediate {@code true} skips transition animation
734 */
Christine Franks245ffd42018-11-16 13:45:14 -0800735 private void applyTint(TintController tintController, boolean immediate) {
736 tintController.cancelAnimator();
Christine Franks6418d0b2017-02-13 09:48:00 -0800737
Christine Franks6418d0b2017-02-13 09:48:00 -0800738 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800739 final float[] from = dtm.getColorMatrix(tintController.getLevel());
740 final float[] to = tintController.getMatrix();
Christine Franks6418d0b2017-02-13 09:48:00 -0800741
742 if (immediate) {
Christine Franks245ffd42018-11-16 13:45:14 -0800743 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800744 } else {
Christine Franks245ffd42018-11-16 13:45:14 -0800745 tintController.setAnimator(ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
746 from == null ? MATRIX_IDENTITY : from, to));
747 tintController.getAnimator().setDuration(TRANSITION_DURATION);
748 tintController.getAnimator().setInterpolator(AnimationUtils.loadInterpolator(
Christine Franks6418d0b2017-02-13 09:48:00 -0800749 getContext(), android.R.interpolator.fast_out_slow_in));
Christine Franks245ffd42018-11-16 13:45:14 -0800750 tintController.getAnimator().addUpdateListener((ValueAnimator animator) -> {
751 final float[] value = (float[]) animator.getAnimatedValue();
752 dtm.setColorMatrix(tintController.getLevel(), value);
Christine Franks6418d0b2017-02-13 09:48:00 -0800753 });
Christine Franks245ffd42018-11-16 13:45:14 -0800754 tintController.getAnimator().addListener(new AnimatorListenerAdapter() {
Christine Franks6418d0b2017-02-13 09:48:00 -0800755
756 private boolean mIsCancelled;
757
758 @Override
759 public void onAnimationCancel(Animator animator) {
760 mIsCancelled = true;
761 }
762
763 @Override
764 public void onAnimationEnd(Animator animator) {
765 if (!mIsCancelled) {
766 // Ensure final color matrix is set at the end of the animation. If the
767 // animation is cancelled then don't set the final color matrix so the new
768 // animator can pick up from where this one left off.
Christine Franks245ffd42018-11-16 13:45:14 -0800769 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800770 }
Christine Franks245ffd42018-11-16 13:45:14 -0800771 tintController.setAnimator(null);
Christine Franks6418d0b2017-02-13 09:48:00 -0800772 }
773 });
Christine Franks245ffd42018-11-16 13:45:14 -0800774 tintController.getAnimator().start();
Christine Franks6418d0b2017-02-13 09:48:00 -0800775 }
776 }
777
778 /**
Christine Franks39b03112018-07-03 14:46:07 -0700779 * Returns the first date time corresponding to the local time that occurs before the provided
780 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700781 *
782 * @param compareTime the LocalDateTime to compare against
783 * @return the prior LocalDateTime corresponding to this local time
784 */
Christine Franks57fdde82018-07-03 14:46:07 -0700785 @VisibleForTesting
786 static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700787 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
788 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
789
790 // Check if the local time has passed, if so return the same time yesterday.
791 return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
792 }
793
794 /**
Christine Franks39b03112018-07-03 14:46:07 -0700795 * Returns the first date time corresponding to this local time that occurs after the provided
796 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700797 *
798 * @param compareTime the LocalDateTime to compare against
799 * @return the next LocalDateTime corresponding to this local time
800 */
Christine Franks57fdde82018-07-03 14:46:07 -0700801 @VisibleForTesting
802 static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700803 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
804 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
805
806 // Check if the local time has passed, if so return the same time tomorrow.
807 return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
808 }
809
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800810 private void updateDisplayWhiteBalanceStatus() {
811 boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
812 mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() &&
813 !mNightDisplayTintController.isActivated() &&
814 DisplayTransformManager.needsLinearColorMatrix());
815 boolean activated = mDisplayWhiteBalanceTintController.isActivated();
816
817 if (mDisplayWhiteBalanceListener != null && oldActivated != activated) {
818 mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(activated);
Christine Franks245ffd42018-11-16 13:45:14 -0800819 }
Daniel Solomon86508f82019-01-18 19:01:02 -0800820
821 // If disabled, clear the tint. If enabled, do nothing more here and let the next
822 // temperature update set the correct tint.
823 if (!activated) {
824 applyTint(mDisplayWhiteBalanceTintController, false);
825 }
Christine Franks245ffd42018-11-16 13:45:14 -0800826 }
827
828 private boolean isDisplayWhiteBalanceSettingEnabled() {
829 return Secure.getIntForUser(getContext().getContentResolver(),
830 Secure.DISPLAY_WHITE_BALANCE_ENABLED, 0, mCurrentUser) == 1;
831 }
832
Christine Franks39b03112018-07-03 14:46:07 -0700833 private boolean isDeviceColorManagedInternal() {
834 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
835 return dtm.isDeviceColorManaged();
836 }
837
Christine Franks55194dc2019-01-15 13:47:06 -0800838 private int getTransformCapabilitiesInternal() {
839 int availabilityFlags = ColorDisplayManager.CAPABILITY_NONE;
840 if (SurfaceControl.getProtectedContentSupport()) {
841 availabilityFlags |= ColorDisplayManager.CAPABILITY_PROTECTED_CONTENT;
842 }
843 final Resources res = getContext().getResources();
844 if (res.getBoolean(R.bool.config_setColorTransformAccelerated)) {
845 availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_GLOBAL;
846 }
847 if (res.getBoolean(R.bool.config_setColorTransformAcceleratedPerLayer)) {
848 availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_PER_APP;
849 }
850 return availabilityFlags;
851 }
852
Christine Franks83cc5412018-07-03 14:46:07 -0700853 private boolean setNightDisplayAutoModeInternal(@AutoMode int autoMode) {
854 if (getNightDisplayAutoModeInternal() != autoMode) {
855 Secure.putStringForUser(getContext().getContentResolver(),
856 Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
857 null,
858 mCurrentUser);
859 }
860 return Secure.putIntForUser(getContext().getContentResolver(),
861 Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mCurrentUser);
862 }
863
864 private int getNightDisplayAutoModeInternal() {
865 int autoMode = getNightDisplayAutoModeRawInternal();
866 if (autoMode == NOT_SET) {
867 autoMode = getContext().getResources().getInteger(
868 R.integer.config_defaultNightDisplayAutoMode);
869 }
870 if (autoMode != AUTO_MODE_DISABLED
871 && autoMode != AUTO_MODE_CUSTOM_TIME
872 && autoMode != AUTO_MODE_TWILIGHT) {
873 Slog.e(TAG, "Invalid autoMode: " + autoMode);
874 autoMode = AUTO_MODE_DISABLED;
875 }
876 return autoMode;
877 }
878
879 private int getNightDisplayAutoModeRawInternal() {
880 return Secure
881 .getIntForUser(getContext().getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE,
882 NOT_SET, mCurrentUser);
883 }
884
885 private Time getNightDisplayCustomStartTimeInternal() {
886 int startTimeValue = Secure.getIntForUser(getContext().getContentResolver(),
887 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, NOT_SET, mCurrentUser);
888 if (startTimeValue == NOT_SET) {
889 startTimeValue = getContext().getResources().getInteger(
890 R.integer.config_defaultNightDisplayCustomStartTime);
891 }
892 return new Time(LocalTime.ofSecondOfDay(startTimeValue / 1000));
893 }
894
895 private boolean setNightDisplayCustomStartTimeInternal(Time startTime) {
896 return Secure.putIntForUser(getContext().getContentResolver(),
897 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME,
898 startTime.getLocalTime().toSecondOfDay() * 1000,
899 mCurrentUser);
900 }
901
902 private Time getNightDisplayCustomEndTimeInternal() {
903 int endTimeValue = Secure.getIntForUser(getContext().getContentResolver(),
904 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, NOT_SET, mCurrentUser);
905 if (endTimeValue == NOT_SET) {
906 endTimeValue = getContext().getResources().getInteger(
907 R.integer.config_defaultNightDisplayCustomEndTime);
908 }
909 return new Time(LocalTime.ofSecondOfDay(endTimeValue / 1000));
910 }
911
912 private boolean setNightDisplayCustomEndTimeInternal(Time endTime) {
913 return Secure.putIntForUser(getContext().getContentResolver(),
914 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.getLocalTime().toSecondOfDay() * 1000,
915 mCurrentUser);
916 }
917
Christine Franks57fdde82018-07-03 14:46:07 -0700918 /**
919 * Returns the last time the night display transform activation state was changed, or {@link
920 * LocalDateTime#MIN} if night display has never been activated.
921 */
Christine Franks245ffd42018-11-16 13:45:14 -0800922 private LocalDateTime getNightDisplayLastActivatedTimeSetting() {
Christine Franks57fdde82018-07-03 14:46:07 -0700923 final ContentResolver cr = getContext().getContentResolver();
924 final String lastActivatedTime = Secure.getStringForUser(
925 cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
926 if (lastActivatedTime != null) {
927 try {
928 return LocalDateTime.parse(lastActivatedTime);
929 } catch (DateTimeParseException ignored) {
930 }
931 // Uses the old epoch time.
932 try {
933 return LocalDateTime.ofInstant(
934 Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
935 ZoneId.systemDefault());
936 } catch (DateTimeException | NumberFormatException ignored) {
937 }
938 }
939 return LocalDateTime.MIN;
940 }
941
Christine Franksf3529b22019-01-03 13:20:17 -0800942 private boolean setAppSaturationLevelInternal(String packageName, int saturationLevel) {
943 return mAppSaturationController
944 .setSaturationLevel(packageName, mCurrentUser, saturationLevel);
945 }
946
947 private void dumpInternal(PrintWriter pw) {
948 pw.println("COLOR DISPLAY MANAGER dumpsys (color_display)");
949 pw.println("Night Display:");
950 if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
951 pw.println(" Activated: " + mNightDisplayTintController.isActivated());
952 } else {
953 pw.println(" Not available");
954 }
955 mAppSaturationController.dump(pw);
956 }
957
Christine Franks83cc5412018-07-03 14:46:07 -0700958 private boolean isNightDisplayActivatedSetting() {
959 return Secure.getIntForUser(getContext().getContentResolver(),
960 Secure.NIGHT_DISPLAY_ACTIVATED, 0, mCurrentUser) == 1;
961 }
962
963 private int getNightDisplayColorTemperatureSetting() {
964 return clampNightDisplayColorTemperature(Secure.getIntForUser(
965 getContext().getContentResolver(), Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, NOT_SET,
966 mCurrentUser));
967 }
968
969 private int clampNightDisplayColorTemperature(int colorTemperature) {
970 if (colorTemperature == NOT_SET) {
971 colorTemperature = getContext().getResources().getInteger(
972 R.integer.config_nightDisplayColorTemperatureDefault);
973 }
974 final int minimumTemperature = ColorDisplayManager.getMinimumColorTemperature(getContext());
975 final int maximumTemperature = ColorDisplayManager.getMaximumColorTemperature(getContext());
976 if (colorTemperature < minimumTemperature) {
977 colorTemperature = minimumTemperature;
978 } else if (colorTemperature > maximumTemperature) {
979 colorTemperature = maximumTemperature;
980 }
981
982 return colorTemperature;
983 }
984
Christine Franks57fdde82018-07-03 14:46:07 -0700985 private abstract class NightDisplayAutoMode {
986
987 public abstract void onActivated(boolean activated);
988
Justin Klaassen911e8892016-06-21 18:24:24 -0700989 public abstract void onStart();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800990
Justin Klaassen911e8892016-06-21 18:24:24 -0700991 public abstract void onStop();
Christine Franks57fdde82018-07-03 14:46:07 -0700992
993 public void onCustomStartTimeChanged(LocalTime startTime) {
994 }
995
996 public void onCustomEndTimeChanged(LocalTime endTime) {
997 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700998 }
999
Christine Franks57fdde82018-07-03 14:46:07 -07001000 private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements
1001 AlarmManager.OnAlarmListener {
Justin Klaassen911e8892016-06-21 18:24:24 -07001002
1003 private final AlarmManager mAlarmManager;
1004 private final BroadcastReceiver mTimeChangedReceiver;
1005
Christine Franks03213462017-08-25 13:57:26 -07001006 private LocalTime mStartTime;
1007 private LocalTime mEndTime;
Justin Klaassen911e8892016-06-21 18:24:24 -07001008
Christine Franks03213462017-08-25 13:57:26 -07001009 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -07001010
Christine Franks57fdde82018-07-03 14:46:07 -07001011 CustomNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -07001012 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
1013 mTimeChangedReceiver = new BroadcastReceiver() {
1014 @Override
1015 public void onReceive(Context context, Intent intent) {
1016 updateActivated();
1017 }
1018 };
1019 }
1020
1021 private void updateActivated() {
Christine Franks03213462017-08-25 13:57:26 -07001022 final LocalDateTime now = LocalDateTime.now();
1023 final LocalDateTime start = getDateTimeBefore(mStartTime, now);
1024 final LocalDateTime end = getDateTimeAfter(mEndTime, start);
1025 boolean activate = now.isBefore(end);
Justin Klaassen911e8892016-06-21 18:24:24 -07001026
Christine Frankse5bb03e2017-02-10 17:36:10 -08001027 if (mLastActivatedTime != null) {
Christine Frankse5bb03e2017-02-10 17:36:10 -08001028 // Maintain the existing activated state if within the current period.
Christine Franks03213462017-08-25 13:57:26 -07001029 if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
1030 && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
Christine Franks83cc5412018-07-03 14:46:07 -07001031 activate = isNightDisplayActivatedSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -07001032 }
1033 }
1034
Christine Franks245ffd42018-11-16 13:45:14 -08001035 if (mNightDisplayTintController.isActivatedStateNotSet() || (
1036 mNightDisplayTintController.isActivated() != activate)) {
Christine Franks83cc5412018-07-03 14:46:07 -07001037 mNightDisplayTintController.setActivated(activate);
Justin Klaassen911e8892016-06-21 18:24:24 -07001038 }
Christine Franks03213462017-08-25 13:57:26 -07001039
Christine Franks245ffd42018-11-16 13:45:14 -08001040 updateNextAlarm(mNightDisplayTintController.isActivated(), now);
Justin Klaassen911e8892016-06-21 18:24:24 -07001041 }
1042
Christine Franks03213462017-08-25 13:57:26 -07001043 private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
Justin Klaassen4346f632016-08-08 15:01:47 -07001044 if (activated != null) {
Christine Franks03213462017-08-25 13:57:26 -07001045 final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
1046 : getDateTimeAfter(mStartTime, now);
1047 final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
1048 mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
Justin Klaassen911e8892016-06-21 18:24:24 -07001049 }
1050 }
1051
1052 @Override
1053 public void onStart() {
1054 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
1055 intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
1056 getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
1057
Christine Franks83cc5412018-07-03 14:46:07 -07001058 mStartTime = getNightDisplayCustomStartTimeInternal().getLocalTime();
1059 mEndTime = getNightDisplayCustomEndTimeInternal().getLocalTime();
Justin Klaassen911e8892016-06-21 18:24:24 -07001060
Christine Franks57fdde82018-07-03 14:46:07 -07001061 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Frankse5bb03e2017-02-10 17:36:10 -08001062
Justin Klaassen911e8892016-06-21 18:24:24 -07001063 // Force an update to initialize state.
1064 updateActivated();
1065 }
1066
1067 @Override
1068 public void onStop() {
1069 getContext().unregisterReceiver(mTimeChangedReceiver);
1070
1071 mAlarmManager.cancel(this);
1072 mLastActivatedTime = null;
1073 }
1074
1075 @Override
1076 public void onActivated(boolean activated) {
Christine Franks57fdde82018-07-03 14:46:07 -07001077 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Franks03213462017-08-25 13:57:26 -07001078 updateNextAlarm(activated, LocalDateTime.now());
Justin Klaassen911e8892016-06-21 18:24:24 -07001079 }
1080
1081 @Override
Christine Franks03213462017-08-25 13:57:26 -07001082 public void onCustomStartTimeChanged(LocalTime startTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -07001083 mStartTime = startTime;
1084 mLastActivatedTime = null;
1085 updateActivated();
1086 }
1087
1088 @Override
Christine Franks03213462017-08-25 13:57:26 -07001089 public void onCustomEndTimeChanged(LocalTime endTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -07001090 mEndTime = endTime;
1091 mLastActivatedTime = null;
1092 updateActivated();
1093 }
1094
1095 @Override
1096 public void onAlarm() {
Justin Klaassenec8837a2016-08-23 12:04:42 -07001097 Slog.d(TAG, "onAlarm");
Justin Klaassen911e8892016-06-21 18:24:24 -07001098 updateActivated();
1099 }
1100 }
1101
Christine Franks57fdde82018-07-03 14:46:07 -07001102 private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements
1103 TwilightListener {
Justin Klaassen911e8892016-06-21 18:24:24 -07001104
1105 private final TwilightManager mTwilightManager;
Christine Franks57fdde82018-07-03 14:46:07 -07001106 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -07001107
Christine Franks57fdde82018-07-03 14:46:07 -07001108 TwilightNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -07001109 mTwilightManager = getLocalService(TwilightManager.class);
Justin Klaassen911e8892016-06-21 18:24:24 -07001110 }
1111
Justin Klaassen908b86c2016-08-08 09:18:42 -07001112 private void updateActivated(TwilightState state) {
Justin Klaassen3da4c442017-05-05 15:19:33 -07001113 if (state == null) {
1114 // If there isn't a valid TwilightState then just keep the current activated
1115 // state.
1116 return;
1117 }
1118
1119 boolean activate = state.isNight();
Christine Franks57fdde82018-07-03 14:46:07 -07001120 if (mLastActivatedTime != null) {
Christine Franks03213462017-08-25 13:57:26 -07001121 final LocalDateTime now = LocalDateTime.now();
1122 final LocalDateTime sunrise = state.sunrise();
1123 final LocalDateTime sunset = state.sunset();
Christine Frankse5bb03e2017-02-10 17:36:10 -08001124 // Maintain the existing activated state if within the current period.
Christine Franks57fdde82018-07-03 14:46:07 -07001125 if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
1126 ^ mLastActivatedTime.isBefore(sunset))) {
Christine Franks83cc5412018-07-03 14:46:07 -07001127 activate = isNightDisplayActivatedSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -07001128 }
1129 }
Justin Klaassen908b86c2016-08-08 09:18:42 -07001130
Christine Franks245ffd42018-11-16 13:45:14 -08001131 if (mNightDisplayTintController.isActivatedStateNotSet() || (
1132 mNightDisplayTintController.isActivated() != activate)) {
Christine Franks83cc5412018-07-03 14:46:07 -07001133 mNightDisplayTintController.setActivated(activate);
Justin Klaassen908b86c2016-08-08 09:18:42 -07001134 }
Justin Klaassen911e8892016-06-21 18:24:24 -07001135 }
1136
1137 @Override
Christine Franks57fdde82018-07-03 14:46:07 -07001138 public void onActivated(boolean activated) {
1139 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
1140 }
1141
1142 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -07001143 public void onStart() {
1144 mTwilightManager.registerListener(this, mHandler);
Christine Franks57fdde82018-07-03 14:46:07 -07001145 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -07001146
1147 // Force an update to initialize state.
Justin Klaassen908b86c2016-08-08 09:18:42 -07001148 updateActivated(mTwilightManager.getLastTwilightState());
Justin Klaassen911e8892016-06-21 18:24:24 -07001149 }
1150
1151 @Override
1152 public void onStop() {
1153 mTwilightManager.unregisterListener(this);
Christine Franks57fdde82018-07-03 14:46:07 -07001154 mLastActivatedTime = null;
Justin Klaassen908b86c2016-08-08 09:18:42 -07001155 }
1156
1157 @Override
1158 public void onTwilightStateChanged(@Nullable TwilightState state) {
Justin Klaassenec8837a2016-08-23 12:04:42 -07001159 Slog.d(TAG, "onTwilightStateChanged: isNight="
1160 + (state == null ? null : state.isNight()));
Justin Klaassen908b86c2016-08-08 09:18:42 -07001161 updateActivated(state);
Justin Klaassen911e8892016-06-21 18:24:24 -07001162 }
1163 }
Justin Klaassen639214e2016-07-14 21:00:06 -07001164
1165 /**
1166 * Interpolates between two 4x4 color transform matrices (in column-major order).
1167 */
1168 private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> {
1169
1170 /**
1171 * Result matrix returned by {@link #evaluate(float, float[], float[])}.
1172 */
1173 private final float[] mResultMatrix = new float[16];
1174
1175 @Override
1176 public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
1177 for (int i = 0; i < mResultMatrix.length; i++) {
1178 mResultMatrix[i] = MathUtils.lerp(startValue[i], endValue[i], fraction);
1179 }
1180 return mResultMatrix;
1181 }
1182 }
Christine Franks39b03112018-07-03 14:46:07 -07001183
Christine Franks245ffd42018-11-16 13:45:14 -08001184 private abstract static class TintController {
1185
1186 private ValueAnimator mAnimator;
1187 private Boolean mIsActivated;
1188
1189 public ValueAnimator getAnimator() {
1190 return mAnimator;
1191 }
1192
1193 public void setAnimator(ValueAnimator animator) {
1194 mAnimator = animator;
1195 }
1196
1197 /**
1198 * Cancel the animator if it's still running.
1199 */
1200 public void cancelAnimator() {
1201 if (mAnimator != null) {
1202 mAnimator.cancel();
1203 }
1204 }
1205
1206 /**
1207 * End the animator if it's still running, jumping to the end state.
1208 */
1209 public void endAnimator() {
1210 if (mAnimator != null) {
1211 mAnimator.end();
1212 mAnimator = null;
1213 }
1214 }
1215
1216 public void setActivated(Boolean isActivated) {
1217 mIsActivated = isActivated;
1218 }
1219
1220 public boolean isActivated() {
1221 return mIsActivated != null && mIsActivated;
1222 }
1223
1224 public boolean isActivatedStateNotSet() {
1225 return mIsActivated == null;
1226 }
1227
1228 /**
Daniel Solomon8b72c5b2018-11-25 11:07:13 -08001229 * Dump debug information.
1230 */
1231 public void dump(PrintWriter pw) {
1232 }
1233
1234 /**
Christine Franks245ffd42018-11-16 13:45:14 -08001235 * Set up any constants needed for computing the matrix.
1236 */
1237 public abstract void setUp(Context context, boolean needsLinear);
1238
1239 /**
1240 * Sets the 4x4 matrix to apply.
1241 */
1242 public abstract void setMatrix(int value);
1243
1244 /**
1245 * Get the 4x4 matrix to apply.
1246 */
1247 public abstract float[] getMatrix();
1248
1249 /**
1250 * Get the color transform level to apply the matrix.
1251 */
1252 public abstract int getLevel();
1253 }
1254
Christine Franks83cc5412018-07-03 14:46:07 -07001255 private final class NightDisplayTintController extends TintController {
1256
1257 private float[] mMatrix = new float[16];
1258 private final float[] mColorTempCoefficients = new float[9];
1259 private Integer mColorTemp;
1260
1261 /**
1262 * Set coefficients based on whether the color matrix is linear or not.
1263 */
1264 @Override
1265 public void setUp(Context context, boolean needsLinear) {
1266 final String[] coefficients = context.getResources().getStringArray(needsLinear
1267 ? R.array.config_nightDisplayColorTemperatureCoefficients
1268 : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
1269 for (int i = 0; i < 9 && i < coefficients.length; i++) {
1270 mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
1271 }
1272 }
1273
1274 @Override
1275 public void setMatrix(int cct) {
1276 if (mMatrix.length != 16) {
1277 Slog.d(TAG, "The display transformation matrix must be 4x4");
1278 return;
1279 }
1280
1281 Matrix.setIdentityM(mMatrix, 0);
1282
1283 final float squareTemperature = cct * cct;
1284 final float red = squareTemperature * mColorTempCoefficients[0]
1285 + cct * mColorTempCoefficients[1] + mColorTempCoefficients[2];
1286 final float green = squareTemperature * mColorTempCoefficients[3]
1287 + cct * mColorTempCoefficients[4] + mColorTempCoefficients[5];
1288 final float blue = squareTemperature * mColorTempCoefficients[6]
1289 + cct * mColorTempCoefficients[7] + mColorTempCoefficients[8];
1290 mMatrix[0] = red;
1291 mMatrix[5] = green;
1292 mMatrix[10] = blue;
1293 }
1294
1295 @Override
1296 public float[] getMatrix() {
1297 return isActivated() ? mMatrix : MATRIX_IDENTITY;
1298 }
1299
1300 @Override
1301 public void setActivated(Boolean activated) {
1302 if (activated == null) {
1303 super.setActivated(null);
1304 return;
1305 }
1306
1307 boolean activationStateChanged = activated != isActivated();
1308
1309 if (!isActivatedStateNotSet() && activationStateChanged) {
1310 // This is a true state change, so set this as the last activation time.
1311 Secure.putStringForUser(getContext().getContentResolver(),
1312 Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
1313 LocalDateTime.now().toString(),
1314 mCurrentUser);
1315 }
1316
1317 if (isActivatedStateNotSet() || activationStateChanged) {
1318 super.setActivated(activated);
1319 Secure.putIntForUser(getContext().getContentResolver(),
1320 Secure.NIGHT_DISPLAY_ACTIVATED,
1321 activated ? 1 : 0, mCurrentUser);
1322 onActivated(activated);
1323 }
1324 }
1325
1326 @Override
1327 public int getLevel() {
1328 return LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
1329 }
1330
1331 void onActivated(boolean activated) {
1332 Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
1333 if (mNightDisplayAutoMode != null) {
1334 mNightDisplayAutoMode.onActivated(activated);
1335 }
1336
1337 if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
1338 updateDisplayWhiteBalanceStatus();
1339 }
1340
1341 mHandler.sendEmptyMessage(MSG_APPLY_NIGHT_DISPLAY_ANIMATED);
1342 }
1343
1344 int getColorTemperature() {
1345 return mColorTemp != null ? clampNightDisplayColorTemperature(mColorTemp)
1346 : getNightDisplayColorTemperatureSetting();
1347 }
1348
1349 boolean setColorTemperature(int temperature) {
1350 mColorTemp = temperature;
1351 final boolean success = Secure.putIntForUser(getContext().getContentResolver(),
1352 Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, temperature, mCurrentUser);
1353 onColorTemperatureChanged(temperature);
1354 return success;
1355 }
1356
1357 void onColorTemperatureChanged(int temperature) {
1358 setMatrix(temperature);
1359 mHandler.sendEmptyMessage(MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE);
1360 }
1361 }
1362
Christine Franks245ffd42018-11-16 13:45:14 -08001363 /**
1364 * Local service that allows color transforms to be enabled from other system services.
1365 */
1366 public final class ColorDisplayServiceInternal {
1367
1368 /**
1369 * Set the current CCT value for the display white balance transform, and if the transform
1370 * is enabled, apply it.
1371 *
1372 * @param cct the color temperature in Kelvin.
1373 */
1374 public boolean setDisplayWhiteBalanceColorTemperature(int cct) {
1375 // Update the transform matrix even if it can't be applied.
Christine Franks245ffd42018-11-16 13:45:14 -08001376 mDisplayWhiteBalanceTintController.setMatrix(cct);
1377
1378 if (mDisplayWhiteBalanceTintController.isActivated()) {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -08001379 applyTint(mDisplayWhiteBalanceTintController, false);
Christine Franks245ffd42018-11-16 13:45:14 -08001380 return true;
1381 }
1382 return false;
1383 }
1384
1385 /**
1386 * Sets the listener and returns whether display white balance is currently enabled.
1387 */
1388 public boolean setDisplayWhiteBalanceListener(DisplayWhiteBalanceListener listener) {
1389 mDisplayWhiteBalanceListener = listener;
1390 return mDisplayWhiteBalanceTintController.isActivated();
1391 }
Daniel Solomon8b72c5b2018-11-25 11:07:13 -08001392
1393 public void dump(PrintWriter pw) {
1394 mDisplayWhiteBalanceTintController.dump(pw);
1395 }
Christine Franksf3529b22019-01-03 13:20:17 -08001396
1397 /**
1398 * Adds a {@link WeakReference<ColorTransformController>} for a newly started activity, and
1399 * invokes {@link ColorTransformController#applyAppSaturation(float[], float[])} if needed.
1400 */
Christine Franks55194dc2019-01-15 13:47:06 -08001401 public boolean attachColorTransformController(String packageName, @UserIdInt int userId,
Christine Franksf3529b22019-01-03 13:20:17 -08001402 WeakReference<ColorTransformController> controller) {
1403 return mAppSaturationController
Christine Franks55194dc2019-01-15 13:47:06 -08001404 .addColorTransformController(packageName, userId, controller);
Christine Franksf3529b22019-01-03 13:20:17 -08001405 }
Christine Franks245ffd42018-11-16 13:45:14 -08001406 }
1407
1408 /**
1409 * Listener for changes in display white balance status.
1410 */
1411 public interface DisplayWhiteBalanceListener {
1412
1413 /**
1414 * Notify that the display white balance status has changed, either due to preemption by
1415 * another transform or the feature being turned off.
1416 */
1417 void onDisplayWhiteBalanceStatusChanged(boolean enabled);
1418 }
1419
Christine Franks09c229e2018-12-14 10:37:40 -08001420 private final class TintHandler extends Handler {
1421
Christine Franks83cc5412018-07-03 14:46:07 -07001422 private TintHandler(Looper looper) {
Christine Franks09c229e2018-12-14 10:37:40 -08001423 super(looper, null, true /* async */);
1424 }
1425
1426 @Override
1427 public void handleMessage(Message msg) {
1428 switch (msg.what) {
1429 case MSG_APPLY_GLOBAL_SATURATION:
1430 mGlobalSaturationTintController.setMatrix(msg.arg1);
1431 applyTint(mGlobalSaturationTintController, false);
1432 break;
Christine Franks83cc5412018-07-03 14:46:07 -07001433 case MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE:
1434 applyTint(mNightDisplayTintController, true);
1435 break;
1436 case MSG_APPLY_NIGHT_DISPLAY_ANIMATED:
1437 applyTint(mNightDisplayTintController, false);
1438 break;
Christine Franks09c229e2018-12-14 10:37:40 -08001439 }
1440 }
1441 }
1442
Christine Franksf3529b22019-01-03 13:20:17 -08001443 /**
1444 * Interface for applying transforms to a given AppWindow.
1445 */
1446 public interface ColorTransformController {
1447
1448 /** Apply the given saturation (grayscale) matrix to the associated AppWindow. */
1449 void applyAppSaturation(@Size(9) float[] matrix, @Size(3) float[] translation);
1450 }
1451
Christine Franks83cc5412018-07-03 14:46:07 -07001452 @VisibleForTesting
1453 final class BinderService extends IColorDisplayManager.Stub {
Christine Franks57fdde82018-07-03 14:46:07 -07001454
Christine Franks39b03112018-07-03 14:46:07 -07001455 @Override
1456 public boolean isDeviceColorManaged() {
1457 final long token = Binder.clearCallingIdentity();
1458 try {
1459 return isDeviceColorManagedInternal();
1460 } finally {
1461 Binder.restoreCallingIdentity(token);
1462 }
1463 }
Christine Franks09c229e2018-12-14 10:37:40 -08001464
1465 @Override
1466 public boolean setSaturationLevel(int level) {
1467 final boolean hasTransformsPermission = getContext()
1468 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
1469 == PackageManager.PERMISSION_GRANTED;
1470 final boolean hasLegacyPermission = getContext()
1471 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
1472 == PackageManager.PERMISSION_GRANTED;
1473 if (!hasTransformsPermission && !hasLegacyPermission) {
1474 throw new SecurityException("Permission required to set display saturation level");
1475 }
1476 final long token = Binder.clearCallingIdentity();
1477 try {
1478 final Message message = mHandler.obtainMessage(MSG_APPLY_GLOBAL_SATURATION);
1479 message.arg1 = level;
1480 mHandler.sendMessage(message);
1481 } finally {
1482 Binder.restoreCallingIdentity(token);
1483 }
1484 return true;
1485 }
Christine Franksf3529b22019-01-03 13:20:17 -08001486
1487 @Override
1488 public boolean setAppSaturationLevel(String packageName, int level) {
1489 getContext().enforceCallingPermission(
1490 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1491 "Permission required to set display saturation level");
1492 final long token = Binder.clearCallingIdentity();
1493 try {
1494 return setAppSaturationLevelInternal(packageName, level);
1495 } finally {
1496 Binder.restoreCallingIdentity(token);
1497 }
1498 }
1499
Christine Franks55194dc2019-01-15 13:47:06 -08001500 public int getTransformCapabilities() {
1501 getContext().enforceCallingPermission(
1502 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1503 "Permission required to query transform capabilities");
1504 final long token = Binder.clearCallingIdentity();
1505 try {
1506 return getTransformCapabilitiesInternal();
1507 } finally {
1508 Binder.restoreCallingIdentity(token);
1509 }
1510 }
1511
Christine Franksf3529b22019-01-03 13:20:17 -08001512 @Override
Christine Franks83cc5412018-07-03 14:46:07 -07001513 public boolean setNightDisplayActivated(boolean activated) {
1514 getContext().enforceCallingOrSelfPermission(
1515 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1516 "Permission required to set night display activated");
1517 final long token = Binder.clearCallingIdentity();
1518 try {
1519 mNightDisplayTintController.setActivated(activated);
1520 return true;
1521 } finally {
1522 Binder.restoreCallingIdentity(token);
1523 }
1524 }
1525
1526 @Override
1527 public boolean isNightDisplayActivated() {
1528 final long token = Binder.clearCallingIdentity();
1529 try {
1530 return mNightDisplayTintController.isActivated();
1531 } finally {
1532 Binder.restoreCallingIdentity(token);
1533 }
1534 }
1535
1536 @Override
1537 public boolean setNightDisplayColorTemperature(int temperature) {
1538 getContext().enforceCallingOrSelfPermission(
1539 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1540 "Permission required to set night display temperature");
1541 final long token = Binder.clearCallingIdentity();
1542 try {
1543 return mNightDisplayTintController.setColorTemperature(temperature);
1544 } finally {
1545 Binder.restoreCallingIdentity(token);
1546 }
1547 }
1548
1549 @Override
1550 public int getNightDisplayColorTemperature() {
1551 final long token = Binder.clearCallingIdentity();
1552 try {
1553 return mNightDisplayTintController.getColorTemperature();
1554 } finally {
1555 Binder.restoreCallingIdentity(token);
1556 }
1557 }
1558
1559 @Override
1560 public boolean setNightDisplayAutoMode(int autoMode) {
1561 getContext().enforceCallingOrSelfPermission(
1562 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1563 "Permission required to set night display auto mode");
1564 final long token = Binder.clearCallingIdentity();
1565 try {
1566 return setNightDisplayAutoModeInternal(autoMode);
1567 } finally {
1568 Binder.restoreCallingIdentity(token);
1569 }
1570 }
1571
1572 @Override
1573 public int getNightDisplayAutoMode() {
1574 getContext().enforceCallingOrSelfPermission(
1575 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1576 "Permission required to get night display auto mode");
1577 final long token = Binder.clearCallingIdentity();
1578 try {
1579 return getNightDisplayAutoModeInternal();
1580 } finally {
1581 Binder.restoreCallingIdentity(token);
1582 }
1583 }
1584
1585 @Override
1586 public int getNightDisplayAutoModeRaw() {
1587 final long token = Binder.clearCallingIdentity();
1588 try {
1589 return getNightDisplayAutoModeRawInternal();
1590 } finally {
1591 Binder.restoreCallingIdentity(token);
1592 }
1593 }
1594
1595 @Override
1596 public boolean setNightDisplayCustomStartTime(Time startTime) {
1597 getContext().enforceCallingOrSelfPermission(
1598 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1599 "Permission required to set night display custom start time");
1600 final long token = Binder.clearCallingIdentity();
1601 try {
1602 return setNightDisplayCustomStartTimeInternal(startTime);
1603 } finally {
1604 Binder.restoreCallingIdentity(token);
1605 }
1606 }
1607
1608 @Override
1609 public Time getNightDisplayCustomStartTime() {
1610 final long token = Binder.clearCallingIdentity();
1611 try {
1612 return getNightDisplayCustomStartTimeInternal();
1613 } finally {
1614 Binder.restoreCallingIdentity(token);
1615 }
1616 }
1617
1618 @Override
1619 public boolean setNightDisplayCustomEndTime(Time endTime) {
1620 getContext().enforceCallingOrSelfPermission(
1621 Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
1622 "Permission required to set night display custom end time");
1623 final long token = Binder.clearCallingIdentity();
1624 try {
1625 return setNightDisplayCustomEndTimeInternal(endTime);
1626 } finally {
1627 Binder.restoreCallingIdentity(token);
1628 }
1629 }
1630
1631 @Override
1632 public Time getNightDisplayCustomEndTime() {
1633 final long token = Binder.clearCallingIdentity();
1634 try {
1635 return getNightDisplayCustomEndTimeInternal();
1636 } finally {
1637 Binder.restoreCallingIdentity(token);
1638 }
1639 }
1640
1641 @Override
Christine Franksf3529b22019-01-03 13:20:17 -08001642 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1643 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
1644
1645 final long token = Binder.clearCallingIdentity();
1646 try {
1647 dumpInternal(pw);
1648 } finally {
1649 Binder.restoreCallingIdentity(token);
1650 }
1651 }
Christine Franks39b03112018-07-03 14:46:07 -07001652 }
Justin Klaassen911e8892016-06-21 18:24:24 -07001653}