blob: 2fe17d8f3c98601b9f6f2cf1cd7181d823ba9b96 [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 Franks245ffd42018-11-16 13:45:14 -080019import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
Christine Franks39b03112018-07-03 14:46:07 -070020import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
Christine Franks09c229e2018-12-14 10:37:40 -080021import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_SATURATION;
Christine Franks39b03112018-07-03 14:46:07 -070022
Christine Franks09c229e2018-12-14 10:37:40 -080023import android.Manifest;
Justin Klaassen639214e2016-07-14 21:00:06 -070024import android.animation.Animator;
25import android.animation.AnimatorListenerAdapter;
26import android.animation.TypeEvaluator;
27import android.animation.ValueAnimator;
Justin Klaassen4346f632016-08-08 15:01:47 -070028import android.annotation.NonNull;
Justin Klaassen908b86c2016-08-08 09:18:42 -070029import android.annotation.Nullable;
Justin Klaassen911e8892016-06-21 18:24:24 -070030import android.app.AlarmManager;
31import android.content.BroadcastReceiver;
32import android.content.ContentResolver;
33import android.content.Context;
34import android.content.Intent;
35import android.content.IntentFilter;
Christine Franks09c229e2018-12-14 10:37:40 -080036import android.content.pm.PackageManager;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080037import android.content.res.Resources;
Justin Klaassen2696d992016-07-11 21:26:46 -070038import android.database.ContentObserver;
Christine Franks245ffd42018-11-16 13:45:14 -080039import android.hardware.display.ColorDisplayManager;
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080040import android.graphics.ColorSpace;
Christine Franks39b03112018-07-03 14:46:07 -070041import android.hardware.display.IColorDisplayManager;
Justin Klaassen2696d992016-07-11 21:26:46 -070042import android.net.Uri;
Justin Klaassen639214e2016-07-14 21:00:06 -070043import android.opengl.Matrix;
Christine Franks39b03112018-07-03 14:46:07 -070044import android.os.Binder;
Justin Klaassen911e8892016-06-21 18:24:24 -070045import android.os.Handler;
46import android.os.Looper;
Christine Franks09c229e2018-12-14 10:37:40 -080047import android.os.Message;
Justin Klaassen911e8892016-06-21 18:24:24 -070048import android.os.UserHandle;
49import android.provider.Settings.Secure;
Christine Franks57fdde82018-07-03 14:46:07 -070050import android.provider.Settings.System;
Justin Klaassen639214e2016-07-14 21:00:06 -070051import android.util.MathUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070052import android.util.Slog;
Justin Klaassen639214e2016-07-14 21:00:06 -070053import android.view.animation.AnimationUtils;
Justin Klaassen911e8892016-06-21 18:24:24 -070054
Christine Franks39b03112018-07-03 14:46:07 -070055import com.android.internal.R;
Christine Franks57fdde82018-07-03 14:46:07 -070056import com.android.internal.annotations.VisibleForTesting;
Christine Franks5397f032017-11-01 18:35:16 -070057import com.android.internal.app.ColorDisplayController;
Christine Franks57fdde82018-07-03 14:46:07 -070058import com.android.server.DisplayThread;
Justin Klaassen911e8892016-06-21 18:24:24 -070059import com.android.server.SystemService;
60import com.android.server.twilight.TwilightListener;
61import com.android.server.twilight.TwilightManager;
62import com.android.server.twilight.TwilightState;
63
Daniel Solomon8b72c5b2018-11-25 11:07:13 -080064import java.io.PrintWriter;
Christine Franks57fdde82018-07-03 14:46:07 -070065import java.time.DateTimeException;
66import java.time.Instant;
Christine Franks03213462017-08-25 13:57:26 -070067import java.time.LocalDateTime;
68import java.time.LocalTime;
69import java.time.ZoneId;
Christine Franks57fdde82018-07-03 14:46:07 -070070import java.time.format.DateTimeParseException;
Christine Franks09c229e2018-12-14 10:37:40 -080071import java.util.Arrays;
Christine Franks8ad71492017-10-24 19:04:22 -070072
Justin Klaassen911e8892016-06-21 18:24:24 -070073/**
Christine Franks39b03112018-07-03 14:46:07 -070074 * Controls the display's color transforms.
Justin Klaassen911e8892016-06-21 18:24:24 -070075 */
Christine Franks57fdde82018-07-03 14:46:07 -070076public final class ColorDisplayService extends SystemService {
Justin Klaassen911e8892016-06-21 18:24:24 -070077
Christine Franks5397f032017-11-01 18:35:16 -070078 private static final String TAG = "ColorDisplayService";
Justin Klaassen911e8892016-06-21 18:24:24 -070079
80 /**
Christine Franks7b83b4282017-01-18 14:55:00 -080081 * The transition time, in milliseconds, for Night Display to turn on/off.
82 */
83 private static final long TRANSITION_DURATION = 3000L;
84
85 /**
Justin Klaassen639214e2016-07-14 21:00:06 -070086 * The identity matrix, used if one of the given matrices is {@code null}.
87 */
88 private static final float[] MATRIX_IDENTITY = new float[16];
Christine Franks57fdde82018-07-03 14:46:07 -070089
Justin Klaassen639214e2016-07-14 21:00:06 -070090 static {
91 Matrix.setIdentityM(MATRIX_IDENTITY, 0);
92 }
93
Christine Franks09c229e2018-12-14 10:37:40 -080094 private static final int MSG_APPLY_NIGHT_DISPLAY_IMMEDIATE = 0;
95 private static final int MSG_APPLY_NIGHT_DISPLAY_ANIMATED = 1;
96 private static final int MSG_APPLY_GLOBAL_SATURATION = 2;
97
Justin Klaassen639214e2016-07-14 21:00:06 -070098 /**
99 * Evaluator used to animate color matrix transitions.
100 */
101 private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
102
Christine Franks245ffd42018-11-16 13:45:14 -0800103 private final TintController mNightDisplayTintController = new TintController() {
104
105 private float[] mMatrixNightDisplay = new float[16];
106 private final float[] mColorTempCoefficients = new float[9];
107
108 /**
109 * Set coefficients based on whether the color matrix is linear or not.
110 */
111 @Override
112 public void setUp(Context context, boolean needsLinear) {
113 final String[] coefficients = context.getResources().getStringArray(needsLinear
114 ? R.array.config_nightDisplayColorTemperatureCoefficients
115 : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
116 for (int i = 0; i < 9 && i < coefficients.length; i++) {
117 mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
118 }
119 }
120
121 @Override
122 public void setMatrix(int cct) {
123 if (mMatrixNightDisplay.length != 16) {
124 Slog.d(TAG, "The display transformation matrix must be 4x4");
125 return;
126 }
127
128 Matrix.setIdentityM(mMatrixNightDisplay, 0);
129
130 final float squareTemperature = cct * cct;
131 final float red = squareTemperature * mColorTempCoefficients[0]
132 + cct * mColorTempCoefficients[1] + mColorTempCoefficients[2];
133 final float green = squareTemperature * mColorTempCoefficients[3]
134 + cct * mColorTempCoefficients[4] + mColorTempCoefficients[5];
135 final float blue = squareTemperature * mColorTempCoefficients[6]
136 + cct * mColorTempCoefficients[7] + mColorTempCoefficients[8];
137 mMatrixNightDisplay[0] = red;
138 mMatrixNightDisplay[5] = green;
139 mMatrixNightDisplay[10] = blue;
140 }
141
142 @Override
143 public float[] getMatrix() {
144 return isActivated() ? mMatrixNightDisplay : MATRIX_IDENTITY;
145 }
146
147 @Override
148 public int getLevel() {
149 return LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
150 }
151 };
152
153 private final TintController mDisplayWhiteBalanceTintController = new TintController() {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800154 // Three chromaticity coordinates per color: X, Y, and Z
155 private final int NUM_VALUES_PER_PRIMARY = 3;
156 // Four colors: red, green, blue, and white
157 private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY;
Christine Franks245ffd42018-11-16 13:45:14 -0800158
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800159 private final Object mLock = new Object();
160 private int mTemperatureMin;
161 private int mTemperatureMax;
162 private int mTemperatureDefault;
163 private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
164 private ColorSpace.Rgb mDisplayColorSpaceRGB;
165 private float[] mChromaticAdaptationMatrix;
166 private int mCurrentColorTemperature;
167 private float[] mCurrentColorTemperatureXYZ;
168 private boolean mSetUp = false;
Christine Franks245ffd42018-11-16 13:45:14 -0800169 private float[] mMatrixDisplayWhiteBalance = new float[16];
170
171 @Override
172 public void setUp(Context context, boolean needsLinear) {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800173 mSetUp = false;
174
175 final Resources res = getContext().getResources();
176 final String[] displayPrimariesValues = res.getStringArray(
177 R.array.config_displayWhiteBalanceDisplayPrimaries);
178 final String[] nominalWhiteValues = res.getStringArray(
179 R.array.config_displayWhiteBalanceDisplayNominalWhite);
180
181 if (displayPrimariesValues.length != NUM_DISPLAY_PRIMARIES_VALS) {
182 Slog.e(TAG, "Unexpected display white balance primaries resource length " +
183 displayPrimariesValues.length);
184 return;
185 }
186
187 if (nominalWhiteValues.length != NUM_VALUES_PER_PRIMARY) {
188 Slog.e(TAG, "Unexpected display white balance nominal white resource length " +
189 nominalWhiteValues.length);
190 return;
191 }
192
193 float[] displayRedGreenBlueXYZ =
194 new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
195 float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
196 for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
197 displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
198 }
199 for (int i = 0; i < displayWhiteXYZ.length; i++) {
200 displayWhiteXYZ[i] = Float.parseFloat(
201 displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
202 }
203
204 final ColorSpace.Rgb displayColorSpaceRGB = new ColorSpace.Rgb(
205 "Display Color Space",
206 displayRedGreenBlueXYZ,
207 displayWhiteXYZ,
208 2.2f // gamma, unused for display white balance
209 );
210
211 float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
212 for (int i = 0; i < nominalWhiteValues.length; i++) {
213 displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
214 }
215
216 final int colorTemperatureMin = res.getInteger(
217 R.integer.config_displayWhiteBalanceColorTemperatureMin);
218 if (colorTemperatureMin <= 0) {
219 Slog.e(TAG, "display white balance minimum temperature must be greater than 0");
220 return;
221 }
222
223 final int colorTemperatureMax = res.getInteger(
224 R.integer.config_displayWhiteBalanceColorTemperatureMax);
225 if (colorTemperatureMax < colorTemperatureMin) {
226 Slog.e(TAG, "display white balance max temp must be greater or equal to min");
227 return;
228 }
229
230 final int colorTemperature = res.getInteger(
231 R.integer.config_displayWhiteBalanceColorTemperatureDefault);
232
233 synchronized (mLock) {
234 mDisplayColorSpaceRGB = displayColorSpaceRGB;
235 mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
236 mTemperatureMin = colorTemperatureMin;
237 mTemperatureMax = colorTemperatureMax;
238 mTemperatureDefault = colorTemperature;
239 mSetUp = true;
240 }
241
242 setMatrix(mTemperatureDefault);
Christine Franks245ffd42018-11-16 13:45:14 -0800243 }
244
245 @Override
246 public float[] getMatrix() {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800247 return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
Christine Franks245ffd42018-11-16 13:45:14 -0800248 }
249
250 @Override
251 public void setMatrix(int cct) {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800252 if (!mSetUp) {
253 Slog.w(TAG, "Can't set display white balance temperature: uninitialized");
254 return;
255 }
256
257 if (cct < mTemperatureMin) {
258 Slog.w(TAG, "Requested display color temperature is below allowed minimum");
259 cct = mTemperatureMin;
260 } else if (cct > mTemperatureMax) {
261 Slog.w(TAG, "Requested display color temperature is above allowed maximum");
262 cct = mTemperatureMax;
263 }
264
265 Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);
266
267 synchronized (mLock) {
268 mCurrentColorTemperature = cct;
269
270 // Adapt the display's nominal white point to match the requested CCT value
271 mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct);
272
273 mChromaticAdaptationMatrix =
274 ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
275 mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
276
277 // Convert the adaptation matrix to RGB space
278 float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
279 mDisplayColorSpaceRGB.getTransform());
280 result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);
281
282 // Normalize the transform matrix to peak white value in RGB space
283 final float adaptedMaxR = result[0] + result[3] + result[6];
284 final float adaptedMaxG = result[1] + result[4] + result[7];
285 final float adaptedMaxB = result[2] + result[5] + result[8];
286 final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
287 for (int i = 0; i < result.length; i++) {
288 result[i] /= denum;
289 }
290
291 Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
292 java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
293 java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
294 java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
295 }
Christine Franks245ffd42018-11-16 13:45:14 -0800296 }
297
298 @Override
299 public int getLevel() {
300 return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
301 }
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800302
303 /**
304 * Format a given matrix into a string.
305 *
306 * @param matrix the matrix to format
307 * @param cols number of columns in the matrix
308 */
309 private String matrixToString(float[] matrix, int cols) {
310 if (matrix == null || cols <= 0) {
311 Slog.e(TAG, "Invalid arguments when formatting matrix to string");
312 return "";
313 }
314
315 StringBuilder sb = new StringBuilder("");
316 for (int i = 0; i < matrix.length; i++) {
317 if (i % cols == 0) {
318 sb.append("\n ");
319 }
320 sb.append(String.format("%9.6f ", matrix[i]));
321 }
322 return sb.toString();
323 }
324
325 @Override
326 public void dump(PrintWriter pw) {
327 synchronized (mLock) {
328 pw.println("ColorDisplayService");
329 pw.println(" mSetUp = " + mSetUp);
330
331 if (!mSetUp) {
332 return;
333 }
334
335 pw.println(" isActivated = " + isActivated());
336 pw.println(" mTemperatureMin = " + mTemperatureMin);
337 pw.println(" mTemperatureMax = " + mTemperatureMax);
338 pw.println(" mTemperatureDefault = " + mTemperatureDefault);
339 pw.println(" mCurrentColorTemperature = " + mCurrentColorTemperature);
340 pw.println(" mCurrentColorTemperatureXYZ = " +
341 matrixToString(mCurrentColorTemperatureXYZ, 3));
342 pw.println(" mDisplayColorSpaceRGB RGB-to-XYZ = " +
343 matrixToString(mDisplayColorSpaceRGB.getTransform(), 3));
344 pw.println(" mChromaticAdaptationMatrix = " +
345 matrixToString(mChromaticAdaptationMatrix, 3));
346 pw.println(" mDisplayColorSpaceRGB XYZ-to-RGB = " +
347 matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3));
348 pw.println(" mMatrixDisplayWhiteBalance = " +
349 matrixToString(mMatrixDisplayWhiteBalance, 4));
350 }
351 }
Christine Franks245ffd42018-11-16 13:45:14 -0800352 };
353
Christine Franks09c229e2018-12-14 10:37:40 -0800354 private final TintController mGlobalSaturationTintController = new TintController() {
355
356 private float[] mMatrixGlobalSaturation = new float[16];
357
358 @Override
359 public void setUp(Context context, boolean needsLinear) {
360 }
361
362 @Override
363 public float[] getMatrix() {
364 return Arrays.copyOf(mMatrixGlobalSaturation, mMatrixGlobalSaturation.length);
365 }
366
367 @Override
368 public void setMatrix(int saturationLevel) {
369 if (saturationLevel < 0) {
370 saturationLevel = 0;
371 } else if (saturationLevel > 100) {
372 saturationLevel = 100;
373 }
374 Slog.d(TAG, "Setting saturation level: " + saturationLevel);
375
376 if (saturationLevel == 100) {
377 Matrix.setIdentityM(mMatrixGlobalSaturation, 0);
378 } else {
379 float saturation = saturationLevel * 0.1f;
380 float desaturation = 1.0f - saturation;
381 float[] luminance = {0.231f * desaturation, 0.715f * desaturation,
382 0.072f * desaturation};
383 mMatrixGlobalSaturation[0] = luminance[0] + saturation;
384 mMatrixGlobalSaturation[1] = luminance[0];
385 mMatrixGlobalSaturation[2] = luminance[0];
386 mMatrixGlobalSaturation[4] = luminance[1];
387 mMatrixGlobalSaturation[5] = luminance[1] + saturation;
388 mMatrixGlobalSaturation[6] = luminance[1];
389 mMatrixGlobalSaturation[8] = luminance[2];
390 mMatrixGlobalSaturation[9] = luminance[2];
391 mMatrixGlobalSaturation[10] = luminance[2] + saturation;
392 }
393 }
394
395 @Override
396 public int getLevel() {
397 return LEVEL_COLOR_MATRIX_SATURATION;
398 }
399 };
400
Justin Klaassen2696d992016-07-11 21:26:46 -0700401 private final Handler mHandler;
402
Justin Klaassen911e8892016-06-21 18:24:24 -0700403 private int mCurrentUser = UserHandle.USER_NULL;
Justin Klaassen2696d992016-07-11 21:26:46 -0700404 private ContentObserver mUserSetupObserver;
Justin Klaassen911e8892016-06-21 18:24:24 -0700405 private boolean mBootCompleted;
406
Christine Franks57fdde82018-07-03 14:46:07 -0700407 private ColorDisplayController mNightDisplayController;
408 private ContentObserver mContentObserver;
Christine Franks57fdde82018-07-03 14:46:07 -0700409
Christine Franks245ffd42018-11-16 13:45:14 -0800410 private DisplayWhiteBalanceListener mDisplayWhiteBalanceListener;
411
Christine Franks57fdde82018-07-03 14:46:07 -0700412 private NightDisplayAutoMode mNightDisplayAutoMode;
Justin Klaassen911e8892016-06-21 18:24:24 -0700413
Christine Franks5397f032017-11-01 18:35:16 -0700414 public ColorDisplayService(Context context) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700415 super(context);
Christine Franks09c229e2018-12-14 10:37:40 -0800416 mHandler = new TintHandler(Looper.getMainLooper());
Justin Klaassen911e8892016-06-21 18:24:24 -0700417 }
418
419 @Override
420 public void onStart() {
Christine Franks39b03112018-07-03 14:46:07 -0700421 publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService());
Christine Franks245ffd42018-11-16 13:45:14 -0800422 publishLocalService(ColorDisplayServiceInternal.class, new ColorDisplayServiceInternal());
Justin Klaassen911e8892016-06-21 18:24:24 -0700423 }
424
425 @Override
Justin Klaassen2696d992016-07-11 21:26:46 -0700426 public void onBootPhase(int phase) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800427 if (phase >= PHASE_BOOT_COMPLETED) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700428 mBootCompleted = true;
429
430 // Register listeners now that boot is complete.
431 if (mCurrentUser != UserHandle.USER_NULL && mUserSetupObserver == null) {
432 setUp();
433 }
434 }
435 }
436
437 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700438 public void onStartUser(int userHandle) {
439 super.onStartUser(userHandle);
440
Justin Klaassen911e8892016-06-21 18:24:24 -0700441 if (mCurrentUser == UserHandle.USER_NULL) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700442 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700443 }
444 }
445
446 @Override
447 public void onSwitchUser(int userHandle) {
448 super.onSwitchUser(userHandle);
449
Justin Klaassen2696d992016-07-11 21:26:46 -0700450 onUserChanged(userHandle);
Justin Klaassen911e8892016-06-21 18:24:24 -0700451 }
452
453 @Override
454 public void onStopUser(int userHandle) {
455 super.onStopUser(userHandle);
456
Justin Klaassen911e8892016-06-21 18:24:24 -0700457 if (mCurrentUser == userHandle) {
Justin Klaassen2696d992016-07-11 21:26:46 -0700458 onUserChanged(UserHandle.USER_NULL);
Justin Klaassen911e8892016-06-21 18:24:24 -0700459 }
460 }
461
Justin Klaassen2696d992016-07-11 21:26:46 -0700462 private void onUserChanged(int userHandle) {
463 final ContentResolver cr = getContext().getContentResolver();
Justin Klaassen911e8892016-06-21 18:24:24 -0700464
Justin Klaassen2696d992016-07-11 21:26:46 -0700465 if (mCurrentUser != UserHandle.USER_NULL) {
466 if (mUserSetupObserver != null) {
467 cr.unregisterContentObserver(mUserSetupObserver);
468 mUserSetupObserver = null;
469 } else if (mBootCompleted) {
470 tearDown();
471 }
472 }
473
474 mCurrentUser = userHandle;
475
476 if (mCurrentUser != UserHandle.USER_NULL) {
477 if (!isUserSetupCompleted(cr, mCurrentUser)) {
478 mUserSetupObserver = new ContentObserver(mHandler) {
479 @Override
480 public void onChange(boolean selfChange, Uri uri) {
481 if (isUserSetupCompleted(cr, mCurrentUser)) {
482 cr.unregisterContentObserver(this);
483 mUserSetupObserver = null;
484
485 if (mBootCompleted) {
486 setUp();
487 }
488 }
489 }
490 };
491 cr.registerContentObserver(Secure.getUriFor(Secure.USER_SETUP_COMPLETE),
Christine Franks39b03112018-07-03 14:46:07 -0700492 false /* notifyForDescendants */, mUserSetupObserver, mCurrentUser);
Justin Klaassen2696d992016-07-11 21:26:46 -0700493 } else if (mBootCompleted) {
494 setUp();
Justin Klaassen911e8892016-06-21 18:24:24 -0700495 }
496 }
497 }
498
Justin Klaassen2696d992016-07-11 21:26:46 -0700499 private static boolean isUserSetupCompleted(ContentResolver cr, int userHandle) {
500 return Secure.getIntForUser(cr, Secure.USER_SETUP_COMPLETE, 0, userHandle) == 1;
501 }
502
503 private void setUp() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700504 Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
505
Christine Franks57fdde82018-07-03 14:46:07 -0700506 mNightDisplayController = new ColorDisplayController(getContext(), mCurrentUser);
507
508 // Listen for external changes to any of the settings.
509 if (mContentObserver == null) {
510 mContentObserver = new ContentObserver(new Handler(DisplayThread.get().getLooper())) {
511 @Override
512 public void onChange(boolean selfChange, Uri uri) {
513 super.onChange(selfChange, uri);
514
515 final String setting = uri == null ? null : uri.getLastPathSegment();
516 if (setting != null) {
517 switch (setting) {
518 case Secure.NIGHT_DISPLAY_ACTIVATED:
519 onNightDisplayActivated(mNightDisplayController.isActivated());
520 break;
521 case Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE:
522 onNightDisplayColorTemperatureChanged(
523 mNightDisplayController.getColorTemperature());
524 break;
525 case Secure.NIGHT_DISPLAY_AUTO_MODE:
526 onNightDisplayAutoModeChanged(
527 mNightDisplayController.getAutoMode());
528 break;
529 case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
530 onNightDisplayCustomStartTimeChanged(
531 mNightDisplayController.getCustomStartTime());
532 break;
533 case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
534 onNightDisplayCustomEndTimeChanged(
535 mNightDisplayController.getCustomEndTime());
536 break;
537 case System.DISPLAY_COLOR_MODE:
538 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
539 break;
540 case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED:
541 case Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED:
542 onAccessibilityTransformChanged();
543 break;
Christine Franks245ffd42018-11-16 13:45:14 -0800544 case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800545 updateDisplayWhiteBalanceStatus();
Christine Franks245ffd42018-11-16 13:45:14 -0800546 break;
Christine Franks57fdde82018-07-03 14:46:07 -0700547 }
548 }
549 }
550 };
551 }
552 final ContentResolver cr = getContext().getContentResolver();
553 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
554 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
555 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE),
556 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
557 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
558 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
559 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
560 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
561 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
562 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
563 cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE),
564 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
565 cr.registerContentObserver(
566 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED),
567 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
568 cr.registerContentObserver(
569 Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED),
570 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Christine Franks245ffd42018-11-16 13:45:14 -0800571 cr.registerContentObserver(Secure.getUriFor(Secure.DISPLAY_WHITE_BALANCE_ENABLED),
572 false /* notifyForDescendants */, mContentObserver, mCurrentUser);
Justin Klaassen911e8892016-06-21 18:24:24 -0700573
Christine Frankscf388c22018-05-15 15:48:10 -0700574 // Set the color mode, if valid, and immediately apply the updated tint matrix based on the
575 // existing activated state. This ensures consistency of tint across the color mode change.
Christine Franks57fdde82018-07-03 14:46:07 -0700576 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
Christine Frankscf388c22018-05-15 15:48:10 -0700577
Christine Franks245ffd42018-11-16 13:45:14 -0800578 if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
579 // Reset the activated state.
580 mNightDisplayTintController.setActivated(null);
Christine Frankscf388c22018-05-15 15:48:10 -0700581
Christine Franks245ffd42018-11-16 13:45:14 -0800582 // Prepare the night display color transformation matrix.
583 mNightDisplayTintController
584 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
585 mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
Christine Franks8ad71492017-10-24 19:04:22 -0700586
Christine Franks245ffd42018-11-16 13:45:14 -0800587 // Initialize the current auto mode.
588 onNightDisplayAutoModeChanged(mNightDisplayController.getAutoMode());
Christine Franks6418d0b2017-02-13 09:48:00 -0800589
Christine Franks245ffd42018-11-16 13:45:14 -0800590 // Force the initialization current activated state.
591 if (mNightDisplayTintController.isActivatedStateNotSet()) {
592 onNightDisplayActivated(mNightDisplayController.isActivated());
593 }
594 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700595
Christine Franks245ffd42018-11-16 13:45:14 -0800596 if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
597 // Prepare the display white balance transform matrix.
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800598 mDisplayWhiteBalanceTintController.setUp(getContext(), true /* needsLinear */);
Christine Franks245ffd42018-11-16 13:45:14 -0800599
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800600 updateDisplayWhiteBalanceStatus();
Justin Klaassen911e8892016-06-21 18:24:24 -0700601 }
602 }
603
Justin Klaassen2696d992016-07-11 21:26:46 -0700604 private void tearDown() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700605 Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
606
Christine Franks57fdde82018-07-03 14:46:07 -0700607 getContext().getContentResolver().unregisterContentObserver(mContentObserver);
608
609 if (mNightDisplayController != null) {
610 mNightDisplayController = null;
Justin Klaassen2696d992016-07-11 21:26:46 -0700611 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700612
Christine Franks245ffd42018-11-16 13:45:14 -0800613 if (ColorDisplayManager.isNightDisplayAvailable(getContext())) {
614 if (mNightDisplayAutoMode != null) {
615 mNightDisplayAutoMode.onStop();
616 mNightDisplayAutoMode = null;
617 }
618 mNightDisplayTintController.endAnimator();
Justin Klaassen911e8892016-06-21 18:24:24 -0700619 }
620
Christine Franks245ffd42018-11-16 13:45:14 -0800621 if (ColorDisplayManager.isDisplayWhiteBalanceAvailable(getContext())) {
622 mDisplayWhiteBalanceTintController.endAnimator();
Justin Klaassen639214e2016-07-14 21:00:06 -0700623 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700624 }
625
Christine Franks57fdde82018-07-03 14:46:07 -0700626 private void onNightDisplayActivated(boolean activated) {
Christine Franks245ffd42018-11-16 13:45:14 -0800627 if (mNightDisplayTintController.isActivatedStateNotSet()
628 || mNightDisplayTintController.isActivated() != activated) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700629 Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
630
Christine Franks245ffd42018-11-16 13:45:14 -0800631 mNightDisplayTintController.setActivated(activated);
Justin Klaassen908b86c2016-08-08 09:18:42 -0700632
Christine Franks57fdde82018-07-03 14:46:07 -0700633 if (mNightDisplayAutoMode != null) {
634 mNightDisplayAutoMode.onActivated(activated);
Christine Frankse5bb03e2017-02-10 17:36:10 -0800635 }
636
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800637 updateDisplayWhiteBalanceStatus();
638
Christine Franks245ffd42018-11-16 13:45:14 -0800639 applyTint(mNightDisplayTintController, false);
Justin Klaassen911e8892016-06-21 18:24:24 -0700640 }
641 }
642
Christine Franks57fdde82018-07-03 14:46:07 -0700643 private void onNightDisplayAutoModeChanged(int autoMode) {
644 Slog.d(TAG, "onNightDisplayAutoModeChanged: autoMode=" + autoMode);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700645
Christine Franks57fdde82018-07-03 14:46:07 -0700646 if (mNightDisplayAutoMode != null) {
647 mNightDisplayAutoMode.onStop();
648 mNightDisplayAutoMode = null;
Justin Klaassen911e8892016-06-21 18:24:24 -0700649 }
650
Christine Franks5397f032017-11-01 18:35:16 -0700651 if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
Christine Franks57fdde82018-07-03 14:46:07 -0700652 mNightDisplayAutoMode = new CustomNightDisplayAutoMode();
Christine Franks5397f032017-11-01 18:35:16 -0700653 } else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
Christine Franks57fdde82018-07-03 14:46:07 -0700654 mNightDisplayAutoMode = new TwilightNightDisplayAutoMode();
Justin Klaassen911e8892016-06-21 18:24:24 -0700655 }
656
Christine Franks57fdde82018-07-03 14:46:07 -0700657 if (mNightDisplayAutoMode != null) {
658 mNightDisplayAutoMode.onStart();
Justin Klaassen911e8892016-06-21 18:24:24 -0700659 }
660 }
661
Christine Franks57fdde82018-07-03 14:46:07 -0700662 private void onNightDisplayCustomStartTimeChanged(LocalTime startTime) {
663 Slog.d(TAG, "onNightDisplayCustomStartTimeChanged: startTime=" + startTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700664
Christine Franks57fdde82018-07-03 14:46:07 -0700665 if (mNightDisplayAutoMode != null) {
666 mNightDisplayAutoMode.onCustomStartTimeChanged(startTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700667 }
668 }
669
Christine Franks57fdde82018-07-03 14:46:07 -0700670 private void onNightDisplayCustomEndTimeChanged(LocalTime endTime) {
671 Slog.d(TAG, "onNightDisplayCustomEndTimeChanged: endTime=" + endTime);
Justin Klaassenec8837a2016-08-23 12:04:42 -0700672
Christine Franks57fdde82018-07-03 14:46:07 -0700673 if (mNightDisplayAutoMode != null) {
674 mNightDisplayAutoMode.onCustomEndTimeChanged(endTime);
Justin Klaassen911e8892016-06-21 18:24:24 -0700675 }
676 }
677
Christine Franks57fdde82018-07-03 14:46:07 -0700678 private void onNightDisplayColorTemperatureChanged(int colorTemperature) {
Christine Franks245ffd42018-11-16 13:45:14 -0800679 mNightDisplayTintController.setMatrix(colorTemperature);
680 applyTint(mNightDisplayTintController, true);
Christine Franks6418d0b2017-02-13 09:48:00 -0800681 }
682
Christine Franks57fdde82018-07-03 14:46:07 -0700683 private void onDisplayColorModeChanged(int mode) {
Christine Frankscf388c22018-05-15 15:48:10 -0700684 if (mode == -1) {
685 return;
686 }
687
Christine Franks245ffd42018-11-16 13:45:14 -0800688 mNightDisplayTintController.cancelAnimator();
689 mDisplayWhiteBalanceTintController.cancelAnimator();
690
691 mNightDisplayTintController
692 .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
693 mNightDisplayTintController.setMatrix(mNightDisplayController.getColorTemperature());
694
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800695 updateDisplayWhiteBalanceStatus();
Christine Franks8ad71492017-10-24 19:04:22 -0700696
Christine Franks218e6562017-11-27 10:20:14 -0800697 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800698 dtm.setColorMode(mode, mNightDisplayTintController.getMatrix());
Christine Franks8ad71492017-10-24 19:04:22 -0700699 }
700
Christine Franks57fdde82018-07-03 14:46:07 -0700701 private void onAccessibilityTransformChanged() {
702 onDisplayColorModeChanged(mNightDisplayController.getColorMode());
Daniel Solomon317a3572018-03-30 18:36:37 -0700703 }
704
Christine Franks8ad71492017-10-24 19:04:22 -0700705
Christine Franks6418d0b2017-02-13 09:48:00 -0800706 /**
707 * Applies current color temperature matrix, or removes it if deactivated.
708 *
709 * @param immediate {@code true} skips transition animation
710 */
Christine Franks245ffd42018-11-16 13:45:14 -0800711 private void applyTint(TintController tintController, boolean immediate) {
712 tintController.cancelAnimator();
Christine Franks6418d0b2017-02-13 09:48:00 -0800713
Christine Franks6418d0b2017-02-13 09:48:00 -0800714 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
Christine Franks245ffd42018-11-16 13:45:14 -0800715 final float[] from = dtm.getColorMatrix(tintController.getLevel());
716 final float[] to = tintController.getMatrix();
Christine Franks6418d0b2017-02-13 09:48:00 -0800717
718 if (immediate) {
Christine Franks245ffd42018-11-16 13:45:14 -0800719 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800720 } else {
Christine Franks245ffd42018-11-16 13:45:14 -0800721 tintController.setAnimator(ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR,
722 from == null ? MATRIX_IDENTITY : from, to));
723 tintController.getAnimator().setDuration(TRANSITION_DURATION);
724 tintController.getAnimator().setInterpolator(AnimationUtils.loadInterpolator(
Christine Franks6418d0b2017-02-13 09:48:00 -0800725 getContext(), android.R.interpolator.fast_out_slow_in));
Christine Franks245ffd42018-11-16 13:45:14 -0800726 tintController.getAnimator().addUpdateListener((ValueAnimator animator) -> {
727 final float[] value = (float[]) animator.getAnimatedValue();
728 dtm.setColorMatrix(tintController.getLevel(), value);
Christine Franks6418d0b2017-02-13 09:48:00 -0800729 });
Christine Franks245ffd42018-11-16 13:45:14 -0800730 tintController.getAnimator().addListener(new AnimatorListenerAdapter() {
Christine Franks6418d0b2017-02-13 09:48:00 -0800731
732 private boolean mIsCancelled;
733
734 @Override
735 public void onAnimationCancel(Animator animator) {
736 mIsCancelled = true;
737 }
738
739 @Override
740 public void onAnimationEnd(Animator animator) {
741 if (!mIsCancelled) {
742 // Ensure final color matrix is set at the end of the animation. If the
743 // animation is cancelled then don't set the final color matrix so the new
744 // animator can pick up from where this one left off.
Christine Franks245ffd42018-11-16 13:45:14 -0800745 dtm.setColorMatrix(tintController.getLevel(), to);
Christine Franks6418d0b2017-02-13 09:48:00 -0800746 }
Christine Franks245ffd42018-11-16 13:45:14 -0800747 tintController.setAnimator(null);
Christine Franks6418d0b2017-02-13 09:48:00 -0800748 }
749 });
Christine Franks245ffd42018-11-16 13:45:14 -0800750 tintController.getAnimator().start();
Christine Franks6418d0b2017-02-13 09:48:00 -0800751 }
752 }
753
754 /**
Christine Franks39b03112018-07-03 14:46:07 -0700755 * Returns the first date time corresponding to the local time that occurs before the provided
756 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700757 *
758 * @param compareTime the LocalDateTime to compare against
759 * @return the prior LocalDateTime corresponding to this local time
760 */
Christine Franks57fdde82018-07-03 14:46:07 -0700761 @VisibleForTesting
762 static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700763 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
764 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
765
766 // Check if the local time has passed, if so return the same time yesterday.
767 return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
768 }
769
770 /**
Christine Franks39b03112018-07-03 14:46:07 -0700771 * Returns the first date time corresponding to this local time that occurs after the provided
772 * date time.
Christine Franks03213462017-08-25 13:57:26 -0700773 *
774 * @param compareTime the LocalDateTime to compare against
775 * @return the next LocalDateTime corresponding to this local time
776 */
Christine Franks57fdde82018-07-03 14:46:07 -0700777 @VisibleForTesting
778 static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
Christine Franks03213462017-08-25 13:57:26 -0700779 final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
780 compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
781
782 // Check if the local time has passed, if so return the same time tomorrow.
783 return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
784 }
785
Daniel Solomon8b72c5b2018-11-25 11:07:13 -0800786 private void updateDisplayWhiteBalanceStatus() {
787 boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
788 mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() &&
789 !mNightDisplayTintController.isActivated() &&
790 DisplayTransformManager.needsLinearColorMatrix());
791 boolean activated = mDisplayWhiteBalanceTintController.isActivated();
792
793 if (mDisplayWhiteBalanceListener != null && oldActivated != activated) {
794 mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(activated);
Christine Franks245ffd42018-11-16 13:45:14 -0800795 }
796 }
797
798 private boolean isDisplayWhiteBalanceSettingEnabled() {
799 return Secure.getIntForUser(getContext().getContentResolver(),
800 Secure.DISPLAY_WHITE_BALANCE_ENABLED, 0, mCurrentUser) == 1;
801 }
802
Christine Franks39b03112018-07-03 14:46:07 -0700803 private boolean isDeviceColorManagedInternal() {
804 final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
805 return dtm.isDeviceColorManaged();
806 }
807
Christine Franks57fdde82018-07-03 14:46:07 -0700808 /**
809 * Returns the last time the night display transform activation state was changed, or {@link
810 * LocalDateTime#MIN} if night display has never been activated.
811 */
Christine Franks245ffd42018-11-16 13:45:14 -0800812 private LocalDateTime getNightDisplayLastActivatedTimeSetting() {
Christine Franks57fdde82018-07-03 14:46:07 -0700813 final ContentResolver cr = getContext().getContentResolver();
814 final String lastActivatedTime = Secure.getStringForUser(
815 cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, getContext().getUserId());
816 if (lastActivatedTime != null) {
817 try {
818 return LocalDateTime.parse(lastActivatedTime);
819 } catch (DateTimeParseException ignored) {
820 }
821 // Uses the old epoch time.
822 try {
823 return LocalDateTime.ofInstant(
824 Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
825 ZoneId.systemDefault());
826 } catch (DateTimeException | NumberFormatException ignored) {
827 }
828 }
829 return LocalDateTime.MIN;
830 }
831
832 private abstract class NightDisplayAutoMode {
833
834 public abstract void onActivated(boolean activated);
835
Justin Klaassen911e8892016-06-21 18:24:24 -0700836 public abstract void onStart();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800837
Justin Klaassen911e8892016-06-21 18:24:24 -0700838 public abstract void onStop();
Christine Franks57fdde82018-07-03 14:46:07 -0700839
840 public void onCustomStartTimeChanged(LocalTime startTime) {
841 }
842
843 public void onCustomEndTimeChanged(LocalTime endTime) {
844 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700845 }
846
Christine Franks57fdde82018-07-03 14:46:07 -0700847 private final class CustomNightDisplayAutoMode extends NightDisplayAutoMode implements
848 AlarmManager.OnAlarmListener {
Justin Klaassen911e8892016-06-21 18:24:24 -0700849
850 private final AlarmManager mAlarmManager;
851 private final BroadcastReceiver mTimeChangedReceiver;
852
Christine Franks03213462017-08-25 13:57:26 -0700853 private LocalTime mStartTime;
854 private LocalTime mEndTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700855
Christine Franks03213462017-08-25 13:57:26 -0700856 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700857
Christine Franks57fdde82018-07-03 14:46:07 -0700858 CustomNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -0700859 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
860 mTimeChangedReceiver = new BroadcastReceiver() {
861 @Override
862 public void onReceive(Context context, Intent intent) {
863 updateActivated();
864 }
865 };
866 }
867
868 private void updateActivated() {
Christine Franks03213462017-08-25 13:57:26 -0700869 final LocalDateTime now = LocalDateTime.now();
870 final LocalDateTime start = getDateTimeBefore(mStartTime, now);
871 final LocalDateTime end = getDateTimeAfter(mEndTime, start);
872 boolean activate = now.isBefore(end);
Justin Klaassen911e8892016-06-21 18:24:24 -0700873
Christine Frankse5bb03e2017-02-10 17:36:10 -0800874 if (mLastActivatedTime != null) {
Christine Frankse5bb03e2017-02-10 17:36:10 -0800875 // Maintain the existing activated state if within the current period.
Christine Franks03213462017-08-25 13:57:26 -0700876 if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
877 && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
Christine Franks57fdde82018-07-03 14:46:07 -0700878 activate = mNightDisplayController.isActivated();
Justin Klaassen911e8892016-06-21 18:24:24 -0700879 }
880 }
881
Christine Franks245ffd42018-11-16 13:45:14 -0800882 if (mNightDisplayTintController.isActivatedStateNotSet() || (
883 mNightDisplayTintController.isActivated() != activate)) {
Christine Franks57fdde82018-07-03 14:46:07 -0700884 mNightDisplayController.setActivated(activate);
Justin Klaassen911e8892016-06-21 18:24:24 -0700885 }
Christine Franks03213462017-08-25 13:57:26 -0700886
Christine Franks245ffd42018-11-16 13:45:14 -0800887 updateNextAlarm(mNightDisplayTintController.isActivated(), now);
Justin Klaassen911e8892016-06-21 18:24:24 -0700888 }
889
Christine Franks03213462017-08-25 13:57:26 -0700890 private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
Justin Klaassen4346f632016-08-08 15:01:47 -0700891 if (activated != null) {
Christine Franks03213462017-08-25 13:57:26 -0700892 final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
893 : getDateTimeAfter(mStartTime, now);
894 final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
895 mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
Justin Klaassen911e8892016-06-21 18:24:24 -0700896 }
897 }
898
899 @Override
900 public void onStart() {
901 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
902 intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
903 getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
904
Christine Franks57fdde82018-07-03 14:46:07 -0700905 mStartTime = mNightDisplayController.getCustomStartTime();
906 mEndTime = mNightDisplayController.getCustomEndTime();
Justin Klaassen911e8892016-06-21 18:24:24 -0700907
Christine Franks57fdde82018-07-03 14:46:07 -0700908 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800909
Justin Klaassen911e8892016-06-21 18:24:24 -0700910 // Force an update to initialize state.
911 updateActivated();
912 }
913
914 @Override
915 public void onStop() {
916 getContext().unregisterReceiver(mTimeChangedReceiver);
917
918 mAlarmManager.cancel(this);
919 mLastActivatedTime = null;
920 }
921
922 @Override
923 public void onActivated(boolean activated) {
Christine Franks57fdde82018-07-03 14:46:07 -0700924 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Christine Franks03213462017-08-25 13:57:26 -0700925 updateNextAlarm(activated, LocalDateTime.now());
Justin Klaassen911e8892016-06-21 18:24:24 -0700926 }
927
928 @Override
Christine Franks03213462017-08-25 13:57:26 -0700929 public void onCustomStartTimeChanged(LocalTime startTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700930 mStartTime = startTime;
931 mLastActivatedTime = null;
932 updateActivated();
933 }
934
935 @Override
Christine Franks03213462017-08-25 13:57:26 -0700936 public void onCustomEndTimeChanged(LocalTime endTime) {
Justin Klaassen911e8892016-06-21 18:24:24 -0700937 mEndTime = endTime;
938 mLastActivatedTime = null;
939 updateActivated();
940 }
941
942 @Override
943 public void onAlarm() {
Justin Klaassenec8837a2016-08-23 12:04:42 -0700944 Slog.d(TAG, "onAlarm");
Justin Klaassen911e8892016-06-21 18:24:24 -0700945 updateActivated();
946 }
947 }
948
Christine Franks57fdde82018-07-03 14:46:07 -0700949 private final class TwilightNightDisplayAutoMode extends NightDisplayAutoMode implements
950 TwilightListener {
Justin Klaassen911e8892016-06-21 18:24:24 -0700951
952 private final TwilightManager mTwilightManager;
Christine Franks57fdde82018-07-03 14:46:07 -0700953 private LocalDateTime mLastActivatedTime;
Justin Klaassen911e8892016-06-21 18:24:24 -0700954
Christine Franks57fdde82018-07-03 14:46:07 -0700955 TwilightNightDisplayAutoMode() {
Justin Klaassen911e8892016-06-21 18:24:24 -0700956 mTwilightManager = getLocalService(TwilightManager.class);
Justin Klaassen911e8892016-06-21 18:24:24 -0700957 }
958
Justin Klaassen908b86c2016-08-08 09:18:42 -0700959 private void updateActivated(TwilightState state) {
Justin Klaassen3da4c442017-05-05 15:19:33 -0700960 if (state == null) {
961 // If there isn't a valid TwilightState then just keep the current activated
962 // state.
963 return;
964 }
965
966 boolean activate = state.isNight();
Christine Franks57fdde82018-07-03 14:46:07 -0700967 if (mLastActivatedTime != null) {
Christine Franks03213462017-08-25 13:57:26 -0700968 final LocalDateTime now = LocalDateTime.now();
969 final LocalDateTime sunrise = state.sunrise();
970 final LocalDateTime sunset = state.sunset();
Christine Frankse5bb03e2017-02-10 17:36:10 -0800971 // Maintain the existing activated state if within the current period.
Christine Franks57fdde82018-07-03 14:46:07 -0700972 if (mLastActivatedTime.isBefore(now) && (mLastActivatedTime.isBefore(sunrise)
973 ^ mLastActivatedTime.isBefore(sunset))) {
974 activate = mNightDisplayController.isActivated();
Justin Klaassen911e8892016-06-21 18:24:24 -0700975 }
976 }
Justin Klaassen908b86c2016-08-08 09:18:42 -0700977
Christine Franks245ffd42018-11-16 13:45:14 -0800978 if (mNightDisplayTintController.isActivatedStateNotSet() || (
979 mNightDisplayTintController.isActivated() != activate)) {
Christine Franks57fdde82018-07-03 14:46:07 -0700980 mNightDisplayController.setActivated(activate);
Justin Klaassen908b86c2016-08-08 09:18:42 -0700981 }
Justin Klaassen911e8892016-06-21 18:24:24 -0700982 }
983
984 @Override
Christine Franks57fdde82018-07-03 14:46:07 -0700985 public void onActivated(boolean activated) {
986 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
987 }
988
989 @Override
Justin Klaassen911e8892016-06-21 18:24:24 -0700990 public void onStart() {
991 mTwilightManager.registerListener(this, mHandler);
Christine Franks57fdde82018-07-03 14:46:07 -0700992 mLastActivatedTime = getNightDisplayLastActivatedTimeSetting();
Justin Klaassen911e8892016-06-21 18:24:24 -0700993
994 // Force an update to initialize state.
Justin Klaassen908b86c2016-08-08 09:18:42 -0700995 updateActivated(mTwilightManager.getLastTwilightState());
Justin Klaassen911e8892016-06-21 18:24:24 -0700996 }
997
998 @Override
999 public void onStop() {
1000 mTwilightManager.unregisterListener(this);
Christine Franks57fdde82018-07-03 14:46:07 -07001001 mLastActivatedTime = null;
Justin Klaassen908b86c2016-08-08 09:18:42 -07001002 }
1003
1004 @Override
1005 public void onTwilightStateChanged(@Nullable TwilightState state) {
Justin Klaassenec8837a2016-08-23 12:04:42 -07001006 Slog.d(TAG, "onTwilightStateChanged: isNight="
1007 + (state == null ? null : state.isNight()));
Justin Klaassen908b86c2016-08-08 09:18:42 -07001008 updateActivated(state);
Justin Klaassen911e8892016-06-21 18:24:24 -07001009 }
1010 }
Justin Klaassen639214e2016-07-14 21:00:06 -07001011
1012 /**
1013 * Interpolates between two 4x4 color transform matrices (in column-major order).
1014 */
1015 private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> {
1016
1017 /**
1018 * Result matrix returned by {@link #evaluate(float, float[], float[])}.
1019 */
1020 private final float[] mResultMatrix = new float[16];
1021
1022 @Override
1023 public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
1024 for (int i = 0; i < mResultMatrix.length; i++) {
1025 mResultMatrix[i] = MathUtils.lerp(startValue[i], endValue[i], fraction);
1026 }
1027 return mResultMatrix;
1028 }
1029 }
Christine Franks39b03112018-07-03 14:46:07 -07001030
Christine Franks245ffd42018-11-16 13:45:14 -08001031 private abstract static class TintController {
1032
1033 private ValueAnimator mAnimator;
1034 private Boolean mIsActivated;
1035
1036 public ValueAnimator getAnimator() {
1037 return mAnimator;
1038 }
1039
1040 public void setAnimator(ValueAnimator animator) {
1041 mAnimator = animator;
1042 }
1043
1044 /**
1045 * Cancel the animator if it's still running.
1046 */
1047 public void cancelAnimator() {
1048 if (mAnimator != null) {
1049 mAnimator.cancel();
1050 }
1051 }
1052
1053 /**
1054 * End the animator if it's still running, jumping to the end state.
1055 */
1056 public void endAnimator() {
1057 if (mAnimator != null) {
1058 mAnimator.end();
1059 mAnimator = null;
1060 }
1061 }
1062
1063 public void setActivated(Boolean isActivated) {
1064 mIsActivated = isActivated;
1065 }
1066
1067 public boolean isActivated() {
1068 return mIsActivated != null && mIsActivated;
1069 }
1070
1071 public boolean isActivatedStateNotSet() {
1072 return mIsActivated == null;
1073 }
1074
1075 /**
Daniel Solomon8b72c5b2018-11-25 11:07:13 -08001076 * Dump debug information.
1077 */
1078 public void dump(PrintWriter pw) {
1079 }
1080
1081 /**
Christine Franks245ffd42018-11-16 13:45:14 -08001082 * Set up any constants needed for computing the matrix.
1083 */
1084 public abstract void setUp(Context context, boolean needsLinear);
1085
1086 /**
1087 * Sets the 4x4 matrix to apply.
1088 */
1089 public abstract void setMatrix(int value);
1090
1091 /**
1092 * Get the 4x4 matrix to apply.
1093 */
1094 public abstract float[] getMatrix();
1095
1096 /**
1097 * Get the color transform level to apply the matrix.
1098 */
1099 public abstract int getLevel();
1100 }
1101
1102 /**
1103 * Local service that allows color transforms to be enabled from other system services.
1104 */
1105 public final class ColorDisplayServiceInternal {
1106
1107 /**
1108 * Set the current CCT value for the display white balance transform, and if the transform
1109 * is enabled, apply it.
1110 *
1111 * @param cct the color temperature in Kelvin.
1112 */
1113 public boolean setDisplayWhiteBalanceColorTemperature(int cct) {
1114 // Update the transform matrix even if it can't be applied.
Christine Franks245ffd42018-11-16 13:45:14 -08001115 mDisplayWhiteBalanceTintController.setMatrix(cct);
1116
1117 if (mDisplayWhiteBalanceTintController.isActivated()) {
Daniel Solomon8b72c5b2018-11-25 11:07:13 -08001118 applyTint(mDisplayWhiteBalanceTintController, false);
Christine Franks245ffd42018-11-16 13:45:14 -08001119 return true;
1120 }
1121 return false;
1122 }
1123
1124 /**
1125 * Sets the listener and returns whether display white balance is currently enabled.
1126 */
1127 public boolean setDisplayWhiteBalanceListener(DisplayWhiteBalanceListener listener) {
1128 mDisplayWhiteBalanceListener = listener;
1129 return mDisplayWhiteBalanceTintController.isActivated();
1130 }
Daniel Solomon8b72c5b2018-11-25 11:07:13 -08001131
1132 public void dump(PrintWriter pw) {
1133 mDisplayWhiteBalanceTintController.dump(pw);
1134 }
Christine Franks245ffd42018-11-16 13:45:14 -08001135 }
1136
1137 /**
1138 * Listener for changes in display white balance status.
1139 */
1140 public interface DisplayWhiteBalanceListener {
1141
1142 /**
1143 * Notify that the display white balance status has changed, either due to preemption by
1144 * another transform or the feature being turned off.
1145 */
1146 void onDisplayWhiteBalanceStatusChanged(boolean enabled);
1147 }
1148
Christine Franks09c229e2018-12-14 10:37:40 -08001149 private final class TintHandler extends Handler {
1150
1151 TintHandler(Looper looper) {
1152 super(looper, null, true /* async */);
1153 }
1154
1155 @Override
1156 public void handleMessage(Message msg) {
1157 switch (msg.what) {
1158 case MSG_APPLY_GLOBAL_SATURATION:
1159 mGlobalSaturationTintController.setMatrix(msg.arg1);
1160 applyTint(mGlobalSaturationTintController, false);
1161 break;
1162 }
1163 }
1164 }
1165
Christine Franks39b03112018-07-03 14:46:07 -07001166 private final class BinderService extends IColorDisplayManager.Stub {
Christine Franks57fdde82018-07-03 14:46:07 -07001167
Christine Franks39b03112018-07-03 14:46:07 -07001168 @Override
1169 public boolean isDeviceColorManaged() {
1170 final long token = Binder.clearCallingIdentity();
1171 try {
1172 return isDeviceColorManagedInternal();
1173 } finally {
1174 Binder.restoreCallingIdentity(token);
1175 }
1176 }
Christine Franks09c229e2018-12-14 10:37:40 -08001177
1178 @Override
1179 public boolean setSaturationLevel(int level) {
1180 final boolean hasTransformsPermission = getContext()
1181 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
1182 == PackageManager.PERMISSION_GRANTED;
1183 final boolean hasLegacyPermission = getContext()
1184 .checkCallingPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
1185 == PackageManager.PERMISSION_GRANTED;
1186 if (!hasTransformsPermission && !hasLegacyPermission) {
1187 throw new SecurityException("Permission required to set display saturation level");
1188 }
1189 final long token = Binder.clearCallingIdentity();
1190 try {
1191 final Message message = mHandler.obtainMessage(MSG_APPLY_GLOBAL_SATURATION);
1192 message.arg1 = level;
1193 mHandler.sendMessage(message);
1194 } finally {
1195 Binder.restoreCallingIdentity(token);
1196 }
1197 return true;
1198 }
Christine Franks39b03112018-07-03 14:46:07 -07001199 }
Justin Klaassen911e8892016-06-21 18:24:24 -07001200}